Dynamic Dashboards: Disallow adding empty row and tab titles (#113941)
This commit is contained in:
@@ -503,6 +503,60 @@ test.describe(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
test('cannot add a row without a title', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Cannot add row without title');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
|
||||
// edit row title to a non-default and click away to trigger onBlur
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.RowsLayout.titleInput)
|
||||
.fill('Test row 1');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.backButton).click();
|
||||
|
||||
// clear the title input to simulate no title and click away to trigger onBlur
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('Test row 1')).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.RowsLayout.titleInput)
|
||||
.fill('');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.backButton).click();
|
||||
|
||||
// title should be set to a default name
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
|
||||
// add another row
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
|
||||
// edit row title to a non-default and click away to trigger onBlur
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.RowsLayout.titleInput)
|
||||
.fill('Test row 2');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.backButton).click();
|
||||
|
||||
// clear the title input to simulate no title and click away to trigger onBlur
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('Test row 2')).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.RowsLayout.titleInput)
|
||||
.fill('');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.backButton).click();
|
||||
|
||||
// title should be set to a default name + 1 to avoid duplicates
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
/*
|
||||
* Tabs
|
||||
@@ -835,5 +889,51 @@ test.describe(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('cannot add a tab without a title', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Cannot add tab without title');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoTab(page, dashboardPage, selectors);
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
|
||||
// edit tab title to a non-default and click away to trigger onBlur
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput)
|
||||
.fill('Test tab 1');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.backButton).click();
|
||||
|
||||
// clear the title input to simulate no title and click away to trigger onBlur
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Test tab 1')).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput)
|
||||
.fill('');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.backButton).click();
|
||||
|
||||
// title should be set to a default name
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
|
||||
// add another tab
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addTab).click();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeVisible();
|
||||
|
||||
// edit tab title to a non-default and click away to trigger onBlur
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput)
|
||||
.fill('Test tab 2');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.backButton).click();
|
||||
|
||||
// clear the title input to simulate no title and click away to trigger onBlur
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Test tab 2')).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput)
|
||||
.fill('');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.backButton).click();
|
||||
|
||||
// title should be set to a default name + 1 to avoid duplicates
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -683,6 +683,11 @@ export const versionedComponents = {
|
||||
'12.2.0': 'data-testid tab title input',
|
||||
},
|
||||
},
|
||||
RowsLayout: {
|
||||
titleInput: {
|
||||
'12.3.0': 'data-testid row title input',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
PanelInspector: {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useConditionalRenderingEditor } from '../../conditional-rendering/hooks
|
||||
import { dashboardEditActions } from '../../edit-pane/shared';
|
||||
import { getQueryRunnerFor, useDashboard } from '../../utils/utils';
|
||||
import { useLayoutCategory } from '../layouts-shared/DashboardLayoutSelector';
|
||||
import { useEditPaneInputAutoFocus } from '../layouts-shared/utils';
|
||||
import { generateUniqueTitle, useEditPaneInputAutoFocus } from '../layouts-shared/utils';
|
||||
|
||||
import { RowItem } from './RowItem';
|
||||
|
||||
@@ -108,6 +108,7 @@ function RowTitleInput({ row, isNewElement }: { row: RowItem; isNewElement: bool
|
||||
onFocus={() => (prevTitle.current = title || '')}
|
||||
onBlur={() => editRowTitleAction(row, title || '', prevTitle.current || '')}
|
||||
onChange={(e) => row.onChangeTitle(e.currentTarget.value)}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.RowsLayout.titleInput}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
@@ -175,10 +176,18 @@ function RowRepeatSelect({ row, id }: { row: RowItem; id?: string }) {
|
||||
}
|
||||
|
||||
function editRowTitleAction(row: RowItem, title: string, prevTitle: string) {
|
||||
if (title === prevTitle) {
|
||||
if (title !== '' && title === prevTitle) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (title === '') {
|
||||
const rowsLayout = row.getParentLayout();
|
||||
const existingNames = new Set(
|
||||
rowsLayout.state.rows.map((row) => row.state.title).filter((title) => title !== undefined)
|
||||
);
|
||||
title = generateUniqueTitle('New row', existingNames);
|
||||
}
|
||||
|
||||
dashboardEditActions.edit({
|
||||
description: t('dashboard.edit-actions.row-title', 'Change row title'),
|
||||
source: row,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useConditionalRenderingEditor } from '../../conditional-rendering/hooks
|
||||
import { dashboardEditActions } from '../../edit-pane/shared';
|
||||
import { getQueryRunnerFor, useDashboard } from '../../utils/utils';
|
||||
import { useLayoutCategory } from '../layouts-shared/DashboardLayoutSelector';
|
||||
import { useEditPaneInputAutoFocus } from '../layouts-shared/utils';
|
||||
import { generateUniqueTitle, useEditPaneInputAutoFocus } from '../layouts-shared/utils';
|
||||
|
||||
import { TabItem } from './TabItem';
|
||||
|
||||
@@ -147,10 +147,16 @@ function TabRepeatSelect({ tab, id }: { tab: TabItem; id?: string }) {
|
||||
}
|
||||
|
||||
function editTabTitleAction(tab: TabItem, title: string, prevTitle: string) {
|
||||
if (title === prevTitle) {
|
||||
if (title !== '' && title === prevTitle) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (title === '') {
|
||||
const tabs = tab.getParentLayout().getTabsIncludingRepeats();
|
||||
const existingNames = new Set(tabs.map((tab) => tab.state.title).filter((title) => title !== undefined));
|
||||
title = generateUniqueTitle('New tab', existingNames);
|
||||
}
|
||||
|
||||
dashboardEditActions.edit({
|
||||
description: t('dashboard.edit-actions.tab-title', 'Change tab title'),
|
||||
source: tab,
|
||||
|
||||
Reference in New Issue
Block a user