mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
39057552dc
commit
3b2352f066
@ -2379,6 +2379,9 @@ exports[`better eslint`] = {
|
|||||||
"public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx:5381": [
|
"public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx:5381": [
|
||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
|
[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": [
|
"public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx:5381": [
|
||||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
[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, "Unexpected any. Specify a different type.", "3"],
|
||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "4"]
|
[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": [
|
"public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
[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.", "5"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
|
[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": [
|
"public/app/features/dashboard/utils/getPanelMenu.test.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||||
],
|
],
|
||||||
|
@ -5,6 +5,7 @@ import { GrafanaTheme2, urlUtil } from '@grafana/data';
|
|||||||
import { EmbeddedDashboardProps } from '@grafana/runtime';
|
import { EmbeddedDashboardProps } from '@grafana/runtime';
|
||||||
import { SceneObjectStateChangedEvent, sceneUtils } from '@grafana/scenes';
|
import { SceneObjectStateChangedEvent, sceneUtils } from '@grafana/scenes';
|
||||||
import { Spinner, Alert, useStyles2 } from '@grafana/ui';
|
import { Spinner, Alert, useStyles2 } from '@grafana/ui';
|
||||||
|
import { DashboardRoutes } from 'app/types';
|
||||||
|
|
||||||
import { getDashboardScenePageStateManager } from '../pages/DashboardScenePageStateManager';
|
import { getDashboardScenePageStateManager } from '../pages/DashboardScenePageStateManager';
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
@ -14,7 +15,7 @@ export function EmbeddedDashboard(props: EmbeddedDashboardProps) {
|
|||||||
const { dashboard, loadError } = stateManager.useState();
|
const { dashboard, loadError } = stateManager.useState();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
stateManager.loadDashboard({ uid: props.uid!, isEmbedded: true });
|
stateManager.loadDashboard({ uid: props.uid!, route: DashboardRoutes.Embedded });
|
||||||
return () => {
|
return () => {
|
||||||
stateManager.clearState();
|
stateManager.clearState();
|
||||||
};
|
};
|
||||||
|
@ -5,28 +5,28 @@ import { PageLayoutType } from '@grafana/data';
|
|||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
import PageLoader from 'app/core/components/PageLoader/PageLoader';
|
import PageLoader from 'app/core/components/PageLoader/PageLoader';
|
||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
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 { DashboardRoutes } from 'app/types';
|
||||||
|
|
||||||
import { getDashboardScenePageStateManager } from './DashboardScenePageStateManager';
|
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 stateManager = getDashboardScenePageStateManager();
|
||||||
const { dashboard, isLoading, loadError } = stateManager.useState();
|
const { dashboard, isLoading, loadError } = stateManager.useState();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (route.routeName === DashboardRoutes.Home) {
|
stateManager.loadDashboard({
|
||||||
stateManager.loadDashboard({ uid: route.routeName });
|
uid: match.params.uid ?? '',
|
||||||
} else {
|
route: route.routeName as DashboardRoutes,
|
||||||
stateManager.loadDashboard({ uid: match.params.uid! });
|
urlFolderUid: queryParams.folderUid,
|
||||||
}
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
stateManager.clearState();
|
stateManager.clearState();
|
||||||
};
|
};
|
||||||
}, [stateManager, match.params.uid, route.routeName]);
|
}, [stateManager, match.params.uid, route.routeName, queryParams.folderUid]);
|
||||||
|
|
||||||
if (!dashboard) {
|
if (!dashboard) {
|
||||||
return (
|
return (
|
||||||
|
@ -2,6 +2,7 @@ import { advanceBy } from 'jest-date-mock';
|
|||||||
|
|
||||||
import { locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import { getUrlSyncManager } from '@grafana/scenes';
|
import { getUrlSyncManager } from '@grafana/scenes';
|
||||||
|
import { DashboardRoutes } from 'app/types';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
import { setupLoadDashboardMock } from '../utils/test-utils';
|
import { setupLoadDashboardMock } from '../utils/test-utils';
|
||||||
@ -14,12 +15,12 @@ describe('DashboardScenePageStateManager', () => {
|
|||||||
const loadDashboardMock = setupLoadDashboardMock({ dashboard: { uid: 'fake-dash', editable: true }, meta: {} });
|
const loadDashboardMock = setupLoadDashboardMock({ dashboard: { uid: 'fake-dash', editable: true }, meta: {} });
|
||||||
|
|
||||||
const loader = new DashboardScenePageStateManager({});
|
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');
|
expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash');
|
||||||
|
|
||||||
// should use cache second time
|
// 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);
|
expect(loadDashboardMock.mock.calls.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ describe('DashboardScenePageStateManager', () => {
|
|||||||
setupLoadDashboardMock({ dashboard: undefined, meta: {} });
|
setupLoadDashboardMock({ dashboard: undefined, meta: {} });
|
||||||
|
|
||||||
const loader = new DashboardScenePageStateManager({});
|
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.dashboard).toBeUndefined();
|
||||||
expect(loader.state.isLoading).toBe(false);
|
expect(loader.state.isLoading).toBe(false);
|
||||||
@ -38,7 +39,7 @@ describe('DashboardScenePageStateManager', () => {
|
|||||||
setupLoadDashboardMock({ dashboard: { uid: 'fake-dash' }, meta: {} });
|
setupLoadDashboardMock({ dashboard: { uid: 'fake-dash' }, meta: {} });
|
||||||
|
|
||||||
const loader = new DashboardScenePageStateManager({});
|
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.dashboard?.state.uid).toBe('fake-dash');
|
||||||
expect(loader.state.loadError).toBe(undefined);
|
expect(loader.state.loadError).toBe(undefined);
|
||||||
@ -49,7 +50,7 @@ describe('DashboardScenePageStateManager', () => {
|
|||||||
setupLoadDashboardMock({ dashboard: { uid: 'fake-dash' }, meta: {} });
|
setupLoadDashboardMock({ dashboard: { uid: 'fake-dash' }, meta: {} });
|
||||||
|
|
||||||
const loader = new DashboardScenePageStateManager({});
|
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.dashboard).toBeInstanceOf(DashboardScene);
|
||||||
expect(loader.state.isLoading).toBe(false);
|
expect(loader.state.isLoading).toBe(false);
|
||||||
@ -61,7 +62,7 @@ describe('DashboardScenePageStateManager', () => {
|
|||||||
locationService.partial({ from: 'now-5m', to: 'now' });
|
locationService.partial({ from: 'now-5m', to: 'now' });
|
||||||
|
|
||||||
const loader = new DashboardScenePageStateManager({});
|
const loader = new DashboardScenePageStateManager({});
|
||||||
await loader.loadDashboard({ uid: 'fake-dash' });
|
await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
|
||||||
const dash = loader.state.dashboard;
|
const dash = loader.state.dashboard;
|
||||||
|
|
||||||
expect(dash!.state.$timeRange?.state.from).toEqual('now-5m');
|
expect(dash!.state.$timeRange?.state.from).toEqual('now-5m');
|
||||||
@ -71,7 +72,7 @@ describe('DashboardScenePageStateManager', () => {
|
|||||||
// try loading again (and hitting cache)
|
// try loading again (and hitting cache)
|
||||||
locationService.partial({ from: 'now-10m', to: 'now' });
|
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;
|
const dash2 = loader.state.dashboard;
|
||||||
|
|
||||||
expect(dash2!.state.$timeRange?.state.from).toEqual('now-10m');
|
expect(dash2!.state.$timeRange?.state.from).toEqual('now-10m');
|
||||||
@ -83,12 +84,32 @@ describe('DashboardScenePageStateManager', () => {
|
|||||||
locationService.partial({ from: 'now-5m', to: 'now' });
|
locationService.partial({ from: 'now-5m', to: 'now' });
|
||||||
|
|
||||||
const loader = new DashboardScenePageStateManager({});
|
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;
|
const dash = loader.state.dashboard;
|
||||||
|
|
||||||
expect(dash!.state.$timeRange?.state.from).toEqual('now-6h');
|
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', () => {
|
describe('caching', () => {
|
||||||
it('should cache the dashboard DTO', async () => {
|
it('should cache the dashboard DTO', async () => {
|
||||||
setupLoadDashboardMock({ dashboard: { uid: 'fake-dash' }, meta: {} });
|
setupLoadDashboardMock({ dashboard: { uid: 'fake-dash' }, meta: {} });
|
||||||
@ -97,7 +118,7 @@ describe('DashboardScenePageStateManager', () => {
|
|||||||
|
|
||||||
expect(loader.getFromCache('fake-dash')).toBeNull();
|
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();
|
expect(loader.getFromCache('fake-dash')).toBeDefined();
|
||||||
});
|
});
|
||||||
@ -110,15 +131,15 @@ describe('DashboardScenePageStateManager', () => {
|
|||||||
|
|
||||||
expect(loader.getFromCache('fake-dash')).toBeNull();
|
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);
|
expect(loadDashSpy).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
advanceBy(DASHBOARD_CACHE_TTL / 2);
|
advanceBy(DASHBOARD_CACHE_TTL / 2);
|
||||||
await loader.fetchDashboard({ uid: 'fake-dash' });
|
await loader.fetchDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal });
|
||||||
expect(loadDashSpy).toHaveBeenCalledTimes(1);
|
expect(loadDashSpy).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
advanceBy(DASHBOARD_CACHE_TTL / 2 + 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);
|
expect(loadDashSpy).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,6 +11,7 @@ import { DashboardDTO, DashboardRoutes } from 'app/types';
|
|||||||
|
|
||||||
import { PanelEditor } from '../panel-edit/PanelEditor';
|
import { PanelEditor } from '../panel-edit/PanelEditor';
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
|
import { buildNewDashboardSaveModel } from '../serialization/buildNewDashboardSaveModel';
|
||||||
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
|
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
|
||||||
|
|
||||||
export interface DashboardScenePageState {
|
export interface DashboardScenePageState {
|
||||||
@ -22,6 +23,9 @@ export interface DashboardScenePageState {
|
|||||||
|
|
||||||
export const DASHBOARD_CACHE_TTL = 2000;
|
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 {
|
interface DashboardCacheEntry {
|
||||||
dashboard: DashboardDTO;
|
dashboard: DashboardDTO;
|
||||||
ts: number;
|
ts: number;
|
||||||
@ -29,7 +33,8 @@ interface DashboardCacheEntry {
|
|||||||
|
|
||||||
export interface LoadDashboardOptions {
|
export interface LoadDashboardOptions {
|
||||||
uid: string;
|
uid: string;
|
||||||
isEmbedded?: boolean;
|
route: DashboardRoutes;
|
||||||
|
urlFolderUid?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DashboardScenePageStateManager extends StateManagerBase<DashboardScenePageState> {
|
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.
|
// 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.
|
// For now it's a simplistic version to support Home and Normal dashboard routes.
|
||||||
public async fetchDashboard({ uid, isEmbedded }: LoadDashboardOptions) {
|
public async fetchDashboard({ uid, route, urlFolderUid }: LoadDashboardOptions) {
|
||||||
const cachedDashboard = this.getFromCache(uid);
|
const cacheKey = route === DashboardRoutes.Home ? HOME_DASHBOARD_CACHE_KEY : uid;
|
||||||
|
const cachedDashboard = this.getFromCache(cacheKey);
|
||||||
|
|
||||||
if (cachedDashboard) {
|
if (cachedDashboard) {
|
||||||
return cachedDashboard;
|
return cachedDashboard;
|
||||||
@ -49,27 +55,37 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
|
|||||||
let rsp: DashboardDTO | undefined;
|
let rsp: DashboardDTO | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (uid === DashboardRoutes.Home) {
|
switch (route) {
|
||||||
rsp = await getBackendSrv().get('/api/dashboards/home');
|
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 user specified a custom home dashboard redirect to that
|
||||||
if (rsp?.redirectUri) {
|
if (rsp?.redirectUri) {
|
||||||
const newUrl = locationUtil.stripBaseFromUrl(rsp.redirectUri);
|
const newUrl = locationUtil.stripBaseFromUrl(rsp.redirectUri);
|
||||||
locationService.replace(newUrl);
|
locationService.replace(newUrl);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rsp?.meta) {
|
if (rsp?.meta) {
|
||||||
rsp.meta.canSave = false;
|
rsp.meta.canSave = false;
|
||||||
rsp.meta.canShare = false;
|
rsp.meta.canShare = false;
|
||||||
rsp.meta.canStar = false;
|
rsp.meta.canStar = false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
rsp = await dashboardLoaderSrv.loadDashboard('db', '', uid);
|
break;
|
||||||
|
default:
|
||||||
|
rsp = await dashboardLoaderSrv.loadDashboard('db', '', uid);
|
||||||
|
|
||||||
|
if (route === DashboardRoutes.Embedded) {
|
||||||
|
rsp.meta.isEmbedded = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rsp) {
|
if (rsp) {
|
||||||
if (rsp.meta.url && !isEmbedded) {
|
if (rsp.meta.url && route !== DashboardRoutes.Embedded) {
|
||||||
const dashboardUrl = locationUtil.stripBaseFromUrl(rsp.meta.url);
|
const dashboardUrl = locationUtil.stripBaseFromUrl(rsp.meta.url);
|
||||||
const currentPath = locationService.getLocation().pathname;
|
const currentPath = locationService.getLocation().pathname;
|
||||||
if (dashboardUrl !== currentPath) {
|
if (dashboardUrl !== currentPath) {
|
||||||
@ -85,7 +101,12 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
|
|||||||
// Populate nav model in global store according to the folder
|
// Populate nav model in global store according to the folder
|
||||||
await this.initNavModel(rsp);
|
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) {
|
} catch (e) {
|
||||||
// Ignore cancelled errors
|
// Ignore cancelled errors
|
||||||
@ -103,10 +124,7 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
|
|||||||
public async loadDashboard(options: LoadDashboardOptions) {
|
public async loadDashboard(options: LoadDashboardOptions) {
|
||||||
try {
|
try {
|
||||||
const dashboard = await this.loadScene(options);
|
const dashboard = await this.loadScene(options);
|
||||||
|
dashboard.startUrlSync();
|
||||||
if (!options.isEmbedded) {
|
|
||||||
dashboard.startUrlSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ dashboard: dashboard, isLoading: false });
|
this.setState({ dashboard: dashboard, isLoading: false });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -117,8 +135,6 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
|
|||||||
private async loadScene(options: LoadDashboardOptions): Promise<DashboardScene> {
|
private async loadScene(options: LoadDashboardOptions): Promise<DashboardScene> {
|
||||||
const fromCache = this.cache[options.uid];
|
const fromCache = this.cache[options.uid];
|
||||||
if (fromCache) {
|
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;
|
return fromCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,13 +143,12 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
|
|||||||
const rsp = await this.fetchDashboard(options);
|
const rsp = await this.fetchDashboard(options);
|
||||||
|
|
||||||
if (rsp?.dashboard) {
|
if (rsp?.dashboard) {
|
||||||
if (options.isEmbedded) {
|
|
||||||
rsp.meta.isEmbedded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scene = transformSaveModelToScene(rsp);
|
const scene = transformSaveModelToScene(rsp);
|
||||||
|
|
||||||
this.cache[options.uid] = scene;
|
if (options.uid) {
|
||||||
|
this.cache[options.uid] = scene;
|
||||||
|
}
|
||||||
|
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +153,9 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public startUrlSync() {
|
public startUrlSync() {
|
||||||
getUrlSyncManager().initSync(this);
|
if (!this.state.meta.isEmbedded) {
|
||||||
|
getUrlSyncManager().initSync(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public stopUrlSync() {
|
public stopUrlSync() {
|
||||||
@ -206,6 +208,11 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onDiscard = () => {
|
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
|
// No need to listen to changes anymore
|
||||||
this.stopTrackingChanges();
|
this.stopTrackingChanges();
|
||||||
// Stop url sync before updating url
|
// Stop url sync before updating url
|
||||||
@ -233,6 +240,10 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
|||||||
this.propagateEditModeChange();
|
this.propagateEditModeChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public canDiscard() {
|
||||||
|
return this._initialState !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
public onRestore = async (version: DecoratedRevisionModel): Promise<boolean> => {
|
public onRestore = async (version: DecoratedRevisionModel): Promise<boolean> => {
|
||||||
const versionRsp = await historySrv.restoreDashboard(version.uid, version.version);
|
const versionRsp = await historySrv.restoreDashboard(version.uid, version.version);
|
||||||
|
|
||||||
|
@ -128,33 +128,37 @@ export const NavToolbarActions = React.memo<Props>(({ dashboard }) => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (dashboard.canEditDashboard()) {
|
if (dashboard.canEditDashboard()) {
|
||||||
toolbarActions.push(
|
if (!dashboard.state.meta.isNew) {
|
||||||
<Button
|
toolbarActions.push(
|
||||||
onClick={() => {
|
<Button
|
||||||
dashboard.openSaveDrawer({ saveAsCopy: true });
|
onClick={() => {
|
||||||
}}
|
dashboard.openSaveDrawer({ saveAsCopy: true });
|
||||||
size="sm"
|
}}
|
||||||
tooltip="Save as copy"
|
size="sm"
|
||||||
fill="text"
|
tooltip="Save as copy"
|
||||||
key="save-as"
|
fill="text"
|
||||||
>
|
key="save-as"
|
||||||
Save as
|
>
|
||||||
</Button>
|
Save as
|
||||||
);
|
</Button>
|
||||||
toolbarActions.push(
|
);
|
||||||
<Button
|
}
|
||||||
onClick={() => {
|
if (dashboard.canDiscard()) {
|
||||||
dashboard.onDiscard();
|
toolbarActions.push(
|
||||||
}}
|
<Button
|
||||||
tooltip="Discard changes"
|
onClick={() => {
|
||||||
fill="text"
|
dashboard.onDiscard();
|
||||||
size="sm"
|
}}
|
||||||
key="discard"
|
tooltip="Discard changes"
|
||||||
variant="destructive"
|
fill="text"
|
||||||
>
|
size="sm"
|
||||||
Discard
|
key="discard"
|
||||||
</Button>
|
variant="destructive"
|
||||||
);
|
>
|
||||||
|
Discard
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
toolbarActions.push(
|
toolbarActions.push(
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -48,6 +48,7 @@ import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
|||||||
import { NEW_LINK } from '../settings/links/utils';
|
import { NEW_LINK } from '../settings/links/utils';
|
||||||
import { getQueryRunnerFor } from '../utils/utils';
|
import { getQueryRunnerFor } from '../utils/utils';
|
||||||
|
|
||||||
|
import { buildNewDashboardSaveModel } from './buildNewDashboardSaveModel';
|
||||||
import dashboard_to_load1 from './testfiles/dashboard_to_load1.json';
|
import dashboard_to_load1 from './testfiles/dashboard_to_load1.json';
|
||||||
import repeatingRowsAndPanelsDashboardJson from './testfiles/repeating_rows_and_panels.json';
|
import repeatingRowsAndPanelsDashboardJson from './testfiles/repeating_rows_and_panels.json';
|
||||||
import {
|
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', () => {
|
describe('when organizing panels as scene children', () => {
|
||||||
it('should create panels within collapsed rows', () => {
|
it('should create panels within collapsed rows', () => {
|
||||||
const panel = createPanelSaveModel({
|
const panel = createPanelSaveModel({
|
||||||
|
@ -248,6 +248,8 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
|
|||||||
id: oldModel.id,
|
id: oldModel.id,
|
||||||
description: oldModel.description,
|
description: oldModel.description,
|
||||||
editable: oldModel.editable,
|
editable: oldModel.editable,
|
||||||
|
isDirty: oldModel.meta.isNew,
|
||||||
|
isEditing: oldModel.meta.isNew,
|
||||||
meta: oldModel.meta,
|
meta: oldModel.meta,
|
||||||
version: oldModel.version,
|
version: oldModel.version,
|
||||||
body: new SceneGridLayout({
|
body: new SceneGridLayout({
|
||||||
|
@ -37,4 +37,13 @@ describe('dashboard utils', () => {
|
|||||||
|
|
||||||
expect(url).toBe('/d/dash-1?orgId=1&new=A');
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -27,6 +27,10 @@ export interface DashboardUrlOptions {
|
|||||||
export function getDashboardUrl(options: DashboardUrlOptions) {
|
export function getDashboardUrl(options: DashboardUrlOptions) {
|
||||||
let path = `/d/${options.uid}`;
|
let path = `/d/${options.uid}`;
|
||||||
|
|
||||||
|
if (!options.uid) {
|
||||||
|
path = '/dashboard/new';
|
||||||
|
}
|
||||||
|
|
||||||
if (options.soloRoute) {
|
if (options.soloRoute) {
|
||||||
path = `/d-solo/${options.uid}`;
|
path = `/d-solo/${options.uid}`;
|
||||||
}
|
}
|
||||||
@ -34,6 +38,7 @@ export function getDashboardUrl(options: DashboardUrlOptions) {
|
|||||||
if (options.slug) {
|
if (options.slug) {
|
||||||
path += `/${options.slug}`;
|
path += `/${options.slug}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.subPath) {
|
if (options.subPath) {
|
||||||
path += options.subPath;
|
path += options.subPath;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,10 @@ import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
|||||||
|
|
||||||
import { config, locationService } from '@grafana/runtime';
|
import { config, locationService } from '@grafana/runtime';
|
||||||
import { GrafanaContext } from 'app/core/context/GrafanaContext';
|
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 { configureStore } from 'app/store/configureStore';
|
||||||
import { DashboardDTO, DashboardRoutes } from 'app/types';
|
import { DashboardDTO, DashboardRoutes } from 'app/types';
|
||||||
|
|
||||||
@ -87,7 +90,7 @@ describe('DashboardPageProxy', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('home dashboard', async () => {
|
it('home dashboard', async () => {
|
||||||
getDashboardScenePageStateManager().setDashboardCache(DashboardRoutes.Home, dashMock);
|
getDashboardScenePageStateManager().setDashboardCache(HOME_DASHBOARD_CACHE_KEY, dashMock);
|
||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
||||||
@ -105,8 +108,8 @@ describe('DashboardPageProxy', () => {
|
|||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
|
||||||
match: { params: {}, isExact: true, path: '/', url: '/' },
|
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(() => {
|
beforeEach(() => {
|
||||||
config.featureToggles.dashboardSceneForViewers = true;
|
config.featureToggles.dashboardSceneForViewers = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when user can edit a dashboard ', () => {
|
describe('when user can edit a dashboard ', () => {
|
||||||
it('should not render DashboardScenePage if route is Home', async () => {
|
it('should not render DashboardScenePage if route is Home', async () => {
|
||||||
getDashboardScenePageStateManager().setDashboardCache(DashboardRoutes.Home, dashMockEditable);
|
getDashboardScenePageStateManager().setDashboardCache(HOME_DASHBOARD_CACHE_KEY, dashMockEditable);
|
||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
||||||
@ -152,7 +155,7 @@ describe('DashboardPageProxy', () => {
|
|||||||
|
|
||||||
describe('when user can only view a dashboard ', () => {
|
describe('when user can only view a dashboard ', () => {
|
||||||
it('should render DashboardScenePage if route is Home', async () => {
|
it('should render DashboardScenePage if route is Home', async () => {
|
||||||
getDashboardScenePageStateManager().setDashboardCache(DashboardRoutes.Home, dashMock);
|
getDashboardScenePageStateManager().setDashboardCache(HOME_DASHBOARD_CACHE_KEY, dashMock);
|
||||||
act(() => {
|
act(() => {
|
||||||
setup({
|
setup({
|
||||||
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
|
||||||
|
@ -32,13 +32,10 @@ function DashboardPageProxy(props: DashboardPageProxyProps) {
|
|||||||
// To avoid querying single dashboard multiple times, stateManager.fetchDashboard uses a simple, short-lived cache.
|
// To avoid querying single dashboard multiple times, stateManager.fetchDashboard uses a simple, short-lived cache.
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
const dashboard = useAsync(async () => {
|
const dashboard = useAsync(async () => {
|
||||||
const dashToFetch = props.route.routeName === DashboardRoutes.Home ? props.route.routeName : props.match.params.uid;
|
return stateManager.fetchDashboard({
|
||||||
|
route: props.route.routeName as DashboardRoutes,
|
||||||
if (!dashToFetch) {
|
uid: props.match.params.uid ?? '',
|
||||||
return null;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return stateManager.fetchDashboard({ uid: dashToFetch });
|
|
||||||
}, [props.match.params.uid, props.route.routeName]);
|
}, [props.match.params.uid, props.route.routeName]);
|
||||||
|
|
||||||
if (!config.featureToggles.dashboardSceneForViewers) {
|
if (!config.featureToggles.dashboardSceneForViewers) {
|
||||||
|
@ -9,20 +9,16 @@ import store from 'app/core/store';
|
|||||||
import { dashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';
|
import { dashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';
|
||||||
import { DashboardSrv, getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
import { DashboardSrv, getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||||
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
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 { getFolderByUid } from 'app/features/folders/state/actions';
|
||||||
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
||||||
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
|
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
|
||||||
import { toStateKey } from 'app/features/variables/utils';
|
import { toStateKey } from 'app/features/variables/utils';
|
||||||
import {
|
import { DashboardDTO, DashboardInitPhase, DashboardRoutes, StoreState, ThunkDispatch, ThunkResult } from 'app/types';
|
||||||
DashboardDTO,
|
|
||||||
DashboardInitPhase,
|
|
||||||
DashboardMeta,
|
|
||||||
DashboardRoutes,
|
|
||||||
StoreState,
|
|
||||||
ThunkDispatch,
|
|
||||||
ThunkResult,
|
|
||||||
} from 'app/types';
|
|
||||||
|
|
||||||
import { createDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner';
|
import { createDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner';
|
||||||
import { initVariablesTransaction } from '../../variables/state/actions';
|
import { initVariablesTransaction } from '../../variables/state/actions';
|
||||||
@ -44,7 +40,6 @@ export interface InitDashboardArgs {
|
|||||||
routeName?: string;
|
routeName?: string;
|
||||||
fixUrl: boolean;
|
fixUrl: boolean;
|
||||||
keybindingSrv: KeybindingSrv;
|
keybindingSrv: KeybindingSrv;
|
||||||
dashboardDto?: DashboardDTO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchDashboard(
|
async function fetchDashboard(
|
||||||
@ -63,7 +58,7 @@ async function fetchDashboard(
|
|||||||
switch (args.routeName) {
|
switch (args.routeName) {
|
||||||
case DashboardRoutes.Home: {
|
case DashboardRoutes.Home: {
|
||||||
const stateManager = getDashboardScenePageStateManager();
|
const stateManager = getDashboardScenePageStateManager();
|
||||||
const cachedDashboard = stateManager.getFromCache(DashboardRoutes.Home);
|
const cachedDashboard = stateManager.getFromCache(HOME_DASHBOARD_CACHE_KEY);
|
||||||
|
|
||||||
if (cachedDashboard) {
|
if (cachedDashboard) {
|
||||||
return cachedDashboard;
|
return cachedDashboard;
|
||||||
@ -88,11 +83,6 @@ async function fetchDashboard(
|
|||||||
case DashboardRoutes.Public: {
|
case DashboardRoutes.Public: {
|
||||||
return await dashboardLoaderSrv.loadDashboard('public', args.urlSlug, args.accessToken);
|
return await dashboardLoaderSrv.loadDashboard('public', args.urlSlug, args.accessToken);
|
||||||
}
|
}
|
||||||
case DashboardRoutes.Embedded: {
|
|
||||||
if (args.dashboardDto) {
|
|
||||||
return args.dashboardDto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case DashboardRoutes.Normal: {
|
case DashboardRoutes.Normal: {
|
||||||
const dashDTO: DashboardDTO = await dashboardLoaderSrv.loadDashboard(args.urlType, args.urlSlug, args.urlUid);
|
const dashDTO: DashboardDTO = await dashboardLoaderSrv.loadDashboard(args.urlType, args.urlSlug, args.urlUid);
|
||||||
|
|
||||||
@ -130,7 +120,7 @@ async function fetchDashboard(
|
|||||||
if (args.urlFolderUid) {
|
if (args.urlFolderUid) {
|
||||||
await dispatch(getFolderByUid(args.urlFolderUid));
|
await dispatch(getFolderByUid(args.urlFolderUid));
|
||||||
}
|
}
|
||||||
return getNewDashboardModelData(args.urlFolderUid);
|
return buildNewDashboardSaveModel(args.urlFolderUid);
|
||||||
}
|
}
|
||||||
case DashboardRoutes.Path: {
|
case DashboardRoutes.Path: {
|
||||||
const path = args.urlSlug ?? '';
|
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';
|
const DASHBOARD_FROM_LS_KEY = 'DASHBOARD_FROM_LS_KEY';
|
||||||
|
|
||||||
export function setDashboardToFetchFromLocalStorage(model: DashboardDTO) {
|
export function setDashboardToFetchFromLocalStorage(model: DashboardDTO) {
|
||||||
|
@ -2,10 +2,8 @@ import { DataFrame, ExplorePanelsState } from '@grafana/data';
|
|||||||
import { Dashboard, DataQuery, DataSourceRef } from '@grafana/schema';
|
import { Dashboard, DataQuery, DataSourceRef } from '@grafana/schema';
|
||||||
import { DataTransformerConfig } from '@grafana/schema/dist/esm/raw/dashboard/x/dashboard_types.gen';
|
import { DataTransformerConfig } from '@grafana/schema/dist/esm/raw/dashboard/x/dashboard_types.gen';
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
import {
|
import { setDashboardToFetchFromLocalStorage } from 'app/features/dashboard/state/initDashboard';
|
||||||
getNewDashboardModelData,
|
import { buildNewDashboardSaveModel } from 'app/features/dashboard-scene/serialization/buildNewDashboardSaveModel';
|
||||||
setDashboardToFetchFromLocalStorage,
|
|
||||||
} from 'app/features/dashboard/state/initDashboard';
|
|
||||||
import { DashboardDTO, ExplorePanelData } from 'app/types';
|
import { DashboardDTO, ExplorePanelData } from 'app/types';
|
||||||
|
|
||||||
export enum AddToDashboardError {
|
export enum AddToDashboardError {
|
||||||
@ -87,7 +85,7 @@ export async function setDashboardInLocalStorage(options: AddPanelToDashboardOpt
|
|||||||
throw AddToDashboardError.FETCH_DASHBOARD;
|
throw AddToDashboardError.FETCH_DASHBOARD;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dto = getNewDashboardModelData();
|
dto = buildNewDashboardSaveModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
dto.dashboard.panels = [panel, ...(dto.dashboard.panels ?? [])];
|
dto.dashboard.panels = [panel, ...(dto.dashboard.panels ?? [])];
|
||||||
|
@ -51,6 +51,7 @@ export interface DashboardMeta {
|
|||||||
publicDashboardEnabled?: boolean;
|
publicDashboardEnabled?: boolean;
|
||||||
dashboardNotFound?: boolean;
|
dashboardNotFound?: boolean;
|
||||||
isEmbedded?: boolean;
|
isEmbedded?: boolean;
|
||||||
|
isNew?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AnnotationActions {
|
export interface AnnotationActions {
|
||||||
|
Loading…
Reference in New Issue
Block a user