diff --git a/.betterer.results b/.betterer.results
index a07a231954f..28b253c9993 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -1597,9 +1597,7 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "3"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "4"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "5"]
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
],
"public/app/features/alerting/unified/RedirectToRuleViewer.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
@@ -3301,7 +3299,7 @@ exports[`better eslint`] = {
"public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
],
"public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx:5381": [
diff --git a/public/app/features/alerting/unified/PanelAlertTabContent.tsx b/public/app/features/alerting/unified/PanelAlertTabContent.tsx
index 0bd2a1e7def..f4d527640a6 100644
--- a/public/app/features/alerting/unified/PanelAlertTabContent.tsx
+++ b/public/app/features/alerting/unified/PanelAlertTabContent.tsx
@@ -3,6 +3,7 @@ import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Alert, LoadingPlaceholder, ScrollContainer, useStyles2 } from '@grafana/ui';
+import { Trans } from 'app/core/internationalization';
import { contextSrv } from 'app/core/services/context_srv';
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
@@ -59,18 +60,26 @@ export const PanelAlertTabContent = ({ dashboard, panel }: Props) => {
);
}
+ const isNew = !Boolean(dashboard.uid);
+
return (
+
+ There are no alert rules linked to this panel.
+
+
{!!dashboard.meta.canSave && canCreateRules && }
>
)}
- {!dashboard.uid && !!dashboard.meta.canSave && (
+ {isNew && !!dashboard.meta.canSave && (
- Dashboard must be saved before alerts can be added.
+
+ Dashboard must be saved before alerts can be added.
+
)}
diff --git a/public/app/features/alerting/unified/hooks/useCombinedRuleNamespaces.ts b/public/app/features/alerting/unified/hooks/useCombinedRuleNamespaces.ts
index 1b8043691d9..f28bc644ecc 100644
--- a/public/app/features/alerting/unified/hooks/useCombinedRuleNamespaces.ts
+++ b/public/app/features/alerting/unified/hooks/useCombinedRuleNamespaces.ts
@@ -494,6 +494,8 @@ export function useCombinedRules(
result?: CombinedRuleNamespace[];
error?: unknown;
} {
+ const isNewDashboard = !Boolean(dashboardUID);
+
const {
currentData: promRuleNs,
isLoading: isLoadingPromRules,
@@ -505,8 +507,7 @@ export function useCombinedRules(
panelId,
},
{
- // "null" means the dashboard isn't saved yet, as opposed to "undefined" which means we don't want to filter by dashboard UID
- skip: dashboardUID === null,
+ skip: isNewDashboard,
pollingInterval: poll ? RULE_LIST_POLL_INTERVAL_MS : undefined,
}
);
@@ -522,7 +523,7 @@ export function useCombinedRules(
},
{
pollingInterval: poll ? RULE_LIST_POLL_INTERVAL_MS : undefined,
- skip: dashboardUID === null,
+ skip: isNewDashboard,
}
);
diff --git a/public/app/features/apiserver/types.ts b/public/app/features/apiserver/types.ts
index 1df68585beb..bbbdf2f1246 100644
--- a/public/app/features/apiserver/types.ts
+++ b/public/app/features/apiserver/types.ts
@@ -54,7 +54,6 @@ export const AnnoKeySavedFromUI = 'grafana.app/saved-from-ui';
export const AnnoKeyDashboardNotFound = 'grafana.app/dashboard-not-found';
export const AnnoKeyDashboardIsSnapshot = 'grafana.app/dashboard-is-snapshot';
export const AnnoKeyDashboardSnapshotOriginalUrl = 'grafana.app/dashboard-snapshot-original-url';
-export const AnnoKeyDashboardIsNew = 'grafana.app/dashboard-is-new';
export const AnnoKeyDashboardGnetId = 'grafana.app/dashboard-gnet-id';
// Annotations provided by the API
@@ -83,7 +82,6 @@ type GrafanaClientAnnotations = {
[AnnoKeyDashboardNotFound]?: boolean;
[AnnoKeyDashboardIsSnapshot]?: boolean;
[AnnoKeyDashboardSnapshotOriginalUrl]?: string;
- [AnnoKeyDashboardIsNew]?: boolean;
// TODO: This should be provided by the API
// This is the dashboard ID for the Gcom API. This set when a dashboard is created through importing a dashboard from Grafana.com.
diff --git a/public/app/features/dashboard-scene/inspect/PanelInspectDrawer.test.tsx b/public/app/features/dashboard-scene/inspect/PanelInspectDrawer.test.tsx
new file mode 100644
index 00000000000..49998fa30d9
--- /dev/null
+++ b/public/app/features/dashboard-scene/inspect/PanelInspectDrawer.test.tsx
@@ -0,0 +1,76 @@
+import { locationService } from '@grafana/runtime';
+
+import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene';
+import { DefaultGridLayoutManager } from '../scene/layout-default/DefaultGridLayoutManager';
+
+import { onPanelInspectClose } from './PanelInspectDrawer';
+
+describe('onPanelInspectClose', () => {
+ test('when on default home dashboard page', async () => {
+ locationService.push('/');
+
+ const { scene } = await buildTestScene({
+ url: '',
+ slug: '',
+ });
+
+ onPanelInspectClose(scene);
+ expect(locationService.getLocation().pathname).toBe('/');
+ });
+
+ test('when on custom home dashboard page with uid defined', async () => {
+ locationService.push('/');
+
+ const { scene } = await buildTestScene(
+ {
+ url: '',
+ slug: '',
+ },
+ 'home-dash '
+ );
+
+ onPanelInspectClose(scene);
+ expect(locationService.getLocation().pathname).toBe('/');
+ });
+
+ test('when on new dashboard page', async () => {
+ locationService.push('/dashboard/new');
+ const { scene } = await buildTestScene(
+ {
+ url: '',
+ slug: '',
+ },
+ ''
+ );
+
+ onPanelInspectClose(scene);
+ expect(locationService.getLocation().pathname).toBe('/dashboard/new');
+ });
+
+ test('when on a dashboard page', async () => {
+ const { scene } = await buildTestScene(
+ {
+ slug: 'dash-slug',
+ url: '/d/dash-uid/dash-slug',
+ },
+ 'dash-uid'
+ );
+
+ onPanelInspectClose(scene);
+ expect(locationService.getLocation().pathname).toBe('/d/dash-uid/dash-slug');
+ });
+});
+
+async function buildTestScene(metaOverride?: DashboardSceneState['meta'], uid = 'dash-1') {
+ const scene = new DashboardScene({
+ title: 'hello',
+ uid,
+ meta: {
+ canEdit: true,
+ ...metaOverride,
+ },
+ body: DefaultGridLayoutManager.fromVizPanels([]),
+ });
+
+ return { scene };
+}
diff --git a/public/app/features/dashboard-scene/inspect/PanelInspectDrawer.tsx b/public/app/features/dashboard-scene/inspect/PanelInspectDrawer.tsx
index 2b75bec161f..1694d89070f 100644
--- a/public/app/features/dashboard-scene/inspect/PanelInspectDrawer.tsx
+++ b/public/app/features/dashboard-scene/inspect/PanelInspectDrawer.tsx
@@ -15,6 +15,7 @@ import { getDataSourceWithInspector } from 'app/features/dashboard/components/In
import { supportsDataQuery } from 'app/features/dashboard/components/PanelEditor/utils';
import { InspectTab } from 'app/features/inspector/types';
+import { DashboardScene } from '../scene/DashboardScene';
import { getDashboardUrl } from '../utils/getDashboardUrl';
import { getDashboardSceneFor } from '../utils/utils';
@@ -91,21 +92,7 @@ export class PanelInspectDrawer extends SceneObjectBase
}
onClose = () => {
- const dashboard = getDashboardSceneFor(this);
- const meta = dashboard.state.meta;
-
- locationService.push(
- getDashboardUrl({
- uid: dashboard.state.uid,
- slug: dashboard.state.meta.slug,
- currentQueryParams: locationService.getLocation().search,
- updateQuery: {
- inspect: null,
- inspectTab: null,
- },
- isHomeDashboard: !meta.url && !meta.slug && !meta.isNew,
- })
- );
+ onPanelInspectClose(getDashboardSceneFor(this));
};
}
@@ -156,3 +143,23 @@ function PanelInspectRenderer({ model }: SceneComponentProps
);
}
+
+export function onPanelInspectClose(dashboard: DashboardScene) {
+ const meta = dashboard.state.meta;
+ // Checking for location here as well, otherwise down below isHomeDashboard will be set to true
+ // as it doesn't have uid neither slug nor url.
+ const isNew = !dashboard.state.uid && locationService.getLocation().pathname === '/dashboard/new';
+
+ locationService.push(
+ getDashboardUrl({
+ uid: dashboard.state.uid,
+ slug: dashboard.state.meta.slug,
+ currentQueryParams: locationService.getLocation().search,
+ updateQuery: {
+ inspect: null,
+ inspectTab: null,
+ },
+ isHomeDashboard: !meta.url && !meta.slug && !isNew,
+ })
+ );
+}
diff --git a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts
index 74c025ebb02..38bf7a0fcb5 100644
--- a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts
+++ b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts
@@ -133,13 +133,12 @@ describe('DashboardScenePageStateManager v1', () => {
});
describe('New dashboards', () => {
- it('Should have new empty model with meta.isNew and should not be cached', async () => {
+ it('Should have new empty model and should not be cached', async () => {
const loader = new DashboardScenePageStateManager({});
await loader.loadDashboard({ uid: '', route: DashboardRoutes.New });
const dashboard = loader.state.dashboard!;
- expect(dashboard.state.meta.isNew).toBe(true);
expect(dashboard.state.isEditing).toBe(undefined);
expect(dashboard.state.isDirty).toBe(false);
@@ -431,13 +430,12 @@ describe('DashboardScenePageStateManager v2', () => {
});
describe('New dashboards', () => {
- it('Should have new empty model with meta.isNew and should not be cached', async () => {
+ it('Should have new empty model and should not be cached', async () => {
const loader = new DashboardScenePageStateManagerV2({});
await loader.loadDashboard({ uid: '', route: DashboardRoutes.New });
const dashboard = loader.state.dashboard!;
- expect(dashboard.state.meta.isNew).toBe(true);
expect(dashboard.state.isEditing).toBe(undefined);
expect(dashboard.state.isDirty).toBe(false);
diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.test.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.test.tsx
index 40aa3cd0926..1d9e2764ce8 100644
--- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.test.tsx
+++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.test.tsx
@@ -237,7 +237,7 @@ describe('PanelAlertTabContent', () => {
}),
];
- renderAlertTab(dashboard);
+ renderAlertTab(dashboard, dashboard);
const defaults = await clickNewButton();
@@ -264,7 +264,7 @@ describe('PanelAlertTabContent', () => {
}),
];
- renderAlertTab(dashboard);
+ renderAlertTab(dashboard, dashboard);
const defaults = await clickNewButton();
expect(defaults.queries[0].model).toEqual({
@@ -290,7 +290,7 @@ describe('PanelAlertTabContent', () => {
}),
];
- renderAlertTab(dashboard);
+ renderAlertTab(dashboard, dashboard);
const defaults = await clickNewButton();
expect(defaults.queries[0].model).toEqual({
@@ -310,7 +310,7 @@ describe('PanelAlertTabContent', () => {
it('Will render alerts belonging to panel and a button to create alert from panel queries', async () => {
dashboard.panels = [panel];
- renderAlertTab(dashboard);
+ renderAlertTab(dashboard, dashboard);
const rows = await ui.row.findAll();
expect(rows).toHaveLength(2);
@@ -334,8 +334,8 @@ describe('PanelAlertTabContent', () => {
});
});
-function renderAlertTab(dashboard: DashboardModel) {
- const model = createModel(dashboard);
+function renderAlertTab(dashboard: DashboardModel, dto: DashboardDataDTO) {
+ const model = createModel(dashboard, dto);
renderAlertTabContent(model);
}
@@ -353,8 +353,8 @@ async function clickNewButton() {
return defaults;
}
-function createModel(dashboard: DashboardModel) {
- const scene = createDashboardSceneFromDashboardModel(dashboard, {} as DashboardDataDTO);
+function createModel(dashboard: DashboardModel, dto: DashboardDataDTO) {
+ const scene = createDashboardSceneFromDashboardModel(dashboard, dto);
const vizPanel = findVizPanelByKey(scene, getVizPanelKeyForPanelId(34))!;
const model = new PanelDataAlertingTab({ panelRef: vizPanel.getRef() });
jest.spyOn(utils, 'getDashboardSceneFor').mockReturnValue(scene);
diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.tsx
index a4fbbc4471d..5ccbd2af8fe 100644
--- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.tsx
+++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.tsx
@@ -4,6 +4,7 @@ import { GrafanaTheme2 } from '@grafana/data';
import { SceneComponentProps, SceneObjectBase, SceneObjectRef, SceneObjectState, VizPanel } from '@grafana/scenes';
import { Alert, LoadingPlaceholder, Tab, useStyles2 } from '@grafana/ui';
import { contextSrv } from 'app/core/core';
+import { Trans } from 'app/core/internationalization';
import { RulesTable } from 'app/features/alerting/unified/components/rules/RulesTable';
import { usePanelCombinedRules } from 'app/features/alerting/unified/hooks/usePanelCombinedRules';
import { getRulesPermissions } from 'app/features/alerting/unified/utils/access-control';
@@ -86,10 +87,28 @@ export function PanelDataAlertingTabRendered({ model }: SceneComponentProps
-
There are no alert rules linked to this panel.
- {canCreateRules && }
+ {!isNew && (
+ <>
+
+
+ There are no alert rules linked to this panel.
+
+