diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue index 04e9d4f1322..58ae9457718 100644 --- a/kinds/dashboard/dashboard_kind.cue +++ b/kinds/dashboard/dashboard_kind.cue @@ -99,6 +99,9 @@ lineage: schemas: [{ // Snapshot options. They are present only if the dashboard is a snapshot. snapshot?: #Snapshot @grafanamaturity(NeedsExpertReview) + + // When set to true, the dashboard will load all panels in the dashboard when it's loaded. + preload?: bool } @cuetsy(kind="interface") @grafana(TSVeneer="type") /////////////////////////////////////// diff --git a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts index 46e58446be2..784b96494f4 100644 --- a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts +++ b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts @@ -1062,6 +1062,10 @@ export interface Dashboard { * List of dashboard panels */ panels?: Array<(Panel | RowPanel)>; + /** + * When set to true, the dashboard will load all panels in the dashboard when it's loaded. + */ + preload?: boolean; /** * Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". */ diff --git a/pkg/kinds/dashboard/dashboard_spec_gen.go b/pkg/kinds/dashboard/dashboard_spec_gen.go index ab33a177135..889dd767c9b 100644 --- a/pkg/kinds/dashboard/dashboard_spec_gen.go +++ b/pkg/kinds/dashboard/dashboard_spec_gen.go @@ -753,6 +753,9 @@ type Spec struct { // List of dashboard panels Panels []any `json:"panels,omitempty"` + // When set to true, the dashboard will load all panels in the dashboard when it's loaded. + Preload *bool `json:"preload,omitempty"` + // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". Refresh *string `json:"refresh,omitempty"` diff --git a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts index b55f281e4a8..201773bd2f6 100644 --- a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts +++ b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts @@ -322,40 +322,57 @@ export function getDashboardScenePageStateManager(): DashboardScenePageStateMana } function getErrorScene(msg: string) { - return createDashboardSceneFromDashboardModel( - new DashboardModel( - { - ...defaultDashboard, - title: msg, - panels: [ + const dto: DashboardDTO = { + dashboard: { + ...defaultDashboard, + uid: 'error-dash', + title: msg, + annotations: { + list: [ { - fieldConfig: { - defaults: {}, - overrides: [], + builtIn: 1, + datasource: { + type: 'grafana', + uid: '-- Grafana --', }, - gridPos: { - h: 6, - w: 12, - x: 7, - y: 0, - }, - id: 1, - options: { - code: { - language: 'plaintext', - showLineNumbers: false, - showMiniMap: false, - }, - content: `

${msg}

`, - mode: 'html', - }, - title: '', - transparent: true, - type: 'text', + enable: false, + hide: true, + iconColor: 'rgba(0, 211, 255, 1)', + name: 'Annotations & Alerts', + type: 'dashboard', }, ], }, - { canSave: false, canEdit: false } - ) - ); + + panels: [ + { + fieldConfig: { + defaults: {}, + overrides: [], + }, + gridPos: { + h: 6, + w: 12, + x: 7, + y: 0, + }, + id: 1, + options: { + code: { + language: 'plaintext', + showLineNumbers: false, + showMiniMap: false, + }, + content: `

${msg}

`, + mode: 'html', + }, + title: '', + transparent: true, + type: 'text', + }, + ], + }, + meta: { canSave: false, canEdit: false }, + }; + return createDashboardSceneFromDashboardModel(new DashboardModel(dto.dashboard, dto.meta), dto.dashboard); } 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 e5fabb7f09d..1726c669031 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 @@ -28,7 +28,7 @@ import { DataSourceType, GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { configureStore } from 'app/store/configureStore'; -import { AccessControlAction } from 'app/types'; +import { AccessControlAction, DashboardDataDTO } from 'app/types'; import { AlertQuery, PromRulesResponse } from 'app/types/unified-alerting-dto'; import { createDashboardSceneFromDashboardModel } from '../../serialization/transformSaveModelToScene'; @@ -359,7 +359,7 @@ async function clickNewButton() { } function createModel(dashboard: DashboardModel) { - const scene = createDashboardSceneFromDashboardModel(dashboard); + const scene = createDashboardSceneFromDashboardModel(dashboard, {} as DashboardDataDTO); const vizPanel = findVizPanelByKey(scene, getVizPanelKeyForPanelId(34))!; const model = new PanelDataAlertingTab(VizPanelManager.createFor(vizPanel)); jest.spyOn(utils, 'getDashboardSceneFor').mockReturnValue(scene); diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 2b04bec57de..8c84394c5eb 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -78,7 +78,7 @@ import { ScopesScene } from './Scopes/ScopesScene'; import { ViewPanelScene } from './ViewPanelScene'; import { setupKeyboardShortcuts } from './keyboardShortcuts'; -export const PERSISTED_PROPS = ['title', 'description', 'tags', 'editable', 'graphTooltip', 'links', 'meta']; +export const PERSISTED_PROPS = ['title', 'description', 'tags', 'editable', 'graphTooltip', 'links', 'meta', 'preload']; export interface DashboardSceneState extends SceneObjectState { /** The title */ @@ -91,6 +91,8 @@ export interface DashboardSceneState extends SceneObjectState { links: DashboardLink[]; /** Is editable */ editable?: boolean; + /** Allows disabling grid lazy loading */ + preload?: boolean; /** A uid when saved */ uid?: string; /** @deprecated */ diff --git a/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap b/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap index 48a27c18ea2..9b5c0a119ae 100644 --- a/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap +++ b/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap @@ -278,6 +278,7 @@ exports[`transformSceneToSaveModel Given a scene with rows Should transform back "type": "row", }, ], + "preload": false, "refresh": "", "schemaVersion": 39, "tags": [ @@ -545,6 +546,7 @@ exports[`transformSceneToSaveModel Given a simple scene with custom settings Sho "type": "text", }, ], + "preload": false, "refresh": "5m", "schemaVersion": 39, "tags": [ @@ -902,6 +904,7 @@ exports[`transformSceneToSaveModel Given a simple scene with variables Should tr "type": "text", }, ], + "preload": false, "refresh": "", "schemaVersion": 39, "tags": [ diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts index 444676ca936..ea3e0c936f5 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts @@ -111,7 +111,7 @@ describe('transformSaveModelToScene', () => { }; const oldModel = new DashboardModel(dash); - const scene = createDashboardSceneFromDashboardModel(oldModel); + const scene = createDashboardSceneFromDashboardModel(oldModel, dash); const dashboardControls = scene.state.controls!; expect(scene.state.title).toBe('test'); @@ -138,11 +138,13 @@ describe('transformSaveModelToScene', () => { it('should apply cursor sync behavior', () => { const dash = { ...defaultDashboard, + title: 'Test dashboard', + uid: 'test-uid', graphTooltip: DashboardCursorSync.Crosshair, }; const oldModel = new DashboardModel(dash); - const scene = createDashboardSceneFromDashboardModel(oldModel); + const scene = createDashboardSceneFromDashboardModel(oldModel, dash); const cursorSync = scene.state.$behaviors?.find((b) => b instanceof behaviors.CursorSync); expect(cursorSync).toBeInstanceOf(behaviors.CursorSync); @@ -150,8 +152,13 @@ describe('transformSaveModelToScene', () => { }); it('should apply live now timer behavior', () => { - const oldModel = new DashboardModel(defaultDashboard); - const scene = createDashboardSceneFromDashboardModel(oldModel); + const dash = { + ...defaultDashboard, + title: 'Test dashboard', + uid: 'test-uid', + }; + const oldModel = new DashboardModel(dash); + const scene = createDashboardSceneFromDashboardModel(oldModel, dash); const liveNowTimer = scene.state.$behaviors?.find((b) => b instanceof behaviors.LiveNowTimer); expect(liveNowTimer).toBeInstanceOf(behaviors.LiveNowTimer); @@ -172,7 +179,7 @@ describe('transformSaveModelToScene', () => { }; const oldModel = new DashboardModel(dash); - const scene = createDashboardSceneFromDashboardModel(oldModel); + const scene = createDashboardSceneFromDashboardModel(oldModel, dash); expect(scene.state.$variables?.state.variables).toBeDefined(); }); }); @@ -212,12 +219,14 @@ describe('transformSaveModelToScene', () => { const dashboard = { ...defaultDashboard, + title: 'Test dashboard', + uid: 'test-uid', panels: [row], }; const oldModel = new DashboardModel(dashboard); - const scene = createDashboardSceneFromDashboardModel(oldModel); + const scene = createDashboardSceneFromDashboardModel(oldModel, dashboard); const body = scene.state.body as SceneGridLayout; expect(body.state.children).toHaveLength(1); @@ -304,12 +313,14 @@ describe('transformSaveModelToScene', () => { const dashboard = { ...defaultDashboard, + title: 'Test dashboard', + uid: 'test-uid', panels: [panelOutOfRow, libPanelOutOfRow, rowWithPanel, panelInRow, libPanelInRow, emptyRow], }; const oldModel = new DashboardModel(dashboard); - const scene = createDashboardSceneFromDashboardModel(oldModel); + const scene = createDashboardSceneFromDashboardModel(oldModel, dashboard); const body = scene.state.body as SceneGridLayout; expect(body.state.children).toHaveLength(4); diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index 2d76314a1cb..1e11bc51bde 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -30,7 +30,7 @@ import { AdHocFiltersVariable, } from '@grafana/scenes'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; -import { DashboardDTO } from 'app/types'; +import { DashboardDTO, DashboardDataDTO } from 'app/types'; import { AlertStatesDataLayer } from '../scene/AlertStatesDataLayer'; import { DashboardAnnotationsDataLayer } from '../scene/DashboardAnnotationsDataLayer'; @@ -74,7 +74,7 @@ export function transformSaveModelToScene(rsp: DashboardDTO): DashboardScene { // Just to have migrations run const oldModel = new DashboardModel(rsp.dashboard, rsp.meta); - const scene = createDashboardSceneFromDashboardModel(oldModel); + const scene = createDashboardSceneFromDashboardModel(oldModel, rsp.dashboard); // TODO: refactor createDashboardSceneFromDashboardModel to work on Dashboard schema model scene.setInitialSaveModel(rsp.dashboard); @@ -190,7 +190,7 @@ function createRowFromPanelModel(row: PanelModel, content: SceneGridItemLike[]): }); } -export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel) { +export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel, dto: DashboardDataDTO) { let variables: SceneVariableSet | undefined; let annotationLayers: SceneDataLayerProvider[] = []; let alertStatesLayer: AlertStatesDataLayer | undefined; @@ -249,6 +249,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel) const dashboardScene = new DashboardScene({ description: oldModel.description, editable: oldModel.editable, + preload: dto.preload ?? false, id: oldModel.id, isDirty: false, links: oldModel.links || [], @@ -258,7 +259,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel) uid: oldModel.uid, version: oldModel.version, body: new SceneGridLayout({ - isLazy: true, + isLazy: dto.preload ? false : true, children: createSceneObjectsForPanels(oldModel.panels), $behaviors: [trackIfEmpty], }), diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts index e633b7fb0ce..d6bba6aa82f 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts @@ -112,6 +112,7 @@ export function transformSceneToSaveModel(scene: DashboardScene, isSnapshot = fa uid: state.uid, id: state.id, editable: state.editable, + preload: state.preload, time: { from: timeRange.from, to: timeRange.to, diff --git a/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx b/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx index fafe32472c7..73b1d8cdceb 100644 --- a/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx +++ b/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx @@ -12,6 +12,7 @@ import { Label, RadioButtonGroup, Stack, + Switch, TagsInput, TextArea, } from '@grafana/ui'; @@ -161,6 +162,10 @@ export class GeneralSettingsEditView this.getCursorSync()?.setState({ sync: value }); }; + public onPreloadChange = (preload: boolean) => { + this._dashboard.setState({ preload }); + }; + public onDeleteDashboard = () => {}; static Component = ({ model }: SceneComponentProps) => { @@ -271,6 +276,20 @@ export class GeneralSettingsEditView > + + + model.onPreloadChange(e.currentTarget.checked)} + /> + {meta.canDelete && } diff --git a/public/app/features/dashboard/components/HelpWizard/SupportSnapshotService.ts b/public/app/features/dashboard/components/HelpWizard/SupportSnapshotService.ts index 0a1f9b39f2c..e9f68ad556d 100644 --- a/public/app/features/dashboard/components/HelpWizard/SupportSnapshotService.ts +++ b/public/app/features/dashboard/components/HelpWizard/SupportSnapshotService.ts @@ -83,7 +83,7 @@ export class SupportSnapshotService extends StateManagerBase