diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-add-panel.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-add-panel.spec.ts index c2af96349cf..78373c5e094 100644 --- a/e2e-playwright/dashboard-new-layouts/dashboards-add-panel.spec.ts +++ b/e2e-playwright/dashboard-new-layouts/dashboards-add-panel.spec.ts @@ -1,5 +1,7 @@ import { test, expect } from '@grafana/plugin-e2e'; +import { addNewPanelFromSidebar } from './utils'; + test.use({ featureToggles: { kubernetesDashboards: true, @@ -44,5 +46,94 @@ test.describe( .click(); await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.General.content)).toBeVisible(); }); + + test('can add a panel from the sidebar on a new dashboard', async ({ gotoDashboardPage, selectors, page }) => { + const dashboardPage = await gotoDashboardPage({}); + // check that the sidebar is open on Add section + expect(await dashboardPage.getByGrafanaSelector(selectors.components.Sidebar.newPanelButton)).toBeVisible(); + await dashboardPage.getByGrafanaSelector(selectors.components.Sidebar.newPanelButton).click(); + // check that new panel has been added + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) + ).toBeVisible(); + addNewPanelFromSidebar(dashboardPage, selectors); + // check that another has been added + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) + ).toHaveCount(2); + }); + + test('adds a new panel from the sidebar into the layout that was selected last', async ({ + gotoDashboardPage, + selectors, + page, + }) => { + const dashboardPage = await gotoDashboardPage({}); + await dashboardPage.getByGrafanaSelector(selectors.components.Sidebar.newPanelButton).click(); + + // group into tab + await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.groupPanels).click(); + await page.getByText('Group into tab').click(); + + // add new panel from the sidebar + addNewPanelFromSidebar(dashboardPage, selectors); + + // check that another panel has been added inside the tab + const tab = dashboardPage.getByGrafanaSelector(selectors.components.LayoutContainer('tab New tab')); + await expect(tab.getByTestId(selectors.components.Panels.Panel.title('New panel'))).toHaveCount(2); + + // add new tab + await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addTab).click(); + + // add new panel from the sidebar + addNewPanelFromSidebar(dashboardPage, selectors); + //check that new panel has been added there + const tab2 = dashboardPage.getByGrafanaSelector(selectors.components.LayoutContainer('tab New tab 1')); + await expect(tab2.getByTestId(selectors.components.Panels.Panel.title('New panel'))).toHaveCount(1); + + // panel is selected + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldInput('Title')) + ).toBeVisible(); + addNewPanelFromSidebar(dashboardPage, selectors); + await expect(tab2.getByTestId(selectors.components.Panels.Panel.title('New panel'))).toHaveCount(2); + + // group into row + await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.groupPanels).click(); + await page.getByText('Group into row').click(); + // add into the row + addNewPanelFromSidebar(dashboardPage, selectors); + const row = dashboardPage.getByGrafanaSelector(selectors.components.LayoutContainer('row New row')); + + // scroll to the bottom of the row to load all panels + const scrollContainer = page + .getByTestId(selectors.components.DashboardEditPaneSplitter.primaryBody) + .locator('> div') + .first(); + await scrollContainer.evaluate((el) => el.scrollTo(0, el.scrollHeight)); + + await expect(row.getByTestId(selectors.components.Panels.Panel.title('New panel'))).toHaveCount(3); + // add new row and add into it + await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow).click(); + addNewPanelFromSidebar(dashboardPage, selectors); + + const row1 = dashboardPage.getByGrafanaSelector(selectors.components.LayoutContainer('row New row 1')); + await expect(row1.getByTestId(selectors.components.Panels.Panel.title('New panel'))).toHaveCount(1); + + // panel is selected + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldInput('Title')) + ).toBeVisible(); + addNewPanelFromSidebar(dashboardPage, selectors); + + await scrollContainer.evaluate((el) => el.scrollTo(0, el.scrollHeight)); + + // check that the new panel is added next to the last panel selected + await expect(row1.getByTestId(selectors.components.Panels.Panel.title('New panel'))).toHaveCount(2); + }); } ); + +// utils + +// add new panel through sidebar diff --git a/e2e-playwright/dashboard-new-layouts/utils.ts b/e2e-playwright/dashboard-new-layouts/utils.ts index 69851994812..7294f270089 100644 --- a/e2e-playwright/dashboard-new-layouts/utils.ts +++ b/e2e-playwright/dashboard-new-layouts/utils.ts @@ -249,3 +249,8 @@ export async function switchToAutoGrid(page: Page, dashboardPage: DashboardPage) await confirmModal.click(); } } + +export async function addNewPanelFromSidebar(dashboardPage: DashboardPage, selectors: E2ESelectorGroups) { + await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.Sidebar.addButton).click(); + await dashboardPage.getByGrafanaSelector(selectors.components.Sidebar.newPanelButton).click(); +} diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index 0e7e7bf6150..fb1f271314c 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -64,6 +64,9 @@ export const versionedComponents = { dockToggle: { '12.4.0': 'data-testid sidebar-dock-toggle', }, + newPanelButton: { + '12.4.0': 'data-testid sidebar add new panel', + }, }, EditPaneHeader: { deleteButton: { @@ -79,6 +82,9 @@ export const versionedComponents = { '12.1.0': 'data-testid EditPaneHeader duplicate', }, }, + LayoutContainer: { + '12.4.0': (identifier: string) => `data-testid Layout container ${identifier}`, + }, TimePicker: { openButton: { [MIN_GRAFANA_VERSION]: 'data-testid TimePicker Open Button', diff --git a/public/app/features/dashboard-scene/edit-pane/DashboardSidePaneNew.tsx b/public/app/features/dashboard-scene/edit-pane/DashboardSidePaneNew.tsx index 6867aadd1c2..e7f221b0aba 100644 --- a/public/app/features/dashboard-scene/edit-pane/DashboardSidePaneNew.tsx +++ b/public/app/features/dashboard-scene/edit-pane/DashboardSidePaneNew.tsx @@ -2,6 +2,7 @@ import { css, cx } from '@emotion/css'; import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'; import { GrafanaTheme2 } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; import { t } from '@grafana/i18n'; import { SceneObject } from '@grafana/scenes'; import { Sidebar, Text, useStyles2 } from '@grafana/ui'; @@ -30,6 +31,7 @@ export function DashboardSidePaneNew({ onAddPanel, dashboard }: { onAddPanel: () return (
) { const { diff --git a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayoutRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayoutRenderer.tsx index a45e4507ed6..8246bacc8d4 100644 --- a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayoutRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayoutRenderer.tsx @@ -1,10 +1,12 @@ import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; import { SceneComponentProps, sceneGraph } from '@grafana/scenes'; import { useStyles2 } from '@grafana/ui'; import { isRepeatCloneOrChildOf } from '../../utils/clone'; +import { getTestIdForLayout } from '../../utils/test-utils'; import { useDashboardState } from '../../utils/utils'; import { useSoloPanelContext } from '../SoloPanelContext'; import { CanvasGridAddActions } from '../layouts-shared/CanvasGridAddActions'; @@ -33,6 +35,7 @@ export function AutoGridLayoutRenderer({ model }: SceneComponentProps diff --git a/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx index e299272de78..94bc35458e4 100644 --- a/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx @@ -1,6 +1,7 @@ import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; import { t } from '@grafana/i18n'; import { config } from '@grafana/runtime'; import { @@ -31,6 +32,7 @@ import { import { serializeDefaultGridLayout } from '../../serialization/layoutSerializers/DefaultGridLayoutSerializer'; import { isRepeatCloneOrChildOf } from '../../utils/clone'; import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph'; +import { getTestIdForLayout } from '../../utils/test-utils'; import { forceRenderChildren, getPanelIdForVizPanel, @@ -657,7 +659,10 @@ function DefaultGridLayoutManagerRenderer({ model }: SceneComponentProps +
{model.state.grid.Component && } {showCanvasActions && (
diff --git a/public/app/features/dashboard-scene/utils/test-utils.ts b/public/app/features/dashboard-scene/utils/test-utils.ts index f121e185d6f..17e6ab76709 100644 --- a/public/app/features/dashboard-scene/utils/test-utils.ts +++ b/public/app/features/dashboard-scene/utils/test-utils.ts @@ -23,12 +23,18 @@ import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from 'app/features/variables/co import { DashboardDTO } from 'app/types/dashboard'; import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks'; +import { AutoGridLayout } from '../scene/layout-auto-grid/AutoGridLayout'; import { DashboardGridItem, RepeatDirection } from '../scene/layout-default/DashboardGridItem'; import { DefaultGridLayoutManager } from '../scene/layout-default/DefaultGridLayoutManager'; import { RowRepeaterBehavior } from '../scene/layout-default/RowRepeaterBehavior'; +import { RowItem } from '../scene/layout-rows/RowItem'; +import { TabItem } from '../scene/layout-tabs/TabItem'; +import { DashboardLayoutGrid } from '../scene/types/DashboardLayoutGrid'; import { transformSaveModelSchemaV2ToScene } from '../serialization/transformSaveModelSchemaV2ToScene'; import { transformSceneToSaveModelSchemaV2 } from '../serialization/transformSceneToSaveModelSchemaV2'; +import { getRowOrTabForSceneObject } from './utils'; + export function setupLoadDashboardMock(rsp: DeepPartial, spy?: jest.Mock) { const loadDashboardMock = (spy || jest.fn()).mockResolvedValue(rsp); const loadSnapshotMock = (spy || jest.fn()).mockResolvedValue(rsp); @@ -311,3 +317,12 @@ export function getTestDashboardSceneFromSaveModel(spec?: Partial