Repeats: Add E2E tests for tabs layout repeats (#112109)

* add e2e tests for repeated tabs

* finalise tab repeat e2e tests
This commit is contained in:
Sergej-Vlasov
2025-10-09 16:51:36 +03:00
committed by GitHub
parent caaccb7984
commit d408e712e5
8 changed files with 1377 additions and 2 deletions
+1
View File
@@ -429,6 +429,7 @@
/e2e-playwright/dashboards/TestDashboard.json @grafana/dashboards-squad @grafana/grafana-search-navigate-organise
/e2e-playwright/dashboards/TestV2Dashboard.json @grafana/dashboards-squad
/e2e-playwright/dashboards/V2DashWithRepeats.json @grafana/dashboards-squad
/e2e-playwright/dashboards/V2DashWithTabRepeats.json @grafana/dashboards-squad
/e2e-playwright/dashboards-suite/adhoc-filter-from-panel.spec.ts @grafana/datapro
/e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts @grafana/grafana-search-navigate-organise
/e2e-playwright/dashboards-suite/dashboard-browse.spec.ts @grafana/grafana-search-navigate-organise
@@ -1,6 +1,5 @@
import { test, expect } from '@grafana/plugin-e2e';
import testV2Dashboard from '../dashboards/TestV2Dashboard.json';
import testV2DashWithRepeats from '../dashboards/V2DashWithRepeats.json';
import {
@@ -0,0 +1,487 @@
import { test, expect } from '@grafana/plugin-e2e';
import V2DashWithTabRepeats from '../dashboards/V2DashWithTabRepeats.json';
import {
verifyChanges,
saveDashboard,
importTestDashboard,
goToEmbeddedPanel,
checkRepeatedTabTitles,
groupIntoTab,
moveTab,
getTabPosition,
} from './utils';
const repeatTitleBase = 'Tab - ';
const newTitleBase = 'edited tab rep - ';
const repeatOptions = [1, 2, 3, 4];
test.use({
featureToggles: {
kubernetesDashboards: true,
dashboardNewLayouts: true,
groupByVariable: true,
},
});
test.use({
viewport: { width: 1920, height: 1080 },
});
test.describe(
'Repeats - Dashboard tabs layout',
{
tag: ['@dashboards'],
},
() => {
test('can enable tab repeats', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(page, selectors, 'Tabs layout repeats - add repeats');
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
await groupIntoTab(page, dashboardPage, selectors);
await dashboardPage
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput)
.fill(`${repeatTitleBase}$c1`);
await expect(
dashboardPage.getByGrafanaSelector(
selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.join(' + ')}`)
)
).toBeVisible();
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 checkRepeatedTabTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions);
await saveDashboard(dashboardPage, page, selectors);
await page.reload();
await checkRepeatedTabTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions);
});
test('can update tab repeats with variable change', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(
page,
selectors,
'Tabs layout repeats - update on variable change',
JSON.stringify(V2DashWithTabRepeats)
);
const c1Var = dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('c1'));
await c1Var
.locator('..')
.getByTestId(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(repeatOptions.join(',')))
.click();
// deselect last variable option
await dashboardPage
.getByGrafanaSelector(
selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts(`${repeatOptions.at(-1)}`)
)
.click();
await page.locator('body').click({ position: { x: 0, y: 0 } }); // blur select
// verify that repeats are present for first 3 values
await checkRepeatedTabTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions.slice(0, -1));
// verify there is no repeat with last value
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(-1)}`))
).toBeHidden();
});
test('can update repeats in edit pane', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(
page,
selectors,
'Tabs layout repeats - update through edit pane',
JSON.stringify(V2DashWithTabRepeats)
);
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
// select first/original repeat tab to activate edit pane
await dashboardPage
.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(0)}`))
.click();
const titleInput = dashboardPage.getByGrafanaSelector(
selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput
);
await titleInput.fill(`${newTitleBase}$c1`);
await titleInput.blur();
await checkRepeatedTabTitles(dashboardPage, selectors, newTitleBase, repeatOptions);
await saveDashboard(dashboardPage, page, selectors);
await page.reload();
await checkRepeatedTabTitles(dashboardPage, selectors, newTitleBase, repeatOptions);
});
test('can update repeats after panel change', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(
page,
selectors,
'Tabs layout repeats - update repeats after panel change',
JSON.stringify(V2DashWithTabRepeats)
);
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first().click();
const panelTitleInput = dashboardPage.getByGrafanaSelector(
selectors.components.PanelEditor.OptionsPane.fieldInput('Title')
);
await panelTitleInput.fill('New edited panel');
await panelTitleInput.blur();
await dashboardPage
.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(1)}`))
.click();
// intermediate step to verify tab switch happened
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 2 - Row 1 - Panel repeat 1'))
).toBeVisible();
// verify edited panel title updated in repeated tab
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New edited panel'))
).toBeVisible();
await saveDashboard(dashboardPage, page, selectors);
await page.reload();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New edited panel'))
).toBeVisible();
});
test('can update repeats after panel change in editor', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(
page,
selectors,
'Tabs layout repeats - update repeats after panel change in editor',
JSON.stringify(V2DashWithTabRepeats)
);
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).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('New edited panel');
await panelTitleInput.blur();
// playwright too fast, verifying JSON diff that changes landed
await verifyChanges(dashboardPage, page, selectors, 'New edited panel');
// verify panel title change in panel editor UI
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(`New edited panel`))
).toBeVisible();
await dashboardPage
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
.click();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.DashboardEditPaneSplitter.primaryBody)
).toBeVisible(); // verifying that dashboard loaded
await dashboardPage
.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(1)}`))
.click();
// intermediate step to verify tab switch happened
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 2 - Row 1 - Panel repeat 1'))
).toBeVisible();
// verify edited panel title updated in repeated tab
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New edited panel'))
).toBeVisible();
await saveDashboard(dashboardPage, page, selectors);
await page.reload();
// verify edited panel title updated in repeated tab
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New edited panel'))
).toBeVisible();
});
test('can hide canvas grid add row action in repeats', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(
page,
selectors,
'Tabs layout repeats - hide canvas add action in repeats',
JSON.stringify(V2DashWithTabRepeats)
);
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
await expect(dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow)).toBeVisible();
await dashboardPage
.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(1)}`))
.click();
await expect(dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow)).toBeHidden();
});
test('can move repeated tabs', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(
page,
selectors,
'Tabs layout repeats - move repeated tabs',
JSON.stringify(V2DashWithTabRepeats)
);
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
await moveTab(dashboardPage, page, selectors, `${repeatTitleBase}${repeatOptions.at(0)}`, 'New tab');
// playwright too fast - adding intermediate step so that UI has time to update
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab')).click();
// verify move by tab position
const repeatedTab = await getTabPosition(dashboardPage, selectors, `${repeatTitleBase}${repeatOptions.at(0)}`);
const normalTab = await getTabPosition(dashboardPage, selectors, 'New tab');
expect(normalTab?.x).toBeLessThan(repeatedTab?.x || 0);
await saveDashboard(dashboardPage, page, selectors);
await page.reload();
const repeatedTab2 = await getTabPosition(dashboardPage, selectors, `${repeatTitleBase}${repeatOptions.at(0)}`);
const normalTab2 = await getTabPosition(dashboardPage, selectors, 'New tab');
expect(normalTab2?.x).toBeLessThan(repeatedTab2?.x || 0);
});
test('can load into repeated tab', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(
page,
selectors,
'Tabs layout repeats - can load into repeated tab',
JSON.stringify(V2DashWithTabRepeats)
);
await dashboardPage
.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`))
.click();
await page.reload();
await expect(page.locator('[data-testid="uplot-main-div"]').first()).toBeVisible();
expect(
await dashboardPage
.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`))
.getAttribute('aria-selected')
).toBe('true');
});
test('can view panels in repeated tab', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(
page,
selectors,
'Tabs layout repeats - view panels in repeated tabs',
JSON.stringify(V2DashWithTabRepeats)
);
// non repeated panel in repeated tab
await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first().hover();
await page.keyboard.press('v');
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 1 - Panel repeat 1'))
).toBeHidden();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
).toBeVisible();
await page.reload();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
).toBeVisible();
await dashboardPage
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
.click();
// repeated panel in original tab repeat
await dashboardPage
.getByGrafanaSelector(selectors.components.DashboardRow.title('Row 2'))
.scrollIntoViewIfNeeded();
await dashboardPage
.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 2 - Panel repeat 2'))
.hover();
await page.keyboard.press('v');
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 1 - Panel repeat 1'))
).toBeHidden();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 2 - Panel repeat 2'))
).toBeVisible();
await page.reload();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 2 - Panel repeat 2'))
).toBeVisible();
await dashboardPage
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
.click();
// repeated panel in repeated tab
await dashboardPage
.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`))
.click();
await dashboardPage
.getByGrafanaSelector(selectors.components.DashboardRow.title('Row 2'))
.scrollIntoViewIfNeeded();
await dashboardPage
.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 2 - Panel repeat 2'))
.hover();
await page.keyboard.press('v');
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 1 - Panel repeat 1'))
).toBeHidden();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 2 - Panel repeat 2'))
).toBeVisible();
await page.reload();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 2 - Panel repeat 2'))
).toBeVisible();
});
test('can view embedded panels in repeated tab', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(
page,
selectors,
'Tabs layout repeats - view embedded panels in repeated tabs',
JSON.stringify(V2DashWithTabRepeats)
);
const dashUrl = page.url();
// non repeated panel in repeated tab
await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first().hover();
await page.keyboard.press('p+e');
await goToEmbeddedPanel(page);
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
).toBeVisible();
await page.goto(dashUrl);
// repeated panel in original tab repeat
await dashboardPage
.getByGrafanaSelector(selectors.components.DashboardRow.title('Row 2'))
.scrollIntoViewIfNeeded();
await dashboardPage
.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 2 - Panel repeat 2'))
.hover();
await page.keyboard.press('p+e');
await goToEmbeddedPanel(page);
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 2 - Panel repeat 2'))
).toBeVisible();
await page.goto(dashUrl);
// repeated panel in repeated tab
await dashboardPage
.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`))
.click();
await dashboardPage
.getByGrafanaSelector(selectors.components.DashboardRow.title('Row 2'))
.scrollIntoViewIfNeeded();
await dashboardPage
.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 2 - Panel repeat 2'))
.hover();
await page.keyboard.press('p+e');
await goToEmbeddedPanel(page);
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 2 - Panel repeat 2'))
).toBeVisible();
});
test('can remove repeats', async ({ dashboardPage, selectors, page }) => {
await importTestDashboard(
page,
selectors,
'Tabs layout repeats - remove repeats',
JSON.stringify(V2DashWithTabRepeats)
);
// verify 5 tabs are present (4 repeats and 1 normal)
await checkRepeatedTabTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions);
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
await dashboardPage
.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(0)}`))
.click();
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: 'Disable repeating' }).click();
await expect(
dashboardPage.getByGrafanaSelector(
selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.join(' + ')}`)
)
).toBeVisible();
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(1)}`))
).toBeHidden();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`))
).toBeHidden();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(3)}`))
).toBeHidden();
await saveDashboard(dashboardPage, page, selectors);
await page.reload();
await expect(
dashboardPage.getByGrafanaSelector(
selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.join(' + ')}`)
)
).toBeVisible();
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(1)}`))
).toBeHidden();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`))
).toBeHidden();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(3)}`))
).toBeHidden();
});
}
);
@@ -187,3 +187,48 @@ export async function goToEmbeddedPanel(page: Page) {
page.goto(soloPanelUrl!);
}
export async function moveTab(
dashboardPage: DashboardPage,
page: Page,
selectors: E2ESelectorGroups,
sourceTab: string,
targetTab: string
) {
// Get target panel position
const targetTabElement = dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(targetTab)).first();
// Get source panel element
const sourceTabElement = dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(sourceTab)).first();
const targetBox = await targetTabElement.boundingBox();
// Perform drag and drop (dragTo() did not work in this case)
await sourceTabElement.hover();
await page.mouse.down();
// move to adjusted target position (relative to top left)
await page.mouse.move((targetBox?.x || 0) + (targetBox?.width || 0), targetBox?.y || 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 checkRepeatedTabTitles(
dashboardPage: DashboardPage,
selectors: E2ESelectorGroups,
title: string,
options: Array<string | number>
) {
for (const option of options) {
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${title}${option}`))).toBeVisible();
}
}
export async function getTabPosition(dashboardPage: DashboardPage, selectors: E2ESelectorGroups, tabTitle: string) {
const tab = dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(tabTitle)).first();
const boundingBox = await tab.boundingBox();
return boundingBox;
}
@@ -0,0 +1,837 @@
{
"apiVersion": "dashboard.grafana.app/v2beta1",
"kind": "Dashboard",
"metadata": {
"name": "addwm76",
"namespace": "default",
"uid": "TOro1wyxnlZJEWBZN3MF2MuXtdBb1GOnG6zynaCifi4X",
"resourceVersion": "3",
"generation": 4,
"creationTimestamp": "2025-09-18T08:03:45Z",
"labels": {},
"annotations": {}
},
"spec": {
"annotations": [],
"cursorSync": "Off",
"description": "",
"editable": true,
"elements": {
"panel-1": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"datasource": {
"name": "PD8C576611E62080A"
},
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {},
"version": "v0"
},
"refId": "A"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 1,
"links": [],
"title": "Tab $c1 - Row $c2 - Panel repeat $c3",
"vizConfig": {
"group": "timeseries",
"kind": "VizConfig",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
}
},
"version": "12.3.0-pre"
}
}
},
"panel-2": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"datasource": {
"name": "PD8C576611E62080A"
},
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {
"scenarioId": "random_walk",
"seriesCount": 1
},
"version": "v0"
},
"refId": "A"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 2,
"links": [],
"title": "New panel",
"vizConfig": {
"group": "timeseries",
"kind": "VizConfig",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
}
},
"version": "12.3.0-pre"
}
}
},
"panel-3": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"datasource": {
"name": "PD8C576611E62080A"
},
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {},
"version": "v0"
},
"refId": "A"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 3,
"links": [],
"title": "New panel",
"vizConfig": {
"group": "timeseries",
"kind": "VizConfig",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
}
},
"version": "12.3.0-pre"
}
}
},
"panel-4": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"datasource": {
"name": "PD8C576611E62080A"
},
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {},
"version": "v0"
},
"refId": "A"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 4,
"links": [],
"title": "New panel",
"vizConfig": {
"group": "timeseries",
"kind": "VizConfig",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
}
},
"version": "12.3.0-pre"
}
}
},
"panel-5": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"datasource": {
"name": "PD8C576611E62080A"
},
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {},
"version": "v0"
},
"refId": "A"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 5,
"links": [],
"title": "New panel",
"vizConfig": {
"group": "timeseries",
"kind": "VizConfig",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
}
},
"version": "12.3.0-pre"
}
}
}
},
"layout": {
"kind": "TabsLayout",
"spec": {
"tabs": [
{
"kind": "TabsLayoutTab",
"spec": {
"layout": {
"kind": "RowsLayout",
"spec": {
"rows": [
{
"kind": "RowsLayoutRow",
"spec": {
"layout": {
"kind": "AutoGridLayout",
"spec": {
"columnWidthMode": "standard",
"items": [
{
"kind": "AutoGridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-5"
}
}
}
],
"maxColumnCount": 3,
"rowHeightMode": "standard"
}
},
"title": "New row"
}
},
{
"kind": "RowsLayoutRow",
"spec": {
"collapse": false,
"layout": {
"kind": "GridLayout",
"spec": {
"items": [
{
"kind": "GridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-1"
},
"height": 8,
"repeat": {
"direction": "h",
"mode": "variable",
"value": "c3"
},
"width": 24,
"x": 0,
"y": 0
}
},
{
"kind": "GridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-2"
},
"height": 8,
"width": 12,
"x": 0,
"y": 8
}
},
{
"kind": "GridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-3"
},
"height": 8,
"width": 12,
"x": 12,
"y": 8
}
}
]
}
},
"repeat": {
"mode": "variable",
"value": "c2"
},
"title": "Row $c2"
}
}
]
}
},
"repeat": {
"mode": "variable",
"value": "c1"
},
"title": "Tab - $c1"
}
},
{
"kind": "TabsLayoutTab",
"spec": {
"layout": {
"kind": "AutoGridLayout",
"spec": {
"columnWidthMode": "standard",
"items": [
{
"kind": "AutoGridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-4"
}
}
}
],
"maxColumnCount": 3,
"rowHeightMode": "standard"
}
},
"title": "New tab"
}
}
]
}
},
"links": [],
"liveNow": false,
"preload": false,
"tags": [],
"timeSettings": {
"autoRefresh": "",
"autoRefreshIntervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
"fiscalYearStartMonth": 0,
"from": "now-6h",
"hideTimepicker": false,
"timezone": "browser",
"to": "now"
},
"title": "tab repeats",
"variables": [
{
"kind": "CustomVariable",
"spec": {
"allowCustomValue": true,
"current": {
"text": ["1", "2", "3", "4"],
"value": ["1", "2", "3", "4"]
},
"hide": "dontHide",
"includeAll": true,
"multi": true,
"name": "c1",
"options": [
{
"selected": true,
"text": "1",
"value": "1"
},
{
"selected": true,
"text": "2",
"value": "2"
},
{
"selected": true,
"text": "3",
"value": "3"
},
{
"selected": true,
"text": "4",
"value": "4"
}
],
"query": "1,2,3,4",
"skipUrlSync": false
}
},
{
"kind": "CustomVariable",
"spec": {
"allowCustomValue": true,
"current": {
"text": ["1", "2", "3", "4"],
"value": ["1", "2", "3", "4"]
},
"hide": "dontHide",
"includeAll": true,
"multi": true,
"name": "c2",
"options": [
{
"selected": true,
"text": "1",
"value": "1"
},
{
"selected": true,
"text": "2",
"value": "2"
},
{
"selected": true,
"text": "3",
"value": "3"
},
{
"selected": true,
"text": "4",
"value": "4"
}
],
"query": "1,2,3,4",
"skipUrlSync": false
}
},
{
"kind": "CustomVariable",
"spec": {
"allowCustomValue": true,
"current": {
"text": ["1", "2", "3", "4"],
"value": ["1", "2", "3", "4"]
},
"hide": "dontHide",
"includeAll": false,
"multi": true,
"name": "c3",
"options": [
{
"selected": true,
"text": "1",
"value": "1"
},
{
"selected": true,
"text": "2",
"value": "2"
},
{
"selected": true,
"text": "3",
"value": "3"
},
{
"selected": true,
"text": "4",
"value": "4"
}
],
"query": "1,2,3,4",
"skipUrlSync": false
}
}
]
},
"status": {}
}
@@ -670,6 +670,11 @@ export const versionedComponents = {
'12.1.0': 'data-testid fill screen switch',
},
},
TabsLayout: {
titleInput: {
'12.2.0': 'data-testid tab title input',
},
},
},
},
PanelInspector: {
@@ -91,6 +91,7 @@ function TabTitleInput({ tab, isNewElement, id }: { tab: TabItem; isNewElement:
onFocus={() => (prevTitle.current = title || '')}
onBlur={() => editTabTitleAction(tab, title || '', prevTitle.current || '')}
onChange={(e) => tab.onChangeTitle(e.currentTarget.value)}
data-testid={selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput}
/>
</Field>
);
@@ -24,7 +24,7 @@ export function TabItemRenderer({ model }: SceneComponentProps<TabItem>) {
const mySlug = model.getSlug();
const urlKey = parentLayout.getUrlKey();
const isActive = mySlug === currentTabSlug;
const myIndex = parentLayout.state.tabs.findIndex((tab) => tab === model);
const myIndex = parentLayout.getTabsIncludingRepeats().findIndex((tab) => tab === model);
const location = useLocation();
const href = textUtil.sanitize(locationUtil.getUrlForPartial(location, { [urlKey]: mySlug }));
const styles = useStyles2(getStyles);