Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 701e2cf55d | |||
| 68178cd60f | |||
| 23054ce75b | |||
| d36f08b019 | |||
| 9d07a53785 |
@@ -71,6 +71,11 @@ func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec) (m
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert panels: %w", err)
|
||||
}
|
||||
// Count total panels including those in collapsed rows
|
||||
totalPanelsConverted := countTotalPanels(panels)
|
||||
if totalPanelsConverted < len(in.Elements) {
|
||||
return nil, fmt.Errorf("some panels were not converted from v2alpha1 to v1beta1")
|
||||
}
|
||||
|
||||
if len(panels) > 0 {
|
||||
dashboard["panels"] = panels
|
||||
@@ -193,6 +198,29 @@ func convertLinksToV1(links []dashv2alpha1.DashboardDashboardLink) []map[string]
|
||||
return result
|
||||
}
|
||||
|
||||
// countTotalPanels counts all panels including those nested in collapsed row panels.
|
||||
func countTotalPanels(panels []interface{}) int {
|
||||
count := 0
|
||||
for _, p := range panels {
|
||||
panel, ok := p.(map[string]interface{})
|
||||
if !ok {
|
||||
count++
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if this is a row panel with nested panels
|
||||
if panelType, ok := panel["type"].(string); ok && panelType == "row" {
|
||||
if nestedPanels, ok := panel["panels"].([]interface{}); ok {
|
||||
count += len(nestedPanels)
|
||||
}
|
||||
// Don't count the row itself as a panel element
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// convertPanelsFromElementsAndLayout converts V2 layout structures to V1 panel arrays.
|
||||
// V1 only supports a flat array of panels with row panels for grouping.
|
||||
// This function dispatches to the appropriate converter based on layout type:
|
||||
|
||||
+22
-4
@@ -290,7 +290,7 @@
|
||||
],
|
||||
"legend": {
|
||||
"displayMode": "table",
|
||||
"placement": "bottom",
|
||||
"placement": "right",
|
||||
"showLegend": true,
|
||||
"values": [
|
||||
"percent"
|
||||
@@ -304,7 +304,7 @@
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showLegend": false,
|
||||
"showLegend": true,
|
||||
"strokeWidth": 1,
|
||||
"text": {}
|
||||
},
|
||||
@@ -323,6 +323,15 @@
|
||||
}
|
||||
],
|
||||
"title": "Percent",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "renameByRegex",
|
||||
"options": {
|
||||
"regex": "^Backend-(.*)$",
|
||||
"renamePattern": "b-$1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "piechart"
|
||||
},
|
||||
{
|
||||
@@ -366,7 +375,7 @@
|
||||
],
|
||||
"legend": {
|
||||
"displayMode": "table",
|
||||
"placement": "bottom",
|
||||
"placement": "right",
|
||||
"showLegend": true,
|
||||
"values": [
|
||||
"value"
|
||||
@@ -380,7 +389,7 @@
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showLegend": false,
|
||||
"showLegend": true,
|
||||
"strokeWidth": 1,
|
||||
"text": {}
|
||||
},
|
||||
@@ -399,6 +408,15 @@
|
||||
}
|
||||
],
|
||||
"title": "Value",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "renameByRegex",
|
||||
"options": {
|
||||
"regex": "(.*)",
|
||||
"renamePattern": "$1-how-much-wood-could-a-woodchuck-chuck-if-a-woodchuck-could-chuck-wood"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "piechart"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -248,7 +248,7 @@
|
||||
"legend": {
|
||||
"values": ["percent"],
|
||||
"displayMode": "table",
|
||||
"placement": "bottom"
|
||||
"placement": "right"
|
||||
},
|
||||
"pieType": "pie",
|
||||
"reduceOptions": {
|
||||
@@ -256,7 +256,7 @@
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showLegend": false,
|
||||
"showLegend": true,
|
||||
"strokeWidth": 1,
|
||||
"text": {}
|
||||
},
|
||||
@@ -272,6 +272,15 @@
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Percent",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "renameByRegex",
|
||||
"options": {
|
||||
"regex": "^Backend-(.*)$",
|
||||
"renamePattern": "b-$1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "piechart"
|
||||
},
|
||||
{
|
||||
@@ -311,7 +320,7 @@
|
||||
"legend": {
|
||||
"values": ["value"],
|
||||
"displayMode": "table",
|
||||
"placement": "bottom"
|
||||
"placement": "right"
|
||||
},
|
||||
"pieType": "pie",
|
||||
"reduceOptions": {
|
||||
@@ -319,7 +328,7 @@
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showLegend": false,
|
||||
"showLegend": true,
|
||||
"strokeWidth": 1,
|
||||
"text": {}
|
||||
},
|
||||
@@ -335,6 +344,15 @@
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Value",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "renameByRegex",
|
||||
"options": {
|
||||
"regex": "(.*)",
|
||||
"renamePattern": "$1-how-much-wood-could-a-woodchuck-chuck-if-a-woodchuck-could-chuck-wood"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "piechart"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,574 @@
|
||||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import V2DashWithRowRepeats from '../dashboards/V2DashWithRowRepeats.json';
|
||||
|
||||
import {
|
||||
verifyChanges,
|
||||
saveDashboard,
|
||||
importTestDashboard,
|
||||
goToEmbeddedPanel,
|
||||
groupIntoRow,
|
||||
checkRepeatedRowTitles,
|
||||
moveRow,
|
||||
getRowPosition,
|
||||
} from './utils';
|
||||
|
||||
const repeatTitleBase = 'Row - ';
|
||||
const newTitleBase = 'edited row rep - ';
|
||||
const repeatOptions = [1, 2, 3, 4];
|
||||
const getRepeatedPanelTitle = (row: number, panel: number) => `repeated-row-${row}-repeated-panel-${panel}`;
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
test.use({
|
||||
viewport: { width: 1920, height: 1080 },
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Repeats - Dashboard rows layout',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can enable row repeats', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Row layout repeats - add repeats');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.RowsLayout.titleInput)
|
||||
.fill(`${repeatTitleBase}$c1`);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.join(' + ')}`)
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
const repeatOptionsGroup = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.OptionsGroup.group('dash-row-repeat')
|
||||
);
|
||||
|
||||
// expand repeat options dropdown
|
||||
await repeatOptionsGroup.click();
|
||||
// find repeat variable dropdown
|
||||
await repeatOptionsGroup.getByRole('combobox').click();
|
||||
await page.getByRole('option', { name: 'c1' }).click();
|
||||
|
||||
await checkRepeatedRowTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions);
|
||||
|
||||
await saveDashboard(dashboardPage, page, selectors);
|
||||
await page.reload();
|
||||
|
||||
await checkRepeatedRowTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions);
|
||||
});
|
||||
|
||||
test('can update tab repeats with variable change', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Rows layout repeats - update on variable change',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
|
||||
const c4Var = dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('c4'));
|
||||
await c4Var
|
||||
.locator('..')
|
||||
.getByTestId(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(repeatOptions.join(',')))
|
||||
.click();
|
||||
// deselect last variable option
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts(`${repeatOptions.at(0)}`)
|
||||
)
|
||||
.click();
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } }); // blur select
|
||||
|
||||
// verify that repeats are present for last 3 values
|
||||
await checkRepeatedRowTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions.slice(1, -1));
|
||||
// verify there is no repeat with first value
|
||||
expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(0)}`)
|
||||
)
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
test('can update title for repeat rows in edit pane', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Rows layout repeats - update through edit pane',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
// select first/original repeat row to activate edit pane
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(0)}`))
|
||||
.click();
|
||||
|
||||
const titleInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.ElementEditPane.RowsLayout.titleInput
|
||||
);
|
||||
await titleInput.fill(`${newTitleBase}$c4`);
|
||||
await titleInput.blur();
|
||||
|
||||
await checkRepeatedRowTitles(dashboardPage, selectors, newTitleBase, repeatOptions);
|
||||
|
||||
await saveDashboard(dashboardPage, page, selectors);
|
||||
await page.reload();
|
||||
|
||||
await checkRepeatedRowTitles(dashboardPage, selectors, newTitleBase, repeatOptions);
|
||||
});
|
||||
|
||||
test('can update repeats after panel change', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Row layout repeats - update repeats after panel change',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('single panel row 1')).click();
|
||||
|
||||
const panelTitleInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldInput('Title')
|
||||
);
|
||||
await panelTitleInput.fill('single panel row $c4 edited');
|
||||
await panelTitleInput.blur();
|
||||
|
||||
// close first row to load the second row
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}1`)).click();
|
||||
|
||||
// verify edited panel title updated in repeated row
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('single panel row 2 edited'))
|
||||
).toBeVisible();
|
||||
|
||||
// reopen first row so collapse is not saved
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}1`)).click();
|
||||
|
||||
await saveDashboard(dashboardPage, page, selectors);
|
||||
await page.reload();
|
||||
|
||||
// close first row to load the second row
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}1`)).click();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('single panel row 2 edited'))
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('can update repeats after panel change in editor', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Row layout repeats - update repeats after panel change in editor',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
|
||||
const editedSinglePanelName = (rowNumber: string) => `single panel row ${rowNumber} edited`;
|
||||
const panel = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.title('single panel row 1'))
|
||||
.first();
|
||||
await panel.hover();
|
||||
await page.keyboard.press('e');
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardEditPaneSplitter.primaryBody)
|
||||
).toBeHidden(); // verifying that panel editor loaded
|
||||
|
||||
const panelTitleInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldInput('Title')
|
||||
);
|
||||
await panelTitleInput.fill(editedSinglePanelName('$c4'));
|
||||
await panelTitleInput.blur();
|
||||
|
||||
// playwright too fast, verifying JSON diff that changes landed
|
||||
await verifyChanges(dashboardPage, page, selectors, editedSinglePanelName('$c4'));
|
||||
// verify panel title change in panel editor UI
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(editedSinglePanelName('1')))
|
||||
).toBeVisible();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardEditPaneSplitter.primaryBody)
|
||||
).toBeVisible(); // verifying that dashboard loaded
|
||||
|
||||
// close first row to make sure we are viewing second row
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}1`)).click();
|
||||
|
||||
// verify edited panel title updated in repeated row
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(editedSinglePanelName('2')))
|
||||
).toBeVisible();
|
||||
// open first row again so collapse is not saved
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}1`)).click();
|
||||
|
||||
await saveDashboard(dashboardPage, page, selectors);
|
||||
await page.reload();
|
||||
|
||||
// collapse row again so lazy loading loads 2nd row
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}1`)).click();
|
||||
|
||||
// verify edited panel title updated in repeated tab
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(editedSinglePanelName('2')))
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('can hide add panel action in repeats', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Row layout repeats - hide canvas add action in repeats',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addPanel)
|
||||
).toBeDefined();
|
||||
|
||||
// close first row to make sure second row is in viewport
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}1`)).click();
|
||||
|
||||
const secondRow = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.wrapper(`${repeatTitleBase}2`)
|
||||
);
|
||||
await expect(secondRow.getByTestId(selectors.components.CanvasGridAddActions.addPanel)).toBeHidden();
|
||||
});
|
||||
|
||||
test('can move repeated rows', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Row layout repeats - move repeated rows',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
const singleRowTitle = 'single row';
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// collapse rows and save
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}1`)).click();
|
||||
await saveDashboard(dashboardPage, page, selectors);
|
||||
await page.reload();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
await moveRow(dashboardPage, page, selectors, `${repeatTitleBase}1`, singleRowTitle);
|
||||
|
||||
let singleRow = await getRowPosition(dashboardPage, selectors, singleRowTitle);
|
||||
|
||||
const repeatedRow = await getRowPosition(dashboardPage, selectors, `${repeatTitleBase}1`);
|
||||
expect(singleRow?.y).toBeLessThan(repeatedRow?.y || 0);
|
||||
|
||||
setTimeout(async () => {
|
||||
singleRow = await getRowPosition(dashboardPage, selectors, singleRowTitle);
|
||||
|
||||
await saveDashboard(dashboardPage, page, selectors);
|
||||
await page.reload();
|
||||
for (let i = 1; i <= repeatOptions.length; i++) {
|
||||
// verify move by row position
|
||||
const repeatedRow = await getRowPosition(dashboardPage, selectors, `${repeatTitleBase}${i}`);
|
||||
expect(singleRow?.y).toBeLessThan(repeatedRow?.y || 0);
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
|
||||
test('can view panels in repeated row', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Row layout repeats - view panels in repeated rows',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
|
||||
// non repeated panel in repeated row
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.title('single panel row 1'))
|
||||
.first()
|
||||
.hover();
|
||||
await page.keyboard.press('v');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(1, 1)))
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('single panel row 1'))
|
||||
).toBeVisible();
|
||||
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('single panel row 1'))
|
||||
).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
// repeated panel in original row repeat
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(1, 2)))
|
||||
.hover();
|
||||
await page.keyboard.press('v');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(1, 1)))
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(1, 2)))
|
||||
).toBeVisible();
|
||||
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(1, 2)))
|
||||
).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
// repeated panel in repeated row
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(2, 2)))
|
||||
.hover();
|
||||
await page.keyboard.press('v');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(1, 2)))
|
||||
).toBeHidden();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(2, 2)))
|
||||
).toBeVisible();
|
||||
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(2, 2)))
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('can view embedded panels in repeated tab', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Row layout repeats - view embedded panels in repeated rows',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
|
||||
const dashUrl = page.url();
|
||||
|
||||
// non repeated panel in repeated row
|
||||
// collapse row to make sure row 2 is in viewport
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}1`)).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.title('single panel row 2'))
|
||||
.first()
|
||||
.hover();
|
||||
await page.keyboard.press('p+e');
|
||||
await goToEmbeddedPanel(page);
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('single panel row 2'))
|
||||
).toBeVisible();
|
||||
await page.goto(dashUrl);
|
||||
|
||||
// repeated panel in original row
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(1, 2)))
|
||||
.hover();
|
||||
await page.keyboard.press('p+e');
|
||||
await goToEmbeddedPanel(page);
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(1, 2)))
|
||||
).toBeVisible();
|
||||
await page.goto(dashUrl);
|
||||
|
||||
// repeated panel in repeated row
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(2, 2)))
|
||||
.hover();
|
||||
await page.keyboard.press('p+e');
|
||||
await goToEmbeddedPanel(page);
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(getRepeatedPanelTitle(2, 2)))
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('can remove repeats', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Row layout repeats - remove row repeats',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
|
||||
// verify both repeated and single rows are present
|
||||
await checkRepeatedRowTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions);
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('single row'))
|
||||
).toBeVisible();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(0)}`))
|
||||
.click();
|
||||
|
||||
const repeatOptionsGroup = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.OptionsGroup.group('dash-row-repeat')
|
||||
);
|
||||
// expand repeat options dropdown
|
||||
await repeatOptionsGroup.click();
|
||||
// find repeat variable dropdown
|
||||
await repeatOptionsGroup.getByRole('combobox').click();
|
||||
await page.getByRole('option', { name: 'Disable repeating' }).click();
|
||||
|
||||
const nonRepeatedTitle = `${repeatTitleBase}${repeatOptions.join(' + ')}`;
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(nonRepeatedTitle))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Panels.Panel.title(`single panel row ${repeatOptions.join(' + ')}`)
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(1)}`)
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(2)}`)
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(3)}`)
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(4)}`)
|
||||
)
|
||||
).toBeHidden();
|
||||
await saveDashboard(dashboardPage, page, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(nonRepeatedTitle))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Panels.Panel.title(`single panel row ${repeatOptions.join(' + ')}`)
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(1)}`)
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(2)}`)
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(3)}`)
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.DashboardRow.title(`${repeatTitleBase}${repeatOptions.at(4)}`)
|
||||
)
|
||||
).toBeHidden();
|
||||
});
|
||||
test('can add tabs in repeated rows', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Row layout repeats - remove row repeats',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// add a tab in first row
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.groupPanels).first().click();
|
||||
await page.getByText('Group into tab').click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput)
|
||||
.fill(`tab-row-$c4`);
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`tab-row-1`))).toBeVisible();
|
||||
|
||||
await saveDashboard(dashboardPage, page, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`tab-row-1`))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`tab-row-2`))).toBeVisible();
|
||||
});
|
||||
test('can add repeat tabs in repeated rows', async ({ dashboardPage, selectors, page }) => {
|
||||
const tabRepeatTitle = (tabNo: number, rowNo: number) => `tab-${tabNo}-row-${rowNo}`;
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Row layout repeats - remove row repeats',
|
||||
JSON.stringify(V2DashWithRowRepeats)
|
||||
);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// add a tab in first row
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.groupPanels).first().click();
|
||||
await page.getByText('Group into tab').click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput)
|
||||
.fill(`tab-$c1-row-$c4`);
|
||||
|
||||
const repeatOptionsGroup = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.OptionsGroup.group('repeat-options')
|
||||
);
|
||||
// expand repeat options dropdown
|
||||
await repeatOptionsGroup.getByRole('button').first().click();
|
||||
// find repeat variable dropdown
|
||||
await repeatOptionsGroup.getByRole('combobox').click();
|
||||
await page.getByRole('option', { name: 'c1' }).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(tabRepeatTitle(1, 1)))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(tabRepeatTitle(2, 1)))
|
||||
).toBeVisible();
|
||||
await saveDashboard(dashboardPage, page, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(tabRepeatTitle(1, 1)))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(tabRepeatTitle(2, 1)))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(tabRepeatTitle(1, 2)))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(tabRepeatTitle(2, 2)))
|
||||
).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -314,7 +314,7 @@ test.describe(
|
||||
await importTestDashboard(
|
||||
page,
|
||||
selectors,
|
||||
'Auto-grid repeats - move repeated panels 2',
|
||||
'Auto-grid repeats - view repeated panels 2',
|
||||
JSON.stringify(testV2DashWithRepeats)
|
||||
);
|
||||
|
||||
|
||||
@@ -250,11 +250,41 @@ export async function moveTab(
|
||||
await page.mouse.up();
|
||||
}
|
||||
|
||||
export async function moveRow(
|
||||
dashboardPage: DashboardPage,
|
||||
page: Page,
|
||||
selectors: E2ESelectorGroups,
|
||||
sourceRow: string,
|
||||
targetRow: string
|
||||
) {
|
||||
const targetRowElement = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.wrapper(targetRow))
|
||||
.first();
|
||||
|
||||
const sourceRowElement = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.title(sourceRow))
|
||||
.first();
|
||||
|
||||
const targetBox = await targetRowElement.boundingBox();
|
||||
|
||||
// Perform drag and drop (dragTo() did not work in this case)
|
||||
await sourceRowElement.hover();
|
||||
await page.mouse.down();
|
||||
// move to adjusted target position (relative to top left)
|
||||
await page.mouse.move(targetBox?.x || 0, (targetBox?.y || 0) + (targetBox?.height || 0), { steps: 5 });
|
||||
await page.mouse.up();
|
||||
}
|
||||
|
||||
export async function groupIntoTab(page: Page, dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.groupPanels).click();
|
||||
await page.getByText('Group into tab').click();
|
||||
}
|
||||
|
||||
export async function groupIntoRow(page: Page, dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.groupPanels).click();
|
||||
await page.getByText('Group into row').click();
|
||||
}
|
||||
|
||||
export async function checkRepeatedTabTitles(
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
@@ -272,6 +302,26 @@ export async function getTabPosition(dashboardPage: DashboardPage, selectors: E2
|
||||
return boundingBox;
|
||||
}
|
||||
|
||||
export async function getRowPosition(dashboardPage: DashboardPage, selectors: E2ESelectorGroups, rowTitle: string) {
|
||||
const row = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(rowTitle)).first();
|
||||
const boundingBox = await row.boundingBox();
|
||||
return boundingBox;
|
||||
}
|
||||
|
||||
export async function checkRepeatedRowTitles(
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
title: string,
|
||||
options: Array<string | number>
|
||||
) {
|
||||
for (const option of options) {
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${title}${option}`))
|
||||
).toBeVisible();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(`${title}${option}`)).click();
|
||||
}
|
||||
}
|
||||
|
||||
export async function switchToAutoGrid(page: Page, dashboardPage: DashboardPage) {
|
||||
await page.getByLabel('layout-selection-option-Auto grid').click();
|
||||
// confirm layout change if applicable
|
||||
|
||||
@@ -307,7 +307,23 @@
|
||||
"mode": "variable",
|
||||
"value": "c4"
|
||||
},
|
||||
"title": "Repeated row $c4"
|
||||
"title": "Row - $c4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "RowsLayoutRow",
|
||||
"spec": {
|
||||
"collapse": false,
|
||||
"layout": {
|
||||
"kind": "AutoGridLayout",
|
||||
"spec": {
|
||||
"columnWidthMode": "standard",
|
||||
"items": [],
|
||||
"maxColumnCount": 3,
|
||||
"rowHeightMode": "standard"
|
||||
}
|
||||
},
|
||||
"title": "single row"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { VizLegendTable } from './VizLegendTable';
|
||||
import { VizLegendItem } from './types';
|
||||
|
||||
describe('VizLegendTable', () => {
|
||||
const mockItems: VizLegendItem[] = [
|
||||
{ label: 'Series 1', color: 'red', yAxis: 1 },
|
||||
{ label: 'Series 2', color: 'blue', yAxis: 1 },
|
||||
{ label: 'Series 3', color: 'green', yAxis: 1 },
|
||||
];
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const { container } = render(<VizLegendTable items={mockItems} placement="bottom" />);
|
||||
expect(container.querySelector('table')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders all items', () => {
|
||||
render(<VizLegendTable items={mockItems} placement="bottom" />);
|
||||
expect(screen.getByText('Series 1')).toBeInTheDocument();
|
||||
expect(screen.getByText('Series 2')).toBeInTheDocument();
|
||||
expect(screen.getByText('Series 3')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders table headers when items have display values', () => {
|
||||
const itemsWithStats: VizLegendItem[] = [
|
||||
{
|
||||
label: 'Series 1',
|
||||
color: 'red',
|
||||
yAxis: 1,
|
||||
getDisplayValues: () => [
|
||||
{ numeric: 100, text: '100', title: 'Max' },
|
||||
{ numeric: 50, text: '50', title: 'Min' },
|
||||
],
|
||||
},
|
||||
];
|
||||
render(<VizLegendTable items={itemsWithStats} placement="bottom" />);
|
||||
expect(screen.getByText('Max')).toBeInTheDocument();
|
||||
expect(screen.getByText('Min')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders sort icon when sorted', () => {
|
||||
const { container } = render(
|
||||
<VizLegendTable items={mockItems} placement="bottom" sortBy="Name" sortDesc={false} />
|
||||
);
|
||||
expect(container.querySelector('svg')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onToggleSort when header is clicked', () => {
|
||||
const onToggleSort = jest.fn();
|
||||
render(<VizLegendTable items={mockItems} placement="bottom" onToggleSort={onToggleSort} isSortable={true} />);
|
||||
const header = screen.getByText('Name');
|
||||
header.click();
|
||||
expect(onToggleSort).toHaveBeenCalledWith('Name');
|
||||
});
|
||||
|
||||
it('does not call onToggleSort when not sortable', () => {
|
||||
const onToggleSort = jest.fn();
|
||||
render(<VizLegendTable items={mockItems} placement="bottom" onToggleSort={onToggleSort} isSortable={false} />);
|
||||
const header = screen.getByText('Name');
|
||||
header.click();
|
||||
expect(onToggleSort).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders with long labels', () => {
|
||||
const itemsWithLongLabels: VizLegendItem[] = [
|
||||
{
|
||||
label: 'This is a very long series name that should be scrollable within its table cell',
|
||||
color: 'red',
|
||||
yAxis: 1,
|
||||
},
|
||||
];
|
||||
render(<VizLegendTable items={itemsWithLongLabels} placement="bottom" />);
|
||||
expect(
|
||||
screen.getByText('This is a very long series name that should be scrollable within its table cell')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,112 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { LegendTableItem } from './VizLegendTableItem';
|
||||
import { VizLegendItem } from './types';
|
||||
|
||||
describe('LegendTableItem', () => {
|
||||
const mockItem: VizLegendItem = {
|
||||
label: 'Series 1',
|
||||
color: 'red',
|
||||
yAxis: 1,
|
||||
};
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const { container } = render(
|
||||
<table>
|
||||
<tbody>
|
||||
<LegendTableItem item={mockItem} />
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(container.querySelector('tr')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders label text', () => {
|
||||
render(
|
||||
<table>
|
||||
<tbody>
|
||||
<LegendTableItem item={mockItem} />
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(screen.getByText('Series 1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with long label text', () => {
|
||||
const longLabelItem: VizLegendItem = {
|
||||
...mockItem,
|
||||
label: 'This is a very long series name that should be scrollable in the table cell',
|
||||
};
|
||||
render(
|
||||
<table>
|
||||
<tbody>
|
||||
<LegendTableItem item={longLabelItem} />
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(
|
||||
screen.getByText('This is a very long series name that should be scrollable in the table cell')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders stat values when provided', () => {
|
||||
const itemWithStats: VizLegendItem = {
|
||||
...mockItem,
|
||||
getDisplayValues: () => [
|
||||
{ numeric: 100, text: '100', title: 'Max' },
|
||||
{ numeric: 50, text: '50', title: 'Min' },
|
||||
],
|
||||
};
|
||||
render(
|
||||
<table>
|
||||
<tbody>
|
||||
<LegendTableItem item={itemWithStats} />
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(screen.getByText('100')).toBeInTheDocument();
|
||||
expect(screen.getByText('50')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders right y-axis indicator when yAxis is 2', () => {
|
||||
const rightAxisItem: VizLegendItem = {
|
||||
...mockItem,
|
||||
yAxis: 2,
|
||||
};
|
||||
render(
|
||||
<table>
|
||||
<tbody>
|
||||
<LegendTableItem item={rightAxisItem} />
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(screen.getByText('(right y-axis)')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onLabelClick when label is clicked', () => {
|
||||
const onLabelClick = jest.fn();
|
||||
render(
|
||||
<table>
|
||||
<tbody>
|
||||
<LegendTableItem item={mockItem} onLabelClick={onLabelClick} />
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
const button = screen.getByRole('button');
|
||||
button.click();
|
||||
expect(onLabelClick).toHaveBeenCalledWith(mockItem, expect.any(Object));
|
||||
});
|
||||
|
||||
it('does not call onClick when readonly', () => {
|
||||
const onLabelClick = jest.fn();
|
||||
render(
|
||||
<table>
|
||||
<tbody>
|
||||
<LegendTableItem item={mockItem} onLabelClick={onLabelClick} readonly={true} />
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
const button = screen.getByRole('button');
|
||||
expect(button).toBeDisabled();
|
||||
});
|
||||
});
|
||||
@@ -69,7 +69,7 @@ export const LegendTableItem = ({
|
||||
|
||||
return (
|
||||
<tr className={cx(styles.row, className)}>
|
||||
<td>
|
||||
<td className={styles.labelCell}>
|
||||
<span className={styles.itemWrapper}>
|
||||
<VizLegendSeriesIcon
|
||||
color={item.color}
|
||||
@@ -77,24 +77,26 @@ export const LegendTableItem = ({
|
||||
readonly={readonly}
|
||||
lineStyle={item.lineStyle}
|
||||
/>
|
||||
<button
|
||||
disabled={readonly}
|
||||
type="button"
|
||||
title={item.label}
|
||||
onBlur={onMouseOut}
|
||||
onFocus={onMouseOver}
|
||||
onMouseOver={onMouseOver}
|
||||
onMouseOut={onMouseOut}
|
||||
onClick={!readonly ? onClick : undefined}
|
||||
className={cx(styles.label, item.disabled && styles.labelDisabled)}
|
||||
>
|
||||
{item.label}{' '}
|
||||
{item.yAxis === 2 && (
|
||||
<span className={styles.yAxisLabel}>
|
||||
<Trans i18nKey="grafana-ui.viz-legend.right-axis-indicator">(right y-axis)</Trans>
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
<div className={styles.labelCellInner}>
|
||||
<button
|
||||
disabled={readonly}
|
||||
type="button"
|
||||
title={item.label}
|
||||
onBlur={onMouseOut}
|
||||
onFocus={onMouseOver}
|
||||
onMouseOver={onMouseOver}
|
||||
onMouseOut={onMouseOut}
|
||||
onClick={!readonly ? onClick : undefined}
|
||||
className={cx(styles.label, item.disabled && styles.labelDisabled)}
|
||||
>
|
||||
{item.label}{' '}
|
||||
{item.yAxis === 2 && (
|
||||
<span className={styles.yAxisLabel}>
|
||||
<Trans i18nKey="grafana-ui.viz-legend.right-axis-indicator">(right y-axis)</Trans>
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
{item.getDisplayValues &&
|
||||
@@ -128,6 +130,28 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
background: rowHoverBg,
|
||||
},
|
||||
}),
|
||||
labelCell: css({
|
||||
label: 'LegendLabelCell',
|
||||
maxWidth: 0,
|
||||
width: '100%',
|
||||
minWidth: theme.spacing(16),
|
||||
}),
|
||||
labelCellInner: css({
|
||||
label: 'LegendLabelCellInner',
|
||||
display: 'block',
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
overflowX: 'auto',
|
||||
overflowY: 'hidden',
|
||||
paddingRight: theme.spacing(3),
|
||||
scrollbarWidth: 'none',
|
||||
msOverflowStyle: 'none',
|
||||
maskImage: `linear-gradient(to right, black calc(100% - ${theme.spacing(3)}), transparent 100%)`,
|
||||
WebkitMaskImage: `linear-gradient(to right, black calc(100% - ${theme.spacing(3)}), transparent 100%)`,
|
||||
'&::-webkit-scrollbar': {
|
||||
display: 'none',
|
||||
},
|
||||
}),
|
||||
label: css({
|
||||
label: 'LegendLabel',
|
||||
whiteSpace: 'nowrap',
|
||||
@@ -135,9 +159,6 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
border: 'none',
|
||||
fontSize: 'inherit',
|
||||
padding: 0,
|
||||
maxWidth: '600px',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
userSelect: 'text',
|
||||
}),
|
||||
labelDisabled: css({
|
||||
|
||||
@@ -403,9 +403,6 @@ export function useUpdateFolder() {
|
||||
spec: { title: folder.title },
|
||||
metadata: {
|
||||
name: folder.uid,
|
||||
annotations: {
|
||||
...(folder.parentUid && { [AnnoKeyFolder]: folder.parentUid }),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user