diff --git a/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx index 8acd7763542..98cd9aa1993 100644 --- a/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx +++ b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx @@ -1,4 +1,4 @@ -import { SceneGridLayout, SceneTimeRange } from '@grafana/scenes'; +import { SceneGridLayout, SceneGridRow, SceneTimeRange } from '@grafana/scenes'; import { LibraryPanel } from '@grafana/schema/dist/esm/index.gen'; import { activateFullSceneTree } from '../utils/test-utils'; @@ -46,6 +46,110 @@ describe('AddLibraryPanelWidget', () => { 'Trying to add a library panel in a layout that is not SceneGridLayout' ); }); + + it('should replace grid item when grid item state is passed', async () => { + const libPanel = new LibraryVizPanel({ + title: 'Panel Title', + uid: 'uid', + name: 'name', + panelKey: 'panel-1', + }); + + let gridItem = new DashboardGridItem({ + body: libPanel, + key: 'grid-item-1', + }); + addLibPanelDrawer = new AddLibraryPanelDrawer({ panelToReplaceRef: libPanel.getRef() }); + dashboard = new DashboardScene({ + $timeRange: new SceneTimeRange({}), + title: 'hello', + uid: 'dash-1', + version: 4, + meta: { + canEdit: true, + }, + body: new SceneGridLayout({ + children: [gridItem], + }), + overlay: addLibPanelDrawer, + }); + + const panelInfo: LibraryPanel = { + uid: 'new_uid', + model: { + type: 'timeseries', + }, + name: 'new_name', + version: 1, + type: 'timeseries', + }; + + addLibPanelDrawer.onAddLibraryPanel(panelInfo); + + const layout = dashboard.state.body as SceneGridLayout; + gridItem = layout.state.children[0] as DashboardGridItem; + + expect(layout.state.children.length).toBe(1); + expect(gridItem.state.body!).toBeInstanceOf(LibraryVizPanel); + expect(gridItem.state.key).toBe('grid-item-1'); + expect((gridItem.state.body! as LibraryVizPanel).state.uid).toBe('new_uid'); + expect((gridItem.state.body! as LibraryVizPanel).state.name).toBe('new_name'); + }); + + it('should replace grid item in row when grid item state is passed', async () => { + const libPanel = new LibraryVizPanel({ + title: 'Panel Title', + uid: 'uid', + name: 'name', + panelKey: 'panel-1', + }); + + let gridItem = new DashboardGridItem({ + body: libPanel, + key: 'grid-item-1', + }); + addLibPanelDrawer = new AddLibraryPanelDrawer({ panelToReplaceRef: libPanel.getRef() }); + dashboard = new DashboardScene({ + $timeRange: new SceneTimeRange({}), + title: 'hello', + uid: 'dash-1', + version: 4, + meta: { + canEdit: true, + }, + body: new SceneGridLayout({ + children: [ + new SceneGridRow({ + children: [gridItem], + }), + ], + }), + overlay: addLibPanelDrawer, + }); + + const panelInfo: LibraryPanel = { + uid: 'new_uid', + model: { + type: 'timeseries', + }, + name: 'new_name', + version: 1, + type: 'timeseries', + }; + + addLibPanelDrawer.onAddLibraryPanel(panelInfo); + + const layout = dashboard.state.body as SceneGridLayout; + const gridRow = layout.state.children[0] as SceneGridRow; + gridItem = gridRow.state.children[0] as DashboardGridItem; + + expect(layout.state.children.length).toBe(1); + expect(gridRow.state.children.length).toBe(1); + expect(gridItem.state.body!).toBeInstanceOf(LibraryVizPanel); + expect(gridItem.state.key).toBe('grid-item-1'); + expect((gridItem.state.body! as LibraryVizPanel).state.uid).toBe('new_uid'); + expect((gridItem.state.body! as LibraryVizPanel).state.name).toBe('new_name'); + }); }); async function buildTestScene() { diff --git a/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx index 831c0003506..60b0240694d 100644 --- a/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx +++ b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx @@ -1,6 +1,12 @@ import React from 'react'; -import { SceneComponentProps, SceneGridLayout, SceneObjectBase, SceneObjectState } from '@grafana/scenes'; +import { + SceneComponentProps, + SceneGridLayout, + SceneObjectBase, + SceneObjectRef, + SceneObjectState, +} from '@grafana/scenes'; import { LibraryPanel } from '@grafana/schema'; import { Drawer } from '@grafana/ui'; import { t } from 'app/core/internationalization'; @@ -15,7 +21,9 @@ import { NEW_PANEL_HEIGHT, NEW_PANEL_WIDTH, getDashboardSceneFor, getVizPanelKey import { DashboardGridItem } from './DashboardGridItem'; import { LibraryVizPanel } from './LibraryVizPanel'; -export interface AddLibraryPanelDrawerState extends SceneObjectState {} +export interface AddLibraryPanelDrawerState extends SceneObjectState { + panelToReplaceRef?: SceneObjectRef; +} export class AddLibraryPanelDrawer extends SceneObjectBase { public onClose = () => { @@ -39,16 +47,28 @@ export class AddLibraryPanelDrawer extends SceneObjectBase { locationService.partial({ editview: 'settings' }); }; - public onShowAddLibraryPanelDrawer() { + public onShowAddLibraryPanelDrawer(panelToReplaceRef?: SceneObjectRef) { this.setState({ - overlay: new AddLibraryPanelDrawer({}), + overlay: new AddLibraryPanelDrawer({ panelToReplaceRef }), }); } diff --git a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx index 0aecf323e73..d33ed831670 100644 --- a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx +++ b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx @@ -123,6 +123,14 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { ); }, }); + + moreSubMenu.push({ + text: t('panel.header-menu.replace-library-panel', `Replace library panel`), + onClick: () => { + DashboardInteractions.panelMenuItemClicked('replaceLibraryPanel'); + dashboard.onShowAddLibraryPanelDrawer(parent.getRef()); + }, + }); } else { moreSubMenu.push({ text: t('panel.header-menu.create-library-panel', `Create library panel`), diff --git a/public/app/features/dashboard-scene/utils/interactions.ts b/public/app/features/dashboard-scene/utils/interactions.ts index 4f45bc7b457..85c4b332f82 100644 --- a/public/app/features/dashboard-scene/utils/interactions.ts +++ b/public/app/features/dashboard-scene/utils/interactions.ts @@ -20,6 +20,7 @@ export const DashboardInteractions = { | 'share' | 'createLibraryPanel' | 'unlinkLibraryPanel' + | 'replaceLibraryPanel' | 'duplicate' | 'copy' | 'remove' diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 6f0fec9ee34..802bb676d3e 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -1270,6 +1270,7 @@ "new-alert-rule": "New alert rule", "query": "Query", "remove": "Remove", + "replace-library-panel": "Replace library panel", "share": "Share", "show-legend": "Show legend", "unlink-library-panel": "Unlink library panel", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index b796157a532..855683388bd 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -1270,6 +1270,7 @@ "new-alert-rule": "Ńęŵ äľęřŧ řūľę", "query": "Qūęřy", "remove": "Ŗęmővę", + "replace-library-panel": "Ŗępľäčę ľįþřäřy päʼnęľ", "share": "Ŝĥäřę", "show-legend": "Ŝĥőŵ ľęģęʼnđ", "unlink-library-panel": "Ůʼnľįʼnĸ ľįþřäřy päʼnęľ",