mirror of
https://github.com/grafana/grafana.git
synced 2025-02-14 01:23:32 -06:00
Dashboard: Allow disabling dashboard grid lazy loading (#89280)
* Schema update * Dashboard: Allow opting out from dashboard panels lazy loading * Locale * Lint fix * Snaps fix
This commit is contained in:
parent
d1952bb681
commit
f659bc1f40
@ -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")
|
||||
|
||||
///////////////////////////////////////
|
||||
|
@ -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".
|
||||
*/
|
||||
|
@ -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"`
|
||||
|
||||
|
@ -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: `<br/><br/><center><h1>${msg}</h1></center>`,
|
||||
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: `<br/><br/><center><h1>${msg}</h1></center>`,
|
||||
mode: 'html',
|
||||
},
|
||||
title: '',
|
||||
transparent: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
meta: { canSave: false, canEdit: false },
|
||||
};
|
||||
return createDashboardSceneFromDashboardModel(new DashboardModel(dto.dashboard, dto.meta), dto.dashboard);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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": [
|
||||
|
@ -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);
|
||||
|
@ -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],
|
||||
}),
|
||||
|
@ -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,
|
||||
|
@ -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<GeneralSettingsEditView>) => {
|
||||
@ -271,6 +276,20 @@ export class GeneralSettingsEditView
|
||||
>
|
||||
<RadioButtonGroup onChange={model.onTooltipChange} options={GRAPH_TOOLTIP_OPTIONS} value={graphTooltip} />
|
||||
</Field>
|
||||
|
||||
<Field
|
||||
label={t('dashboard-settings.general.panels-preload-label', 'Preload panels')}
|
||||
description={t(
|
||||
'dashboard-settings.general.panels-preload-description',
|
||||
'When enabled all panels will start loading as soon as the dashboard has been loaded.'
|
||||
)}
|
||||
>
|
||||
<Switch
|
||||
id="preload-panels-dashboards-toggle"
|
||||
value={dashboard.state.preload}
|
||||
onChange={(e) => model.onPreloadChange(e.currentTarget.checked)}
|
||||
/>
|
||||
</Field>
|
||||
</CollapsableSection>
|
||||
|
||||
<Box marginTop={3}>{meta.canDelete && <DeleteDashboardButton dashboard={dashboard} />}</Box>
|
||||
|
@ -83,7 +83,7 @@ export class SupportSnapshotService extends StateManagerBase<SupportSnapshotStat
|
||||
if (!panel.isAngularPlugin()) {
|
||||
try {
|
||||
const oldModel = new DashboardModel(snapshot, { isEmbedded: true });
|
||||
const dash = createDashboardSceneFromDashboardModel(oldModel);
|
||||
const dash = createDashboardSceneFromDashboardModel(oldModel, snapshot);
|
||||
scene = dash.state.body; // skip the wrappers
|
||||
} catch (ex) {
|
||||
console.log('Error creating scene:', ex);
|
||||
|
@ -446,6 +446,8 @@
|
||||
"panel-options-graph-tooltip-description": "Controls tooltip and hover highlight behavior across different panels. Reload the dashboard for changes to take effect",
|
||||
"panel-options-graph-tooltip-label": "Graph tooltip",
|
||||
"panel-options-label": "Panel options",
|
||||
"panels-preload-description": "When enabled all panels will start loading as soon as the dashboard has been loaded.",
|
||||
"panels-preload-label": "Preload panels",
|
||||
"tags-label": "Tags",
|
||||
"title": "General",
|
||||
"title-label": "Title"
|
||||
|
@ -446,6 +446,8 @@
|
||||
"panel-options-graph-tooltip-description": "Cőʼnŧřőľş ŧőőľŧįp äʼnđ ĥővęř ĥįģĥľįģĥŧ þęĥävįőř äčřőşş đįƒƒęřęʼnŧ päʼnęľş. Ŗęľőäđ ŧĥę đäşĥþőäřđ ƒőř čĥäʼnģęş ŧő ŧäĸę ęƒƒęčŧ",
|
||||
"panel-options-graph-tooltip-label": "Ğřäpĥ ŧőőľŧįp",
|
||||
"panel-options-label": "Päʼnęľ őpŧįőʼnş",
|
||||
"panels-preload-description": "Ŵĥęʼn ęʼnäþľęđ äľľ päʼnęľş ŵįľľ şŧäřŧ ľőäđįʼnģ äş şőőʼn äş ŧĥę đäşĥþőäřđ ĥäş þęęʼn ľőäđęđ.",
|
||||
"panels-preload-label": "Přęľőäđ päʼnęľş",
|
||||
"tags-label": "Ŧäģş",
|
||||
"title": "Ğęʼnęřäľ",
|
||||
"title-label": "Ŧįŧľę"
|
||||
|
Loading…
Reference in New Issue
Block a user