diff --git a/public/app/features/apiserver/types.ts b/public/app/features/apiserver/types.ts index aecc57b4f73..054aa62d810 100644 --- a/public/app/features/apiserver/types.ts +++ b/public/app/features/apiserver/types.ts @@ -50,6 +50,7 @@ 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 @@ -78,6 +79,7 @@ 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.tsx b/public/app/features/dashboard-scene/inspect/PanelInspectDrawer.tsx index 0a9704b3e49..2b75bec161f 100644 --- a/public/app/features/dashboard-scene/inspect/PanelInspectDrawer.tsx +++ b/public/app/features/dashboard-scene/inspect/PanelInspectDrawer.tsx @@ -93,7 +93,6 @@ export class PanelInspectDrawer extends SceneObjectBase onClose = () => { const dashboard = getDashboardSceneFor(this); const meta = dashboard.state.meta; - const isNew = dashboard.state.uid === ''; locationService.push( getDashboardUrl({ @@ -104,7 +103,7 @@ export class PanelInspectDrawer extends SceneObjectBase inspect: null, inspectTab: null, }, - isHomeDashboard: !meta.url && !meta.slug && !isNew, + isHomeDashboard: !meta.url && !meta.slug && !meta.isNew, }) ); }; diff --git a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts index c5d4f66de89..b1e60f6e0db 100644 --- a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts +++ b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts @@ -136,12 +136,13 @@ describe('DashboardScenePageStateManager v1', () => { }); describe('New dashboards', () => { - it('Should have new empty model and should not be cached', async () => { + it('Should have new empty model with meta.isNew 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); @@ -433,12 +434,13 @@ describe('DashboardScenePageStateManager v2', () => { }); describe('New dashboards', () => { - it('Should have new empty model and should not be cached', async () => { + it('Should have new empty model with meta.isNew 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 1d9e2764ce8..40aa3cd0926 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, dashboard); + renderAlertTab(dashboard); const defaults = await clickNewButton(); @@ -264,7 +264,7 @@ describe('PanelAlertTabContent', () => { }), ]; - renderAlertTab(dashboard, dashboard); + renderAlertTab(dashboard); const defaults = await clickNewButton(); expect(defaults.queries[0].model).toEqual({ @@ -290,7 +290,7 @@ describe('PanelAlertTabContent', () => { }), ]; - renderAlertTab(dashboard, dashboard); + renderAlertTab(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, dashboard); + renderAlertTab(dashboard); const rows = await ui.row.findAll(); expect(rows).toHaveLength(2); @@ -334,8 +334,8 @@ describe('PanelAlertTabContent', () => { }); }); -function renderAlertTab(dashboard: DashboardModel, dto: DashboardDataDTO) { - const model = createModel(dashboard, dto); +function renderAlertTab(dashboard: DashboardModel) { + const model = createModel(dashboard); renderAlertTabContent(model); } @@ -353,8 +353,8 @@ async function clickNewButton() { return defaults; } -function createModel(dashboard: DashboardModel, dto: DashboardDataDTO) { - const scene = createDashboardSceneFromDashboardModel(dashboard, dto); +function createModel(dashboard: DashboardModel) { + const scene = createDashboardSceneFromDashboardModel(dashboard, {} as DashboardDataDTO); 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/scene/DashboardScene.test.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx index 2c54e174bea..c0ac4ea98c7 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx @@ -110,6 +110,27 @@ describe('DashboardScene', () => { }); }); + describe('Given new dashboard in edit mode', () => { + it('when saving it should clear isNew state', () => { + const scene = buildTestScene({ + meta: { isNew: true }, + }); + + scene.activate(); + scene.onEnterEditMode(); + scene.saveCompleted({} as Dashboard, { + id: 1, + slug: 'slug', + uid: 'dash-1', + url: 'sss', + version: 2, + status: 'aaa', + }); + + expect(scene.state.meta.isNew).toBeFalsy(); + }); + }); + describe('Given scene in edit mode', () => { let scene: DashboardScene; let deactivateScene: () => void; diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 2b38dcf2260..d2efb1279b9 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -112,7 +112,7 @@ export interface DashboardSceneState extends SceneObjectState { /** True when user made a change */ isDirty?: boolean; /** meta flags */ - meta: Omit; + meta: DashboardMeta; /** Version of the dashboard */ version?: number; /** Panel to inspect */ @@ -200,7 +200,7 @@ export class DashboardScene extends SceneObjectBase { private _activationHandler() { let prevSceneContext = window.__grafanaSceneContext; - const isNew = this.state.uid === ''; + window.__grafanaSceneContext = this; this._initializePanelSearch(); @@ -210,7 +210,7 @@ export class DashboardScene extends SceneObjectBase { this._changeTracker.startTrackingChanges(); } - if (isNew) { + if (this.state.meta.isNew) { this.onEnterEditMode(); this.setState({ isDirty: true }); } @@ -284,6 +284,7 @@ export class DashboardScene extends SceneObjectBase { url: result.url, slug: result.slug, folderUid: folderUid, + isNew: false, version: result.version, }, }); @@ -412,7 +413,6 @@ export class DashboardScene extends SceneObjectBase { public getPageNav(location: H.Location, navIndex: NavIndex) { const { meta, viewPanelScene, editPanel, title, uid } = this.state; - const isNew = uid === ''; if (meta.dashboardNotFound) { return { text: 'Not found' }; @@ -425,7 +425,7 @@ export class DashboardScene extends SceneObjectBase { slug: meta.slug, currentQueryParams: location.search, updateQuery: { viewPanel: null, inspect: null, editview: null, editPanel: null, tab: null, shareView: null }, - isHomeDashboard: !meta.url && !meta.slug && !isNew && !meta.isSnapshot, + isHomeDashboard: !meta.url && !meta.slug && !meta.isNew && !meta.isSnapshot, isSnapshot: meta.isSnapshot, }), }; diff --git a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx index 53ce0af2169..fd6b419538f 100644 --- a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx +++ b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx @@ -68,7 +68,6 @@ export function ToolbarActions({ dashboard }: Props) { const isEditedPanelDirty = usePanelEditDirty(editPanel); const isEditingLibraryPanel = editPanel && isLibraryPanel(editPanel.state.panelRef.resolve()); const isNotFound = Boolean(meta.dashboardNotFound); - const isNew = uid === ''; const hasCopiedPanel = store.exists(LS_PANEL_COPY_KEY); // Means we are not in settings view, fullscreen panel or edit panel const isShowingDashboard = !editview && !isViewingPanel && !isEditingPanel; @@ -468,7 +467,7 @@ export function ToolbarActions({ dashboard }: Props) { toolbarActions.push({ group: 'main-buttons', - condition: isEditing && !isNew && isShowingDashboard, + condition: isEditing && !meta.isNew && isShowingDashboard, render: () => (