diff --git a/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx b/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx index 52d279fa5c6..5ca9a009cc2 100644 --- a/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx @@ -1,4 +1,4 @@ -import { findByRole, findByText, findByTitle, getByTestId, render } from '@testing-library/react'; +import { findByRole, findByText, findByTitle, getByTestId, queryByText, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; @@ -163,6 +163,71 @@ describe('AnnotationsField', function () { expect(annotationValueElements[1]).toHaveTextContent('2'); }); + it('should not show rows as panels', async function () { + mockSearchApiResponse(server, [ + mockDashboardSearchItem({ title: 'My dashboard', uid: 'dash-test-uid', type: DashboardSearchItemType.DashDB }), + ]); + + mockGetDashboardResponse( + mockDashboardDto({ + title: 'My dashboard', + uid: 'dash-test-uid', + panels: [ + { id: 1, title: 'Row panel', type: 'row' }, + { id: 2, title: 'First panel', type: 'timeseries' }, + ], + }) + ); + + const user = userEvent.setup(); + + render(); + + await user.click(ui.setDashboardButton.get()); + expect(ui.dashboardPicker.confirmButton.get()).toBeDisabled(); + + await user.click(await findByTitle(ui.dashboardPicker.dialog.get(), 'My dashboard')); + + expect(await findByText(ui.dashboardPicker.dialog.get(), 'First panel')).toBeInTheDocument(); + expect(await queryByText(ui.dashboardPicker.dialog.get(), 'Row panel')).not.toBeInTheDocument(); + }); + + it('should show panels within collapsed rows', async function () { + mockSearchApiResponse(server, [ + mockDashboardSearchItem({ title: 'My dashboard', uid: 'dash-test-uid', type: DashboardSearchItemType.DashDB }), + ]); + + mockGetDashboardResponse( + mockDashboardDto({ + title: 'My dashboard', + uid: 'dash-test-uid', + panels: [ + { id: 1, title: 'First panel', type: 'timeseries' }, + { + id: 2, + title: 'Row panel', + collapsed: true, + type: 'row', + panels: [{ id: 3, title: 'Panel within collapsed row', type: 'timeseries' }], + }, + ], + }) + ); + + const user = userEvent.setup(); + + render(); + + await user.click(ui.setDashboardButton.get()); + expect(ui.dashboardPicker.confirmButton.get()).toBeDisabled(); + + await user.click(await findByTitle(ui.dashboardPicker.dialog.get(), 'My dashboard')); + + expect(await findByText(ui.dashboardPicker.dialog.get(), 'First panel')).toBeInTheDocument(); + expect(await queryByText(ui.dashboardPicker.dialog.get(), 'Row panel')).not.toBeInTheDocument(); + expect(await findByText(ui.dashboardPicker.dialog.get(), 'Panel within collapsed row')).toBeInTheDocument(); + }); + // this test _should_ work in theory but something is stopping the 'onClick' function on the dashboard item // to trigger "handleDashboardChange" – skipping it for now but has been manually tested. it.skip('should update existing dashboard and panel identifies', async function () { diff --git a/public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.tsx b/public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.tsx index 67406197e6c..7617ff84438 100644 --- a/public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.tsx @@ -15,7 +15,7 @@ import { Annotation, annotationLabels } from '../../utils/constants'; import AnnotationHeaderField from './AnnotationHeaderField'; import DashboardAnnotationField from './DashboardAnnotationField'; -import { DashboardPicker, PanelDTO } from './DashboardPicker'; +import { DashboardPicker, mergePanels, PanelDTO } from './DashboardPicker'; import { NeedHelpInfo } from './NeedHelpInfo'; import { RuleEditorSection } from './RuleEditorSection'; @@ -53,7 +53,9 @@ const AnnotationsStep = () => { } setSelectedDashboard(dashboardResult?.dashboard); - const currentPanel = dashboardResult?.dashboard?.panels?.find((panel) => panel.id.toString() === selectedPanelId); + + const allPanels = mergePanels(dashboardResult); + const currentPanel = allPanels.find((panel) => panel.id.toString() === selectedPanelId); setSelectedPanel(currentPanel); }, [selectedPanelId, dashboardResult, isDashboardFetching]); diff --git a/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.tsx b/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.tsx index b78338b093a..444325c2535 100644 --- a/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.tsx @@ -17,6 +17,7 @@ import { Tooltip, useStyles2, } from '@grafana/ui'; +import { DashboardDTO } from 'app/types'; import { dashboardApi } from '../../api/dashboardApi'; @@ -47,6 +48,18 @@ interface DashboardPickerProps { onDismiss: () => void; } +export function mergePanels(dashboardResult: DashboardDTO | undefined) { + const panels = dashboardResult?.dashboard?.panels?.filter((panel) => panel.type !== 'row') || []; + const nestedPanels = + dashboardResult?.dashboard?.panels + ?.filter((row: { collapsed: boolean }) => row.collapsed) + .map((collapsedRow: { panels: PanelDTO[] }) => collapsedRow.panels) || []; + + const allDashboardPanels = [...panels, ...nestedPanels.flat()]; + + return allDashboardPanels; +} + export const DashboardPicker = ({ dashboardUid, panelId, isOpen, onChange, onDismiss }: DashboardPickerProps) => { const styles = useStyles2(getPickerStyles); @@ -72,12 +85,14 @@ export const DashboardPicker = ({ dashboardUid, panelId, isOpen, onChange, onDis setSelectedPanelId(undefined); }, []); + const allDashboardPanels = mergePanels(dashboardResult); + const filteredPanels = - dashboardResult?.dashboard?.panels + allDashboardPanels ?.filter((panel) => panel.title?.toLowerCase().includes(panelFilter.toLowerCase())) .sort(panelSort) ?? []; - const currentPanel: PanelDTO | undefined = dashboardResult?.dashboard?.panels?.find( + const currentPanel: PanelDTO | undefined = allDashboardPanels.find( (panel: PanelDTO) => isValidPanelIdentifier(panel) && panel.id?.toString() === selectedPanelId );