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 a5645d048cf..430c84bd53b 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 { findByText, findByTitle, render } from '@testing-library/react';
+import { findByRole, findByText, findByTitle, getByTestId, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
@@ -107,8 +107,8 @@ describe('AnnotationsField', function () {
title: 'My dashboard',
uid: 'dash-test-uid',
panels: [
- { id: 1, title: 'First panel' },
- { id: 2, title: 'Second panel' },
+ { id: 1, title: 'First panel', type: 'timeseries' },
+ { id: 2, title: 'Second panel', type: 'timeseries' },
],
})
);
@@ -137,8 +137,8 @@ describe('AnnotationsField', function () {
title: 'My dashboard',
uid: 'dash-test-uid',
panels: [
- { id: 1, title: 'First panel' },
- { id: 2, title: 'Second panel' },
+ { id: 1, title: 'First panel', type: 'graph' },
+ { id: 2, title: 'Second panel', type: 'graph' },
],
})
);
@@ -186,8 +186,8 @@ describe('AnnotationsField', function () {
title: 'My dashboard',
uid: 'dash-test-uid',
panels: [
- { id: 1, title: 'First panel' },
- { id: 2, title: 'Second panel' },
+ { id: 1, title: 'First panel', type: 'timeseries' },
+ { id: 2, title: 'Second panel', type: 'timeseries' },
],
})
);
@@ -195,7 +195,7 @@ describe('AnnotationsField', function () {
mockDashboardDto({
title: 'My other dashboard',
uid: 'dash-other-uid',
- panels: [{ id: 3, title: 'Third panel' }],
+ panels: [{ id: 3, title: 'Third panel', type: 'timeseries' }],
})
);
@@ -216,10 +216,12 @@ describe('AnnotationsField', function () {
expect(annotationValueElements[0]).toHaveTextContent('dash-test-uid');
expect(annotationValueElements[1]).toHaveTextContent('1');
+ const { confirmButton, dialog } = ui.dashboardPicker;
+
await user.click(ui.setDashboardButton.get());
- await user.click(await findByTitle(ui.dashboardPicker.dialog.get(), 'My other dashboard'));
- await user.click(await findByText(ui.dashboardPicker.dialog.get(), 'Third panel'));
- await user.click(ui.dashboardPicker.confirmButton.get());
+ await user.click(await findByRole(dialog.get(), 'button', { name: /My other dashboard/ }));
+ await user.click(await findByRole(dialog.get(), 'button', { name: /Third panel/ }));
+ await user.click(confirmButton.get());
expect(ui.dashboardPicker.dialog.query()).not.toBeInTheDocument();
@@ -235,6 +237,36 @@ describe('AnnotationsField', function () {
expect(annotationKeyElements[1]).toHaveTextContent('Panel ID');
expect(annotationValueElements[1]).toHaveTextContent('3');
});
+
+ it('should render warning icon for panels of type other than graph and timeseries', 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: 'bar' },
+ { id: 2, title: 'Second panel', type: 'graph' },
+ ],
+ })
+ );
+
+ const user = userEvent.setup();
+
+ render();
+
+ const { dialog } = ui.dashboardPicker;
+
+ await user.click(ui.setDashboardButton.get());
+ await user.click(await findByTitle(dialog.get(), 'My dashboard'));
+
+ const warnedPanel = await findByRole(dialog.get(), 'button', { name: /First panel/ });
+
+ expect(getByTestId(warnedPanel, 'warning-icon')).toBeInTheDocument();
+ });
});
});
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 ab0fd01aa52..a26569be67c 100644
--- a/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.tsx
+++ b/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.tsx
@@ -5,13 +5,24 @@ import AutoSizer from 'react-virtualized-auto-sizer';
import { FixedSizeList } from 'react-window';
import { GrafanaTheme2 } from '@grafana/data/src';
-import { FilterInput, LoadingPlaceholder, useStyles2, Icon, Modal, Button, Alert } from '@grafana/ui';
+import {
+ FilterInput,
+ LoadingPlaceholder,
+ useStyles2,
+ Icon,
+ Modal,
+ Button,
+ Alert,
+ clearButtonStyles,
+ Tooltip,
+} from '@grafana/ui';
import { dashboardApi } from '../../api/dashboardApi';
export interface PanelDTO {
id: number;
title?: string;
+ type: string;
}
function panelSort(a: PanelDTO, b: PanelDTO) {
@@ -62,7 +73,7 @@ export const DashboardPicker = ({ dashboardUid, panelId, isOpen, onChange, onDis
const filteredPanels =
dashboardResult?.dashboard?.panels
- ?.filter((panel): panel is PanelDTO => typeof panel.id === 'number')
+ ?.filter((panel): panel is PanelDTO => typeof panel.id === 'number' && typeof panel.type === 'string')
?.filter((panel) => panel.title?.toLowerCase().includes(panelFilter.toLowerCase()))
.sort(panelSort) ?? [];
@@ -102,10 +113,10 @@ export const DashboardPicker = ({ dashboardUid, panelId, isOpen, onChange, onDis
handleDashboardChange(dashboard.uid)}
>
-
{dashboard.title}
+
{dashboard.title}
{dashboard.folderTitle ?? 'General'}
@@ -115,16 +126,28 @@ export const DashboardPicker = ({ dashboardUid, panelId, isOpen, onChange, onDis
const PanelRow = ({ index, style }: { index: number; style: CSSProperties }) => {
const panel = filteredPanels[index];
+ const panelTitle = panel.title || '
';
const isSelected = selectedPanelId === panel.id.toString();
+ const isAlertingCompatible = panel.type === 'graph' || panel.type === 'timeseries';
return (
- setSelectedPanelId(panel.id.toString())}
>
- {panel.title || ''}
-
+
+ {panelTitle}
+
+ {!isAlertingCompatible && (
+
+
+
+ )}
+
);
};
@@ -184,12 +207,16 @@ export const DashboardPicker = ({ dashboardUid, panelId, isOpen, onChange, onDis
- {!dashboardUid && !isDashboardFetching &&
Select a dashboard to get a list of available panels
}
+ {!selectedDashboardUid && !isDashboardFetching && (
+
+
Select a dashboard to get a list of available panels
+
+ )}
{isDashboardFetching && (
)}
- {!isDashboardFetching && (
+ {selectedDashboardUid && !isDashboardFetching && (
{({ width, height }) => (
@@ -221,60 +248,93 @@ export const DashboardPicker = ({ dashboardUid, panelId, isOpen, onChange, onDis
);
};
-const getPickerStyles = (theme: GrafanaTheme2) => ({
- container: css`
- display: grid;
- grid-template-columns: 1fr 1fr;
- grid-template-rows: min-content auto;
- gap: ${theme.spacing(2)};
- flex: 1;
- `,
- column: css`
- flex: 1 1 auto;
- `,
- dashboardTitle: css`
- height: 22px;
- font-weight: ${theme.typography.fontWeightBold};
- `,
- dashboardFolder: css`
- height: 20px;
- font-size: ${theme.typography.bodySmall.fontSize};
- color: ${theme.colors.text.secondary};
- display: flex;
- flex-direction: row;
- justify-content: flex-start;
- column-gap: ${theme.spacing(1)};
- align-items: center;
- `,
- row: css`
- padding: ${theme.spacing(0.5)};
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- cursor: pointer;
- border: 2px solid transparent;
- `,
- rowSelected: css`
- border-color: ${theme.colors.primary.border};
- `,
- rowOdd: css`
- background-color: ${theme.colors.background.secondary};
- `,
- loadingPlaceholder: css`
- height: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- `,
- modal: css`
- height: 100%;
- `,
- modalContent: css`
- flex: 1;
- display: flex;
- flex-direction: column;
- `,
- modalAlert: css`
- flex-grow: 0;
- `,
-});
+const getPickerStyles = (theme: GrafanaTheme2) => {
+ const clearButton = clearButtonStyles(theme);
+
+ return {
+ container: css`
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-template-rows: min-content auto;
+ gap: ${theme.spacing(2)};
+ flex: 1;
+ `,
+ column: css`
+ flex: 1 1 auto;
+ `,
+ dashboardTitle: css`
+ height: 22px;
+ font-weight: ${theme.typography.fontWeightBold};
+ `,
+ dashboardFolder: css`
+ height: 20px;
+ font-size: ${theme.typography.bodySmall.fontSize};
+ color: ${theme.colors.text.secondary};
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-start;
+ column-gap: ${theme.spacing(1)};
+ align-items: center;
+ `,
+ rowButton: css`
+ ${clearButton};
+ padding: ${theme.spacing(0.5)};
+ overflow: hidden;
+ text-overflow: ellipsis;
+ text-align: left;
+ white-space: nowrap;
+ cursor: pointer;
+ border: 2px solid transparent;
+
+ &:disabled {
+ cursor: not-allowed;
+ color: ${theme.colors.text.disabled};
+ }
+ `,
+ rowButtonTitle: css`
+ text-overflow: ellipsis;
+ overflow: hidden;
+ `,
+ rowSelected: css`
+ border-color: ${theme.colors.primary.border};
+ `,
+ rowOdd: css`
+ background-color: ${theme.colors.background.secondary};
+ `,
+ panelButton: css`
+ display: flex;
+ gap: ${theme.spacing(1)};
+ justify-content: space-between;
+ align-items: center;
+ `,
+ loadingPlaceholder: css`
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ `,
+ selectDashboardPlaceholder: css`
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ text-align: center;
+ font-weight: ${theme.typography.fontWeightBold};
+ `,
+ modal: css`
+ height: 100%;
+ `,
+ modalContent: css`
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ `,
+ modalAlert: css`
+ flex-grow: 0;
+ `,
+ warnIcon: css`
+ fill: ${theme.colors.warning.main};
+ `,
+ };
+};