From 19743a7fef3aa8e0e2fc25e8b1eefb6ca603afc7 Mon Sep 17 00:00:00 2001 From: Oscar Kilhed Date: Tue, 27 Feb 2024 17:56:29 +0100 Subject: [PATCH] Dashboard scenes: Change library panel to set dashboard key to panel instead of library panel. (#83420) * change library panel so that the dashboard key is attached to the panel instead of the library panel * make sure the save model gets the id from the panel and not the library panel * do not reload everything when the library panel is already loaded * Fix merge issue * Clean up --- .../inspect/InspectJsonTab.test.tsx | 2 +- .../scene/DashboardSceneUrlSync.ts | 41 ++++++++++++++++++- .../dashboard-scene/scene/LibraryVizPanel.tsx | 15 +++++-- .../transformSaveModelToScene.ts | 2 +- .../transformSceneToSaveModel.ts | 5 ++- .../features/dashboard-scene/utils/utils.ts | 5 +++ 6 files changed, 61 insertions(+), 9 deletions(-) diff --git a/public/app/features/dashboard-scene/inspect/InspectJsonTab.test.tsx b/public/app/features/dashboard-scene/inspect/InspectJsonTab.test.tsx index f155cb206fa..264e92daab9 100644 --- a/public/app/features/dashboard-scene/inspect/InspectJsonTab.test.tsx +++ b/public/app/features/dashboard-scene/inspect/InspectJsonTab.test.tsx @@ -216,7 +216,7 @@ async function buildTestSceneWithLibraryPanel() { name: 'LibraryPanel A', title: 'LibraryPanel A title', uid: '111', - key: 'panel-22', + panelKey: 'panel-22', model: panel, version: 1, }; diff --git a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts index 5b922de6bc3..f818fd27870 100644 --- a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts +++ b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts @@ -2,15 +2,22 @@ import { Unsubscribable } from 'rxjs'; import { AppEvents } from '@grafana/data'; import { locationService } from '@grafana/runtime'; -import { SceneObjectBase, SceneObjectState, SceneObjectUrlSyncHandler, SceneObjectUrlValues } from '@grafana/scenes'; +import { + SceneObjectBase, + SceneObjectState, + SceneObjectUrlSyncHandler, + SceneObjectUrlValues, + VizPanel, +} from '@grafana/scenes'; import appEvents from 'app/core/app_events'; import { PanelInspectDrawer } from '../inspect/PanelInspectDrawer'; import { buildPanelEditScene } from '../panel-edit/PanelEditor'; import { createDashboardEditViewFor } from '../settings/utils'; -import { findVizPanelByKey, getDashboardSceneFor, isPanelClone } from '../utils/utils'; +import { findVizPanelByKey, getDashboardSceneFor, isLibraryPanelChild, isPanelClone } from '../utils/utils'; import { DashboardScene, DashboardSceneState } from './DashboardScene'; +import { LibraryVizPanel } from './LibraryVizPanel'; import { ViewPanelScene } from './ViewPanelScene'; import { DashboardRepeatsProcessedEvent } from './types'; @@ -71,6 +78,7 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { // Handle view panel state if (typeof values.viewPanel === 'string') { const panel = findVizPanelByKey(this._scene, values.viewPanel); + if (!panel) { // // If we are trying to view a repeat clone that can't be found it might be that the repeats have not been processed yet if (isPanelClone(values.viewPanel)) { @@ -83,6 +91,11 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { return; } + if (isLibraryPanelChild(panel)) { + this._handleLibraryPanel(panel, (p) => this._buildLibraryPanelViewScene(p)); + return; + } + update.viewPanelScene = new ViewPanelScene({ panelRef: panel.getRef() }); } else if (viewPanelScene && values.viewPanel === null) { update.viewPanelScene = undefined; @@ -99,6 +112,11 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { if (!isEditing) { this._scene.onEnterEditMode(); } + if (isLibraryPanelChild(panel)) { + this._handleLibraryPanel(panel, (p) => { + this._scene.setState({ editPanel: buildPanelEditScene(p) }); + }); + } update.editPanel = buildPanelEditScene(panel); } else if (editPanel && values.editPanel === null) { update.editPanel = undefined; @@ -109,6 +127,25 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { } } + private _buildLibraryPanelViewScene(vizPanel: VizPanel) { + this._scene.setState({ viewPanelScene: new ViewPanelScene({ panelRef: vizPanel.getRef() }) }); + } + + private _handleLibraryPanel(vizPanel: VizPanel, cb: (p: VizPanel) => void): void { + if (!(vizPanel.parent instanceof LibraryVizPanel)) { + throw new Error('Panel is not a child of a LibraryVizPanel'); + } + const libraryPanel = vizPanel.parent; + if (libraryPanel.state.isLoaded) { + cb(vizPanel); + } else { + libraryPanel.subscribeToState((n) => { + cb(n.panel!); + }); + libraryPanel.activate(); + } + } + private _handleViewRepeatClone(viewPanel: string) { if (!this._eventSub) { this._eventSub = this._scene.subscribeToEvent(DashboardRepeatsProcessedEvent, () => { diff --git a/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx b/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx index 61beed8fff2..b1c2be5d6fa 100644 --- a/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx +++ b/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx @@ -16,6 +16,8 @@ interface LibraryVizPanelState extends SceneObjectState { uid: string; name: string; panel?: VizPanel; + isLoaded?: boolean; + panelKey: string; _loadedVersion?: number; } @@ -24,7 +26,8 @@ export class LibraryVizPanel extends SceneObjectBase { constructor(state: LibraryVizPanelState) { super({ - panel: state.panel ?? getLoadingPanel(state.title), + panel: state.panel ?? getLoadingPanel(state.title, state.panelKey), + isLoaded: state.isLoaded ?? false, ...state, }); @@ -32,7 +35,9 @@ export class LibraryVizPanel extends SceneObjectBase { } private _onActivate = () => { - this.loadLibraryPanelFromPanelModel(); + if (!this.state.isLoaded) { + this.loadLibraryPanelFromPanelModel(); + } }; private async loadLibraryPanelFromPanelModel() { @@ -49,6 +54,7 @@ export class LibraryVizPanel extends SceneObjectBase { const panel = new VizPanel({ title: this.state.title, + key: this.state.panelKey, options: libPanelModel.options ?? {}, fieldConfig: libPanelModel.fieldConfig, pluginId: libPanelModel.type, @@ -66,7 +72,7 @@ export class LibraryVizPanel extends SceneObjectBase { ], }); - this.setState({ panel, _loadedVersion: libPanel.version }); + this.setState({ panel, _loadedVersion: libPanel.version, isLoaded: true }); } catch (err) { vizPanel.setState({ _pluginLoadError: 'Unable to load library panel: ' + this.state.uid, @@ -75,8 +81,9 @@ export class LibraryVizPanel extends SceneObjectBase { } } -function getLoadingPanel(title: string) { +function getLoadingPanel(title: string, panelKey: string) { return new VizPanel({ + key: panelKey, title, menu: new VizPanelMenu({ $behaviors: [panelMenuBehavior], diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index b1bf8d691cf..096f378a018 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -408,7 +408,7 @@ export function buildGridItemForLibPanel(panel: PanelModel) { title: panel.title, uid: panel.libraryPanel.uid, name: panel.libraryPanel.name, - key: getVizPanelKeyForPanelId(panel.id), + panelKey: getVizPanelKeyForPanelId(panel.id), }); return new SceneGridItem({ diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts index d452f99539e..d0f61b9f2cf 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts @@ -153,8 +153,11 @@ export function gridItemToPanel(gridItem: SceneGridItemLike, isSnapshot = false) w = gridItem.state.width ?? 0; h = gridItem.state.height ?? 0; + if (!gridItem.state.body.state.panel) { + throw new Error('Library panel has no panel'); + } return { - id: getPanelIdForVizPanel(gridItem.state.body), + id: getPanelIdForVizPanel(gridItem.state.body.state.panel), title: gridItem.state.body.state.title, gridPos: { x, y, w, h }, libraryPanel: { diff --git a/public/app/features/dashboard-scene/utils/utils.ts b/public/app/features/dashboard-scene/utils/utils.ts index b34f5103b9e..4ddf171e05c 100644 --- a/public/app/features/dashboard-scene/utils/utils.ts +++ b/public/app/features/dashboard-scene/utils/utils.ts @@ -15,6 +15,7 @@ import { import { initialIntervalVariableModelState } from 'app/features/variables/interval/reducer'; import { DashboardScene } from '../scene/DashboardScene'; +import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks'; import { panelMenuBehavior } from '../scene/PanelMenuBehavior'; @@ -259,3 +260,7 @@ export function getDefaultVizPanel(dashboard: DashboardScene): VizPanel { }), }); } + +export function isLibraryPanelChild(vizPanel: VizPanel) { + return vizPanel.parent instanceof LibraryVizPanel; +}