DashboardScene: Initial work to support "new" dashboard route and creation logic (#81549)

* DashboardScene: Initial work to get new route to work

* Update

* remove caching of new dashboard

* remove old new dashboard func

* Update

* Update public/app/features/dashboard-scene/scene/DashboardScene.tsx

Co-authored-by: Ivan Ortega Alba <ivanortegaalba@gmail.com>

* Fixing test

* dam messy tests

---------

Co-authored-by: Ivan Ortega Alba <ivanortegaalba@gmail.com>
This commit is contained in:
Torkel Ödegaard 2024-01-31 11:33:46 +01:00 committed by GitHub
parent 39057552dc
commit 3b2352f066
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 218 additions and 143 deletions

View File

@ -2379,6 +2379,9 @@ exports[`better eslint`] = {
"public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
],
"public/app/features/dashboard-scene/pages/DashboardScenePage.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
@ -2753,6 +2756,9 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "4"]
],
"public/app/features/dashboard/containers/DashboardPageProxy.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
@ -2909,9 +2915,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
],
"public/app/features/dashboard/state/initDashboard.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/dashboard/utils/getPanelMenu.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],

View File

@ -5,6 +5,7 @@ import { GrafanaTheme2, urlUtil } from '@grafana/data';
import { EmbeddedDashboardProps } from '@grafana/runtime';
import { SceneObjectStateChangedEvent, sceneUtils } from '@grafana/scenes';
import { Spinner, Alert, useStyles2 } from '@grafana/ui';
import { DashboardRoutes } from 'app/types';
import { getDashboardScenePageStateManager } from '../pages/DashboardScenePageStateManager';
import { DashboardScene } from '../scene/DashboardScene';
@ -14,7 +15,7 @@ export function EmbeddedDashboard(props: EmbeddedDashboardProps) {
const { dashboard, loadError } = stateManager.useState();
useEffect(() => {
stateManager.loadDashboard({ uid: props.uid!, isEmbedded: true });
stateManager.loadDashboard({ uid: props.uid!, route: DashboardRoutes.Embedded });
return () => {
stateManager.clearState();
};

View File

@ -5,28 +5,28 @@ import { PageLayoutType } from '@grafana/data';
import { Page } from 'app/core/components/Page/Page';
import PageLoader from 'app/core/components/PageLoader/PageLoader';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { DashboardPageRouteParams } from 'app/features/dashboard/containers/types';
import { DashboardPageRouteParams, DashboardPageRouteSearchParams } from 'app/features/dashboard/containers/types';
import { DashboardRoutes } from 'app/types';
import { getDashboardScenePageStateManager } from './DashboardScenePageStateManager';
export interface Props extends GrafanaRouteComponentProps<DashboardPageRouteParams> {}
export interface Props extends GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams> {}
export function DashboardScenePage({ match, route }: Props) {
export function DashboardScenePage({ match, route, queryParams }: Props) {
const stateManager = getDashboardScenePageStateManager();
const { dashboard, isLoading, loadError } = stateManager.useState();
useEffect(() => {
if (route.routeName === DashboardRoutes.Home) {
stateManager.loadDashboard({ uid: route.routeName });
} else {
stateManager.loadDashboard({ uid: match.params.uid! });
}
stateManager.loadDashboard({
uid: match.params.uid ?? '',
route: route.routeName as DashboardRoutes,
urlFolderUid: queryParams.folderUid,
});
return () => {
stateManager.clearState();
};
}, [stateManager, match.params.uid, route.routeName]);
}, [stateManager, match.params.uid, route.routeName, queryParams.folderUid]);
if (!dashboard) {
return (

View File

@ -2,6 +2,7 @@ import { advanceBy } from 'jest-date-mock';
import { locationService } from '@grafana/runtime';
import { getUrlSyncManager } from '@grafana/scenes';
import { DashboardRoutes } from 'app/types';
import { DashboardScene } from '../scene/DashboardScene';
import { setupLoadDashboardMock } from '../utils/test-utils';
@ -14,12 +15,12 @@ describe('DashboardScenePageStateManager', () => {
const loadDashboardMock = setupLoadDashboardMock({ dashboard: { uid: 'fake-dash', editable: true }, meta: {} });
const loader = new DashboardScenePageStateManager({});
await loader.loadDashboard({ uid: 'fake-dash' });
await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash');
// should use cache second time
await loader.loadDashboard({ uid: 'fake-dash' });
await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
expect(loadDashboardMock.mock.calls.length).toBe(1);
});
@ -27,7 +28,7 @@ describe('DashboardScenePageStateManager', () => {
setupLoadDashboardMock({ dashboard: undefined, meta: {} });
const loader = new DashboardScenePageStateManager({});
await loader.loadDashboard({ uid: 'fake-dash' });
await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
expect(loader.state.dashboard).toBeUndefined();
expect(loader.state.isLoading).toBe(false);
@ -38,7 +39,7 @@ describe('DashboardScenePageStateManager', () => {
setupLoadDashboardMock({ dashboard: { uid: 'fake-dash' }, meta: {} });
const loader = new DashboardScenePageStateManager({});
await loader.loadDashboard({ uid: 'fake-dash' });
await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
expect(loader.state.dashboard?.state.uid).toBe('fake-dash');
expect(loader.state.loadError).toBe(undefined);
@ -49,7 +50,7 @@ describe('DashboardScenePageStateManager', () => {
setupLoadDashboardMock({ dashboard: { uid: 'fake-dash' }, meta: {} });
const loader = new DashboardScenePageStateManager({});
await loader.loadDashboard({ uid: 'fake-dash' });
await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
expect(loader.state.dashboard).toBeInstanceOf(DashboardScene);
expect(loader.state.isLoading).toBe(false);
@ -61,7 +62,7 @@ describe('DashboardScenePageStateManager', () => {
locationService.partial({ from: 'now-5m', to: 'now' });
const loader = new DashboardScenePageStateManager({});
await loader.loadDashboard({ uid: 'fake-dash' });
await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
const dash = loader.state.dashboard;
expect(dash!.state.$timeRange?.state.from).toEqual('now-5m');
@ -71,7 +72,7 @@ describe('DashboardScenePageStateManager', () => {
// try loading again (and hitting cache)
locationService.partial({ from: 'now-10m', to: 'now' });
await loader.loadDashboard({ uid: 'fake-dash' });
await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
const dash2 = loader.state.dashboard;
expect(dash2!.state.$timeRange?.state.from).toEqual('now-10m');
@ -83,12 +84,32 @@ describe('DashboardScenePageStateManager', () => {
locationService.partial({ from: 'now-5m', to: 'now' });
const loader = new DashboardScenePageStateManager({});
await loader.loadDashboard({ uid: 'fake-dash', isEmbedded: true });
await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Embedded });
const dash = loader.state.dashboard;
expect(dash!.state.$timeRange?.state.from).toEqual('now-6h');
});
describe('New dashboards', () => {
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(true);
expect(dashboard.state.isDirty).toBe(true);
dashboard.setState({ title: 'Changed' });
await loader.loadDashboard({ uid: '', route: DashboardRoutes.New });
const dashboard2 = loader.state.dashboard!;
expect(dashboard2.state.title).toBe('New dashboard');
});
});
describe('caching', () => {
it('should cache the dashboard DTO', async () => {
setupLoadDashboardMock({ dashboard: { uid: 'fake-dash' }, meta: {} });
@ -97,7 +118,7 @@ describe('DashboardScenePageStateManager', () => {
expect(loader.getFromCache('fake-dash')).toBeNull();
await loader.loadDashboard({ uid: 'fake-dash' });
await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
expect(loader.getFromCache('fake-dash')).toBeDefined();
});
@ -110,15 +131,15 @@ describe('DashboardScenePageStateManager', () => {
expect(loader.getFromCache('fake-dash')).toBeNull();
await loader.fetchDashboard({ uid: 'fake-dash' });
await loader.fetchDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
expect(loadDashSpy).toHaveBeenCalledTimes(1);
advanceBy(DASHBOARD_CACHE_TTL / 2);
await loader.fetchDashboard({ uid: 'fake-dash' });
await loader.fetchDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
expect(loadDashSpy).toHaveBeenCalledTimes(1);
advanceBy(DASHBOARD_CACHE_TTL / 2 + 1);
await loader.fetchDashboard({ uid: 'fake-dash' });
await loader.fetchDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
expect(loadDashSpy).toHaveBeenCalledTimes(2);
});
});

View File

@ -11,6 +11,7 @@ import { DashboardDTO, DashboardRoutes } from 'app/types';
import { PanelEditor } from '../panel-edit/PanelEditor';
import { DashboardScene } from '../scene/DashboardScene';
import { buildNewDashboardSaveModel } from '../serialization/buildNewDashboardSaveModel';
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
export interface DashboardScenePageState {
@ -22,6 +23,9 @@ export interface DashboardScenePageState {
export const DASHBOARD_CACHE_TTL = 2000;
/** Only used by cache in loading home in DashboardPageProxy and initDashboard (Old arch), can remove this after old dashboard arch is gone */
export const HOME_DASHBOARD_CACHE_KEY = '__grafana_home_uid__';
interface DashboardCacheEntry {
dashboard: DashboardDTO;
ts: number;
@ -29,7 +33,8 @@ interface DashboardCacheEntry {
export interface LoadDashboardOptions {
uid: string;
isEmbedded?: boolean;
route: DashboardRoutes;
urlFolderUid?: string;
}
export class DashboardScenePageStateManager extends StateManagerBase<DashboardScenePageState> {
@ -39,8 +44,9 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
// To eventualy replace the fetchDashboard function from Dashboard redux state management.
// For now it's a simplistic version to support Home and Normal dashboard routes.
public async fetchDashboard({ uid, isEmbedded }: LoadDashboardOptions) {
const cachedDashboard = this.getFromCache(uid);
public async fetchDashboard({ uid, route, urlFolderUid }: LoadDashboardOptions) {
const cacheKey = route === DashboardRoutes.Home ? HOME_DASHBOARD_CACHE_KEY : uid;
const cachedDashboard = this.getFromCache(cacheKey);
if (cachedDashboard) {
return cachedDashboard;
@ -49,27 +55,37 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
let rsp: DashboardDTO | undefined;
try {
if (uid === DashboardRoutes.Home) {
rsp = await getBackendSrv().get('/api/dashboards/home');
switch (route) {
case DashboardRoutes.New:
rsp = buildNewDashboardSaveModel(urlFolderUid);
break;
case DashboardRoutes.Home:
rsp = await getBackendSrv().get('/api/dashboards/home');
// If user specified a custom home dashboard redirect to that
if (rsp?.redirectUri) {
const newUrl = locationUtil.stripBaseFromUrl(rsp.redirectUri);
locationService.replace(newUrl);
return null;
}
// If user specified a custom home dashboard redirect to that
if (rsp?.redirectUri) {
const newUrl = locationUtil.stripBaseFromUrl(rsp.redirectUri);
locationService.replace(newUrl);
return null;
}
if (rsp?.meta) {
rsp.meta.canSave = false;
rsp.meta.canShare = false;
rsp.meta.canStar = false;
}
} else {
rsp = await dashboardLoaderSrv.loadDashboard('db', '', uid);
if (rsp?.meta) {
rsp.meta.canSave = false;
rsp.meta.canShare = false;
rsp.meta.canStar = false;
}
break;
default:
rsp = await dashboardLoaderSrv.loadDashboard('db', '', uid);
if (route === DashboardRoutes.Embedded) {
rsp.meta.isEmbedded = true;
}
}
if (rsp) {
if (rsp.meta.url && !isEmbedded) {
if (rsp.meta.url && route !== DashboardRoutes.Embedded) {
const dashboardUrl = locationUtil.stripBaseFromUrl(rsp.meta.url);
const currentPath = locationService.getLocation().pathname;
if (dashboardUrl !== currentPath) {
@ -85,7 +101,12 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
// Populate nav model in global store according to the folder
await this.initNavModel(rsp);
this.dashboardCache.set(uid, { dashboard: rsp, ts: Date.now() });
// Do not cache new dashboards
if (uid) {
this.dashboardCache.set(uid, { dashboard: rsp, ts: Date.now() });
} else if (route === DashboardRoutes.Home) {
this.dashboardCache.set(HOME_DASHBOARD_CACHE_KEY, { dashboard: rsp, ts: Date.now() });
}
}
} catch (e) {
// Ignore cancelled errors
@ -103,10 +124,7 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
public async loadDashboard(options: LoadDashboardOptions) {
try {
const dashboard = await this.loadScene(options);
if (!options.isEmbedded) {
dashboard.startUrlSync();
}
dashboard.startUrlSync();
this.setState({ dashboard: dashboard, isLoading: false });
} catch (err) {
@ -117,8 +135,6 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
private async loadScene(options: LoadDashboardOptions): Promise<DashboardScene> {
const fromCache = this.cache[options.uid];
if (fromCache) {
// Need to update this in case we cached an embedded but now opening it standard mode
fromCache.state.meta.isEmbedded = options.isEmbedded;
return fromCache;
}
@ -127,13 +143,12 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
const rsp = await this.fetchDashboard(options);
if (rsp?.dashboard) {
if (options.isEmbedded) {
rsp.meta.isEmbedded = true;
}
const scene = transformSaveModelToScene(rsp);
this.cache[options.uid] = scene;
if (options.uid) {
this.cache[options.uid] = scene;
}
return scene;
}

View File

@ -153,7 +153,9 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
}
public startUrlSync() {
getUrlSyncManager().initSync(this);
if (!this.state.meta.isEmbedded) {
getUrlSyncManager().initSync(this);
}
}
public stopUrlSync() {
@ -206,6 +208,11 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
}
public onDiscard = () => {
if (!this.canDiscard()) {
console.error('Trying to discard back to a state that does not exist, initialState undefined');
return;
}
// No need to listen to changes anymore
this.stopTrackingChanges();
// Stop url sync before updating url
@ -233,6 +240,10 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
this.propagateEditModeChange();
};
public canDiscard() {
return this._initialState !== undefined;
}
public onRestore = async (version: DecoratedRevisionModel): Promise<boolean> => {
const versionRsp = await historySrv.restoreDashboard(version.uid, version.version);

View File

@ -128,33 +128,37 @@ export const NavToolbarActions = React.memo<Props>(({ dashboard }) => {
}
} else {
if (dashboard.canEditDashboard()) {
toolbarActions.push(
<Button
onClick={() => {
dashboard.openSaveDrawer({ saveAsCopy: true });
}}
size="sm"
tooltip="Save as copy"
fill="text"
key="save-as"
>
Save as
</Button>
);
toolbarActions.push(
<Button
onClick={() => {
dashboard.onDiscard();
}}
tooltip="Discard changes"
fill="text"
size="sm"
key="discard"
variant="destructive"
>
Discard
</Button>
);
if (!dashboard.state.meta.isNew) {
toolbarActions.push(
<Button
onClick={() => {
dashboard.openSaveDrawer({ saveAsCopy: true });
}}
size="sm"
tooltip="Save as copy"
fill="text"
key="save-as"
>
Save as
</Button>
);
}
if (dashboard.canDiscard()) {
toolbarActions.push(
<Button
onClick={() => {
dashboard.onDiscard();
}}
tooltip="Discard changes"
fill="text"
size="sm"
key="discard"
variant="destructive"
>
Discard
</Button>
);
}
toolbarActions.push(
<Button
onClick={() => {

View File

@ -0,0 +1,26 @@
import { defaultDashboard } from '@grafana/schema';
import { DashboardDTO } from 'app/types';
export function buildNewDashboardSaveModel(urlFolderUid?: string): DashboardDTO {
const data: DashboardDTO = {
meta: {
canStar: false,
canShare: false,
canDelete: false,
isNew: true,
folderUid: '',
},
dashboard: {
...defaultDashboard,
uid: '',
title: 'New dashboard',
panels: [],
},
};
if (urlFolderUid) {
data.meta.folderUid = urlFolderUid;
}
return data;
}

View File

@ -48,6 +48,7 @@ import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
import { NEW_LINK } from '../settings/links/utils';
import { getQueryRunnerFor } from '../utils/utils';
import { buildNewDashboardSaveModel } from './buildNewDashboardSaveModel';
import dashboard_to_load1 from './testfiles/dashboard_to_load1.json';
import repeatingRowsAndPanelsDashboardJson from './testfiles/repeating_rows_and_panels.json';
import {
@ -167,6 +168,16 @@ describe('transformSaveModelToScene', () => {
});
});
describe('When creating a new dashboard', () => {
it('should initialize the DashboardScene in edit mode and dirty', () => {
const rsp = buildNewDashboardSaveModel();
const scene = transformSaveModelToScene(rsp);
expect(scene.state.isEditing).toBe(true);
expect(scene.state.isDirty).toBe(true);
});
});
describe('when organizing panels as scene children', () => {
it('should create panels within collapsed rows', () => {
const panel = createPanelSaveModel({

View File

@ -248,6 +248,8 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
id: oldModel.id,
description: oldModel.description,
editable: oldModel.editable,
isDirty: oldModel.meta.isNew,
isEditing: oldModel.meta.isNew,
meta: oldModel.meta,
version: oldModel.version,
body: new SceneGridLayout({

View File

@ -37,4 +37,13 @@ describe('dashboard utils', () => {
expect(url).toBe('/d/dash-1?orgId=1&new=A');
});
it('Empty uid should be treated as a new dashboard', () => {
const url = getDashboardUrl({
uid: '',
currentQueryParams: '?orgId=1&filter=A',
});
expect(url).toBe('/dashboard/new?orgId=1&filter=A');
});
});

View File

@ -27,6 +27,10 @@ export interface DashboardUrlOptions {
export function getDashboardUrl(options: DashboardUrlOptions) {
let path = `/d/${options.uid}`;
if (!options.uid) {
path = '/dashboard/new';
}
if (options.soloRoute) {
path = `/d-solo/${options.uid}`;
}
@ -34,6 +38,7 @@ export function getDashboardUrl(options: DashboardUrlOptions) {
if (options.slug) {
path += `/${options.slug}`;
}
if (options.subPath) {
path += options.subPath;
}

View File

@ -7,7 +7,10 @@ import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
import { config, locationService } from '@grafana/runtime';
import { GrafanaContext } from 'app/core/context/GrafanaContext';
import { getDashboardScenePageStateManager } from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager';
import {
HOME_DASHBOARD_CACHE_KEY,
getDashboardScenePageStateManager,
} from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager';
import { configureStore } from 'app/store/configureStore';
import { DashboardDTO, DashboardRoutes } from 'app/types';
@ -87,7 +90,7 @@ describe('DashboardPageProxy', () => {
});
it('home dashboard', async () => {
getDashboardScenePageStateManager().setDashboardCache(DashboardRoutes.Home, dashMock);
getDashboardScenePageStateManager().setDashboardCache(HOME_DASHBOARD_CACHE_KEY, dashMock);
act(() => {
setup({
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
@ -105,8 +108,8 @@ describe('DashboardPageProxy', () => {
act(() => {
setup({
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
match: { params: {}, isExact: true, path: '/', url: '/' },
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
match: { params: { uid: 'abc-def' }, isExact: true, path: '/', url: '/' },
});
});
@ -116,14 +119,14 @@ describe('DashboardPageProxy', () => {
});
});
describe('when dashboardSceneForViewers feature toggle enabled', () => {
describe('when dashboardSceneForViewers feature toggle enabled', () => {
beforeEach(() => {
config.featureToggles.dashboardSceneForViewers = true;
});
describe('when user can edit a dashboard ', () => {
it('should not render DashboardScenePage if route is Home', async () => {
getDashboardScenePageStateManager().setDashboardCache(DashboardRoutes.Home, dashMockEditable);
getDashboardScenePageStateManager().setDashboardCache(HOME_DASHBOARD_CACHE_KEY, dashMockEditable);
act(() => {
setup({
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
@ -152,7 +155,7 @@ describe('DashboardPageProxy', () => {
describe('when user can only view a dashboard ', () => {
it('should render DashboardScenePage if route is Home', async () => {
getDashboardScenePageStateManager().setDashboardCache(DashboardRoutes.Home, dashMock);
getDashboardScenePageStateManager().setDashboardCache(HOME_DASHBOARD_CACHE_KEY, dashMock);
act(() => {
setup({
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },

View File

@ -32,13 +32,10 @@ function DashboardPageProxy(props: DashboardPageProxyProps) {
// To avoid querying single dashboard multiple times, stateManager.fetchDashboard uses a simple, short-lived cache.
// eslint-disable-next-line react-hooks/rules-of-hooks
const dashboard = useAsync(async () => {
const dashToFetch = props.route.routeName === DashboardRoutes.Home ? props.route.routeName : props.match.params.uid;
if (!dashToFetch) {
return null;
}
return stateManager.fetchDashboard({ uid: dashToFetch });
return stateManager.fetchDashboard({
route: props.route.routeName as DashboardRoutes,
uid: props.match.params.uid ?? '',
});
}, [props.match.params.uid, props.route.routeName]);
if (!config.featureToggles.dashboardSceneForViewers) {

View File

@ -9,20 +9,16 @@ import store from 'app/core/store';
import { dashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';
import { DashboardSrv, getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { getDashboardScenePageStateManager } from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager';
import {
HOME_DASHBOARD_CACHE_KEY,
getDashboardScenePageStateManager,
} from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager';
import { buildNewDashboardSaveModel } from 'app/features/dashboard-scene/serialization/buildNewDashboardSaveModel';
import { getFolderByUid } from 'app/features/folders/state/actions';
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
import { toStateKey } from 'app/features/variables/utils';
import {
DashboardDTO,
DashboardInitPhase,
DashboardMeta,
DashboardRoutes,
StoreState,
ThunkDispatch,
ThunkResult,
} from 'app/types';
import { DashboardDTO, DashboardInitPhase, DashboardRoutes, StoreState, ThunkDispatch, ThunkResult } from 'app/types';
import { createDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner';
import { initVariablesTransaction } from '../../variables/state/actions';
@ -44,7 +40,6 @@ export interface InitDashboardArgs {
routeName?: string;
fixUrl: boolean;
keybindingSrv: KeybindingSrv;
dashboardDto?: DashboardDTO;
}
async function fetchDashboard(
@ -63,7 +58,7 @@ async function fetchDashboard(
switch (args.routeName) {
case DashboardRoutes.Home: {
const stateManager = getDashboardScenePageStateManager();
const cachedDashboard = stateManager.getFromCache(DashboardRoutes.Home);
const cachedDashboard = stateManager.getFromCache(HOME_DASHBOARD_CACHE_KEY);
if (cachedDashboard) {
return cachedDashboard;
@ -88,11 +83,6 @@ async function fetchDashboard(
case DashboardRoutes.Public: {
return await dashboardLoaderSrv.loadDashboard('public', args.urlSlug, args.accessToken);
}
case DashboardRoutes.Embedded: {
if (args.dashboardDto) {
return args.dashboardDto;
}
}
case DashboardRoutes.Normal: {
const dashDTO: DashboardDTO = await dashboardLoaderSrv.loadDashboard(args.urlType, args.urlSlug, args.urlUid);
@ -130,7 +120,7 @@ async function fetchDashboard(
if (args.urlFolderUid) {
await dispatch(getFolderByUid(args.urlFolderUid));
}
return getNewDashboardModelData(args.urlFolderUid);
return buildNewDashboardSaveModel(args.urlFolderUid);
}
case DashboardRoutes.Path: {
const path = args.urlSlug ?? '';
@ -300,28 +290,6 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult<void> {
};
}
export function getNewDashboardModelData(urlFolderUid?: string): { dashboard: any; meta: DashboardMeta } {
const data = {
meta: {
canStar: false,
canShare: false,
canDelete: false,
isNew: true,
folderUid: '',
},
dashboard: {
title: 'New dashboard',
panels: [],
},
};
if (urlFolderUid) {
data.meta.folderUid = urlFolderUid;
}
return data;
}
const DASHBOARD_FROM_LS_KEY = 'DASHBOARD_FROM_LS_KEY';
export function setDashboardToFetchFromLocalStorage(model: DashboardDTO) {

View File

@ -2,10 +2,8 @@ import { DataFrame, ExplorePanelsState } from '@grafana/data';
import { Dashboard, DataQuery, DataSourceRef } from '@grafana/schema';
import { DataTransformerConfig } from '@grafana/schema/dist/esm/raw/dashboard/x/dashboard_types.gen';
import { backendSrv } from 'app/core/services/backend_srv';
import {
getNewDashboardModelData,
setDashboardToFetchFromLocalStorage,
} from 'app/features/dashboard/state/initDashboard';
import { setDashboardToFetchFromLocalStorage } from 'app/features/dashboard/state/initDashboard';
import { buildNewDashboardSaveModel } from 'app/features/dashboard-scene/serialization/buildNewDashboardSaveModel';
import { DashboardDTO, ExplorePanelData } from 'app/types';
export enum AddToDashboardError {
@ -87,7 +85,7 @@ export async function setDashboardInLocalStorage(options: AddPanelToDashboardOpt
throw AddToDashboardError.FETCH_DASHBOARD;
}
} else {
dto = getNewDashboardModelData();
dto = buildNewDashboardSaveModel();
}
dto.dashboard.panels = [panel, ...(dto.dashboard.panels ?? [])];

View File

@ -51,6 +51,7 @@ export interface DashboardMeta {
publicDashboardEnabled?: boolean;
dashboardNotFound?: boolean;
isEmbedded?: boolean;
isNew?: boolean;
}
export interface AnnotationActions {