From 83bbdbf8b6ea8a884cf25065cdb583efd35c1c44 Mon Sep 17 00:00:00 2001 From: Ivan Ortega Alba Date: Thu, 23 Jan 2025 12:57:45 +0100 Subject: [PATCH] LibraryPanel: Use id and title from panel model (#99281) --- .../dashboard/v2alpha0/dashboard.schema.cue | 14 +++++++++++- .../src/schema/dashboard/v2alpha0/examples.ts | 12 ++++++---- .../schema/dashboard/v2alpha0/types.gen.ts | 21 ++++++++++++++++-- .../transformSaveModelSchemaV2ToScene.test.ts | 6 ++--- .../transformSaveModelSchemaV2ToScene.ts | 13 +++++++---- .../transformSceneToSaveModelSchemaV2.ts | 20 ++++++++--------- .../dashboard-scene/v2schema/test-helpers.ts | 7 +++--- .../api/ResponseTransformers.test.ts | 18 ++++++++++----- .../dashboard/api/ResponseTransformers.ts | 22 +++++++++---------- 9 files changed, 90 insertions(+), 43 deletions(-) diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue b/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue index d153d6beeb0..4b52f6757b4 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue @@ -64,9 +64,21 @@ LibraryPanelKind: { } LibraryPanelSpec: { + // Panel ID for the library panel in the dashboard + id: number + // Title for the library panel in the dashboard + title: string + + libraryPanel: LibraryPanelRef +} + +// A library panel is a reusable panel that you can use in any dashboard. +// When you make a change to a library panel, that change propagates to all instances of where the panel is used. +// Library panels streamline reuse of panels across multiple dashboards. +LibraryPanelRef: { // Library panel name name: string - // Library panel UID + // Library panel uid uid: string } diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts index c031ed9e6ef..b96fbd6f362 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts @@ -181,11 +181,15 @@ export const handyTestingSchema: DashboardV2Spec = { }, }, }, - 'library-panel-1': { + 'panel-2': { kind: 'LibraryPanel', spec: { - uid: 'library-panel-1', - name: 'Library Panel', + id: 2, + title: 'Test Library Panel', + libraryPanel: { + uid: 'uid-for-library-panel', + name: 'Library Panel', + }, }, }, }, @@ -216,7 +220,7 @@ export const handyTestingSchema: DashboardV2Spec = { spec: { element: { kind: 'ElementReference', - name: 'library-panel-1', + name: 'panel-2', }, height: 100, width: 200, diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha0/types.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha0/types.gen.ts index 8d960e9affb..bd6dba3279d 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2alpha0/types.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha0/types.gen.ts @@ -70,13 +70,30 @@ export const defaultLibraryPanelKind = (): LibraryPanelKind => ({ }); export interface LibraryPanelSpec { + // Panel ID for the library panel in the dashboard + id: number; + // Title for the library panel in the dashboard + title: string; + libraryPanel: LibraryPanelRef; +} + +export const defaultLibraryPanelSpec = (): LibraryPanelSpec => ({ + id: 0, + title: "", + libraryPanel: defaultLibraryPanelRef(), +}); + +// A library panel is a reusable panel that you can use in any dashboard. +// When you make a change to a library panel, that change propagates to all instances of where the panel is used. +// Library panels streamline reuse of panels across multiple dashboards. +export interface LibraryPanelRef { // Library panel name name: string; - // Library panel UID + // Library panel uid uid: string; } -export const defaultLibraryPanelSpec = (): LibraryPanelSpec => ({ +export const defaultLibraryPanelRef = (): LibraryPanelRef => ({ name: "", uid: "", }); diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts index 9352284b4fc..71f622c7eba 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts @@ -239,14 +239,14 @@ describe('transformSaveModelSchemaV2ToScene', () => { validateVizPanel(vizPanel, dash); // Library Panel - const libraryPanel = getLibraryPanelElement(dash, 'library-panel-1')!; - expect(layout.state.grid.state.children[1].state.key).toBe(`grid-item-${libraryPanel.spec.uid}`); + const libraryPanel = getLibraryPanelElement(dash, 'panel-2')!; + expect(layout.state.grid.state.children[1].state.key).toBe(`grid-item-${libraryPanel.spec.id}`); const libraryGridLayoutItemSpec = dash.layout.spec.items[1].spec; expect(layout.state.grid.state.children[1].state.width).toBe(libraryGridLayoutItemSpec.width); expect(layout.state.grid.state.children[1].state.height).toBe(libraryGridLayoutItemSpec.height); expect(layout.state.grid.state.children[1].state.x).toBe(libraryGridLayoutItemSpec.x); expect(layout.state.grid.state.children[1].state.y).toBe(libraryGridLayoutItemSpec.y); - const vizLibraryPanel = vizPanels.find((p) => p.state.key === 'library-panel-1')!; + const vizLibraryPanel = vizPanels.find((p) => p.state.key === 'panel-2')!; validateVizPanel(vizLibraryPanel, dash); // Transformations diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts index 6c9af20e122..65f333c88e8 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts @@ -259,7 +259,7 @@ function createSceneGridLayoutForItems(dashboard: DashboardV2Spec): SceneGridIte const libraryPanel = buildLibraryPanel(panel); return new DashboardGridItem({ - key: `grid-item-${panel.spec.uid}`, + key: `grid-item-${panel.spec.id}`, x: element.spec.x, y: element.spec.y, width: element.spec.width, @@ -293,12 +293,17 @@ function buildLibraryPanel(panel: LibraryPanelKind): VizPanel { titleItems.push(new PanelNotices()); const vizPanelState: VizPanelState = { - key: panel.spec.uid, + key: getVizPanelKeyForPanelId(panel.spec.id), titleItems, - $behaviors: [new LibraryPanelBehavior({ uid: panel.spec.uid, name: panel.spec.name })], + $behaviors: [ + new LibraryPanelBehavior({ + uid: panel.spec.libraryPanel.uid, + name: panel.spec.libraryPanel.name, + }), + ], extendPanelContext: setDashboardPanelContext, pluginId: LibraryPanelBehavior.LOADING_VIZ_PANEL_PLUGIN_ID, - title: '', + title: panel.spec.title, options: {}, fieldConfig: { defaults: {}, diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts index bee0227e61e..bc5b020c539 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts @@ -248,8 +248,12 @@ function getElements(state: DashboardSceneState) { const elementSpec: LibraryPanelKind = { kind: 'LibraryPanel', spec: { - name: behavior.state.name, - uid: behavior.state.uid, + id: getPanelIdForVizPanel(vizPanel), + title: vizPanel.state.title, + libraryPanel: { + uid: behavior.state.uid, + name: behavior.state.name, + }, }, }; return elementSpec; @@ -432,14 +436,10 @@ function getVizPanelQueryOptions(vizPanel: VizPanel): QueryOptionsSpec { } function createElements(panels: Element[]): Record { - const elements: Record = {}; - - for (const panel of panels) { - const key = panel.kind === 'Panel' ? getVizPanelKeyForPanelId(panel.spec.id) : panel.spec.uid; - elements[key] = panel; - } - - return elements; + return panels.reduce>((elements, panel) => { + elements[getVizPanelKeyForPanelId(panel.spec.id)] = panel; + return elements; + }, {}); } function repeaterToLayoutItems(repeater: DashboardGridItem, isSnapshot = false): GridLayoutItemKind[] { diff --git a/public/app/features/dashboard-scene/v2schema/test-helpers.ts b/public/app/features/dashboard-scene/v2schema/test-helpers.ts index f37f8802405..9c37f1df5d5 100644 --- a/public/app/features/dashboard-scene/v2schema/test-helpers.ts +++ b/public/app/features/dashboard-scene/v2schema/test-helpers.ts @@ -94,9 +94,10 @@ export function validateVizPanel(vizPanel: VizPanel, dash: DashboardV2Spec) { expect(vizPanelLinks.state.rawLinks).toEqual(panel.spec.links); expect(queryRunner.state.dataLayerFilter?.panelId).toBe(panel.spec.id); } else if (panel.kind === 'LibraryPanel') { - expect(getLibraryPanelBehavior(vizPanel)?.state.name).toBe(panel.spec.name); - expect(getLibraryPanelBehavior(vizPanel)?.state.uid).toBe(panel.spec.uid); - + expect(getLibraryPanelBehavior(vizPanel)?.state.name).toBe(panel.spec.libraryPanel.name); + expect(getLibraryPanelBehavior(vizPanel)?.state.uid).toBe(panel.spec.libraryPanel.uid); + expect(getPanelIdForVizPanel(vizPanel)).toBe(panel.spec.id); + expect(vizPanel.state.title).toBe(panel.spec.title); expect(vizPanel.state.pluginId).toBe(LibraryPanelBehavior.LOADING_VIZ_PANEL_PLUGIN_ID); } else { throw new Error('vizPanel is not a valid element kind'); diff --git a/public/app/features/dashboard/api/ResponseTransformers.test.ts b/public/app/features/dashboard/api/ResponseTransformers.test.ts index 8100e0aa5b4..1209f884b47 100644 --- a/public/app/features/dashboard/api/ResponseTransformers.test.ts +++ b/public/app/features/dashboard/api/ResponseTransformers.test.ts @@ -314,6 +314,7 @@ describe('ResponseTransformers', () => { { id: 2, type: 'table', + title: 'Just a shared table', libraryPanel: { uid: 'library-panel-table', name: 'Table Panel as Library Panel', @@ -458,18 +459,22 @@ describe('ResponseTransformers', () => { expect(spec.layout.spec.items[1].spec).toEqual({ element: { kind: 'ElementReference', - name: 'library-panel-table', + name: '2', }, x: 0, y: 8, width: 12, height: 8, }); - expect(spec.elements['library-panel-table']).toEqual({ + expect(spec.elements['2']).toEqual({ kind: 'LibraryPanel', spec: { - uid: 'library-panel-table', - name: 'Table Panel as Library Panel', + libraryPanel: { + uid: 'library-panel-table', + name: 'Table Panel as Library Panel', + }, + id: 2, + title: 'Just a shared table', }, }); @@ -633,7 +638,10 @@ describe('ResponseTransformers', () => { expect(panelV2.kind).toBe('Panel'); validatePanel(dashboard.panels![0], panelV2, dashboardV2.spec.layout, panelKey); // library panel - expect(dashboard.panels![1].libraryPanel).toEqual(dashboardV2.spec.elements['library-panel-1'].spec); + expect(dashboard.panels![1].libraryPanel).toEqual({ + uid: 'uid-for-library-panel', + name: 'Library Panel', + }); }); describe('getPanelQueries', () => { diff --git a/public/app/features/dashboard/api/ResponseTransformers.ts b/public/app/features/dashboard/api/ResponseTransformers.ts index 071004812a5..695157f5c6c 100644 --- a/public/app/features/dashboard/api/ResponseTransformers.ts +++ b/public/app/features/dashboard/api/ResponseTransformers.ts @@ -278,23 +278,23 @@ function getElementsFromPanels(panels: Panel[]): [DashboardV2Spec['elements'], D // iterate over panels for (const p of panels) { - let elementName; + const elementName = p.id!.toString(); // LibraryPanelKind if (p.libraryPanel) { - elementName = p.libraryPanel.uid; - elements[elementName] = { kind: 'LibraryPanel', spec: { - uid: p.libraryPanel.uid, - name: p.libraryPanel.name, + libraryPanel: { + uid: p.libraryPanel.uid, + name: p.libraryPanel.name, + }, + id: p.id!, + title: p.title ?? '', }, }; // PanelKind } else { - elementName = p.id!.toString(); - // FIXME: for now we should skip row panels if (p.type === 'row') { continue; @@ -826,12 +826,12 @@ function getPanelsV1( } else if (p.kind === 'LibraryPanel') { const panel = p.spec; return { - id: 0, //TODO: LibraryPanelSpec.id will be available after https://github.com/grafana/grafana/pull/99281/ is merged - title: panel.name, + id: panel.id, + title: panel.title, gridPos, libraryPanel: { - uid: panel.uid, - name: panel.name, + uid: panel.libraryPanel.uid, + name: panel.libraryPanel.name, }, }; } else {