From 6881eabedfca6a831a2c8f6d5954fcded08d0bf1 Mon Sep 17 00:00:00 2001 From: "grafana-delivery-bot[bot]" <132647405+grafana-delivery-bot[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:45:46 +0300 Subject: [PATCH] [v11.0.x] DashboardScene: Move add library panel view from grid item to drawer (#86409) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DashboardScene: Move add library panel view from grid item to drawer (#86257) * DashboardScene: Add library panel in drawer * DashboardScene: Move add libray panel view to a drawer instead of a grid item * Update * Update tests (cherry picked from commit 5837def8505dcd9da20c5ba0dad29c5f845d7318) Co-authored-by: Torkel Ödegaard --- .../scene/AddLibraryPanelDrawer.test.tsx | 74 +++++ .../scene/AddLibraryPanelDrawer.tsx | 69 +++++ .../scene/AddLibraryPanelWidget.test.tsx | 253 ------------------ .../scene/AddLibraryPanelWidget.tsx | 163 ----------- .../scene/DashboardGridItem.tsx | 8 +- .../scene/DashboardScene.test.tsx | 15 -- .../dashboard-scene/scene/DashboardScene.tsx | 30 +-- .../scene/NavToolbarActions.tsx | 2 +- .../transformSaveModelToScene.test.ts | 91 ++----- .../transformSaveModelToScene.ts | 41 +-- .../transformSceneToSaveModel.test.ts | 3 - .../transformSceneToSaveModel.ts | 15 -- .../dashboard/dashgrid/DashboardEmpty.tsx | 2 +- 13 files changed, 175 insertions(+), 591 deletions(-) create mode 100644 public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx create mode 100644 public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx delete mode 100644 public/app/features/dashboard-scene/scene/AddLibraryPanelWidget.test.tsx delete mode 100644 public/app/features/dashboard-scene/scene/AddLibraryPanelWidget.tsx diff --git a/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx new file mode 100644 index 00000000000..8acd7763542 --- /dev/null +++ b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx @@ -0,0 +1,74 @@ +import { SceneGridLayout, SceneTimeRange } from '@grafana/scenes'; +import { LibraryPanel } from '@grafana/schema/dist/esm/index.gen'; + +import { activateFullSceneTree } from '../utils/test-utils'; + +import { AddLibraryPanelDrawer } from './AddLibraryPanelDrawer'; +import { DashboardGridItem } from './DashboardGridItem'; +import { DashboardScene } from './DashboardScene'; +import { LibraryVizPanel } from './LibraryVizPanel'; + +describe('AddLibraryPanelWidget', () => { + let dashboard: DashboardScene; + let addLibPanelDrawer: AddLibraryPanelDrawer; + + beforeEach(async () => { + const result = await buildTestScene(); + dashboard = result.dashboard; + addLibPanelDrawer = result.drawer; + }); + + it('should add library panel from menu', () => { + const panelInfo: LibraryPanel = { + uid: 'uid', + model: { + type: 'timeseries', + }, + name: 'name', + version: 1, + type: 'timeseries', + }; + + addLibPanelDrawer.onAddLibraryPanel(panelInfo); + + const layout = dashboard.state.body as SceneGridLayout; + const gridItem = layout.state.children[0] as DashboardGridItem; + + expect(layout.state.children.length).toBe(1); + expect(gridItem.state.body!).toBeInstanceOf(LibraryVizPanel); + expect((gridItem.state.body! as LibraryVizPanel).state.panelKey).toBe('panel-1'); + }); + + it('should throw error if adding lib panel in a layout that is not SceneGridLayout', () => { + dashboard.setState({ body: undefined }); + + expect(() => addLibPanelDrawer.onAddLibraryPanel({} as LibraryPanel)).toThrow( + 'Trying to add a library panel in a layout that is not SceneGridLayout' + ); + }); +}); + +async function buildTestScene() { + const drawer = new AddLibraryPanelDrawer({}); + const dashboard = new DashboardScene({ + $timeRange: new SceneTimeRange({}), + title: 'hello', + uid: 'dash-1', + version: 4, + meta: { + canEdit: true, + }, + body: new SceneGridLayout({ + children: [], + }), + overlay: drawer, + }); + + activateFullSceneTree(dashboard); + + await new Promise((r) => setTimeout(r, 1)); + + dashboard.onEnterEditMode(); + + return { dashboard, drawer }; +} diff --git a/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx new file mode 100644 index 00000000000..831c0003506 --- /dev/null +++ b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx @@ -0,0 +1,69 @@ +import React from 'react'; + +import { SceneComponentProps, SceneGridLayout, SceneObjectBase, SceneObjectState } from '@grafana/scenes'; +import { LibraryPanel } from '@grafana/schema'; +import { Drawer } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; +import { + LibraryPanelsSearch, + LibraryPanelsSearchVariant, +} from 'app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch'; + +import { dashboardSceneGraph } from '../utils/dashboardSceneGraph'; +import { NEW_PANEL_HEIGHT, NEW_PANEL_WIDTH, getDashboardSceneFor, getVizPanelKeyForPanelId } from '../utils/utils'; + +import { DashboardGridItem } from './DashboardGridItem'; +import { LibraryVizPanel } from './LibraryVizPanel'; + +export interface AddLibraryPanelDrawerState extends SceneObjectState {} + +export class AddLibraryPanelDrawer extends SceneObjectBase { + public onClose = () => { + getDashboardSceneFor(this).closeModal(); + }; + + public onAddLibraryPanel = (panelInfo: LibraryPanel) => { + const dashboard = getDashboardSceneFor(this); + const layout = dashboard.state.body; + + if (!(layout instanceof SceneGridLayout)) { + throw new Error('Trying to add a library panel in a layout that is not SceneGridLayout'); + } + + const panelId = dashboardSceneGraph.getNextPanelId(dashboard); + + const body = new LibraryVizPanel({ + title: 'Panel Title', + uid: panelInfo.uid, + name: panelInfo.name, + panelKey: getVizPanelKeyForPanelId(panelId), + }); + + const newGridItem = new DashboardGridItem({ + height: NEW_PANEL_HEIGHT, + width: NEW_PANEL_WIDTH, + x: 0, + y: 0, + body: body, + key: `grid-item-${panelId}`, + }); + + layout.setState({ children: [newGridItem, ...layout.state.children] }); + + this.onClose(); + }; + + static Component = ({ model }: SceneComponentProps) => { + const title = t('library-panel.add-widget.title', 'Add panel from panel library'); + + return ( + + + + ); + }; +} diff --git a/public/app/features/dashboard-scene/scene/AddLibraryPanelWidget.test.tsx b/public/app/features/dashboard-scene/scene/AddLibraryPanelWidget.test.tsx deleted file mode 100644 index 9a4fe24206f..00000000000 --- a/public/app/features/dashboard-scene/scene/AddLibraryPanelWidget.test.tsx +++ /dev/null @@ -1,253 +0,0 @@ -import { SceneGridLayout, SceneGridRow, SceneTimeRange } from '@grafana/scenes'; -import { LibraryPanel } from '@grafana/schema/dist/esm/index.gen'; - -import { DashboardScene } from '../scene/DashboardScene'; -import { activateFullSceneTree } from '../utils/test-utils'; - -import { AddLibraryPanelWidget } from './AddLibraryPanelWidget'; -import { DashboardGridItem } from './DashboardGridItem'; -import { LibraryVizPanel } from './LibraryVizPanel'; - -describe('AddLibraryPanelWidget', () => { - let dashboard: DashboardScene; - let addLibPanelWidget: AddLibraryPanelWidget; - const mockEvent = { - preventDefault: jest.fn(), - } as unknown as React.MouseEvent; - - beforeEach(async () => { - const result = await buildTestScene(); - dashboard = result.dashboard; - addLibPanelWidget = result.addLibPanelWidget; - }); - - it('should return the dashboard', () => { - expect(addLibPanelWidget.getDashboard()).toBe(dashboard); - }); - - it('should cancel adding a lib panel', () => { - addLibPanelWidget.onCancelAddPanel(mockEvent); - - const body = dashboard.state.body as SceneGridLayout; - - expect(body.state.children.length).toBe(0); - }); - - it('should cancel lib panel at correct position', () => { - const anotherLibPanelWidget = new AddLibraryPanelWidget({ key: 'panel-2' }); - const body = dashboard.state.body as SceneGridLayout; - - body.setState({ - children: [ - ...body.state.children, - new DashboardGridItem({ - key: 'griditem-2', - x: 0, - y: 0, - width: 10, - height: 12, - body: anotherLibPanelWidget, - }), - ], - }); - dashboard.setState({ body }); - - anotherLibPanelWidget.onCancelAddPanel(mockEvent); - - const gridItem = body.state.children[0] as DashboardGridItem; - - expect(body.state.children.length).toBe(1); - expect(gridItem.state.body!.state.key).toBe(addLibPanelWidget.state.key); - }); - - it('should cancel lib panel inside a row child', () => { - const anotherLibPanelWidget = new AddLibraryPanelWidget({ key: 'panel-2' }); - dashboard.setState({ - body: new SceneGridLayout({ - children: [ - new SceneGridRow({ - key: 'panel-2', - children: [ - new DashboardGridItem({ - key: 'griditem-2', - x: 0, - y: 0, - width: 10, - height: 12, - body: anotherLibPanelWidget, - }), - ], - }), - ], - }), - }); - - const body = dashboard.state.body as SceneGridLayout; - - anotherLibPanelWidget.onCancelAddPanel(mockEvent); - - const gridRow = body.state.children[0] as SceneGridRow; - - expect(body.state.children.length).toBe(1); - expect(gridRow.state.children.length).toBe(0); - }); - - it('should add library panel from menu', () => { - const panelInfo: LibraryPanel = { - uid: 'uid', - model: { - type: 'timeseries', - }, - name: 'name', - version: 1, - type: 'timeseries', - }; - - const body = dashboard.state.body as SceneGridLayout; - const gridItem = body.state.children[0] as DashboardGridItem; - - expect(gridItem.state.body!).toBeInstanceOf(AddLibraryPanelWidget); - - addLibPanelWidget.onAddLibraryPanel(panelInfo); - - expect(body.state.children.length).toBe(1); - expect(gridItem.state.body!).toBeInstanceOf(LibraryVizPanel); - expect((gridItem.state.body! as LibraryVizPanel).state.panelKey).toBe(addLibPanelWidget.state.key); - }); - - it('should add a lib panel at correct position', () => { - const anotherLibPanelWidget = new AddLibraryPanelWidget({ key: 'panel-2' }); - const body = dashboard.state.body as SceneGridLayout; - - body.setState({ - children: [ - ...body.state.children, - new DashboardGridItem({ - key: 'griditem-2', - x: 0, - y: 0, - width: 10, - height: 12, - body: anotherLibPanelWidget, - }), - ], - }); - dashboard.setState({ body }); - - const panelInfo: LibraryPanel = { - uid: 'uid', - model: { - type: 'timeseries', - }, - name: 'name', - version: 1, - type: 'timeseries', - }; - - anotherLibPanelWidget.onAddLibraryPanel(panelInfo); - - const gridItemOne = body.state.children[0] as DashboardGridItem; - const gridItemTwo = body.state.children[1] as DashboardGridItem; - - expect(body.state.children.length).toBe(2); - expect(gridItemOne.state.body!).toBeInstanceOf(AddLibraryPanelWidget); - expect((gridItemTwo.state.body! as LibraryVizPanel).state.panelKey).toBe(anotherLibPanelWidget.state.key); - }); - - it('should add library panel from menu to a row child', () => { - const anotherLibPanelWidget = new AddLibraryPanelWidget({ key: 'panel-2' }); - dashboard.setState({ - body: new SceneGridLayout({ - children: [ - new SceneGridRow({ - key: 'panel-2', - children: [ - new DashboardGridItem({ - key: 'griditem-2', - x: 0, - y: 0, - width: 10, - height: 12, - body: anotherLibPanelWidget, - }), - ], - }), - ], - }), - }); - - const panelInfo: LibraryPanel = { - uid: 'uid', - model: { - type: 'timeseries', - }, - name: 'name', - version: 1, - type: 'timeseries', - }; - - const body = dashboard.state.body as SceneGridLayout; - - anotherLibPanelWidget.onAddLibraryPanel(panelInfo); - - const gridRow = body.state.children[0] as SceneGridRow; - const gridItem = gridRow.state.children[0] as DashboardGridItem; - - expect(body.state.children.length).toBe(1); - expect(gridItem.state.body!).toBeInstanceOf(LibraryVizPanel); - expect((gridItem.state.body! as LibraryVizPanel).state.panelKey).toBe(anotherLibPanelWidget.state.key); - }); - - it('should throw error if adding lib panel in a layout that is not SceneGridLayout', () => { - dashboard.setState({ - body: undefined, - }); - - expect(() => addLibPanelWidget.onAddLibraryPanel({} as LibraryPanel)).toThrow( - 'Trying to add a library panel in a layout that is not SceneGridLayout' - ); - }); - - it('should throw error if removing the library panel widget in a layout that is not SceneGridLayout', () => { - dashboard.setState({ - body: undefined, - }); - - expect(() => addLibPanelWidget.onCancelAddPanel(mockEvent)).toThrow( - 'Trying to remove the library panel widget in a layout that is not SceneGridLayout' - ); - }); -}); - -async function buildTestScene() { - const addLibPanelWidget = new AddLibraryPanelWidget({ key: 'panel-1' }); - const dashboard = new DashboardScene({ - $timeRange: new SceneTimeRange({}), - title: 'hello', - uid: 'dash-1', - version: 4, - meta: { - canEdit: true, - }, - body: new SceneGridLayout({ - children: [ - new DashboardGridItem({ - key: 'griditem-1', - x: 0, - y: 0, - width: 10, - height: 12, - body: addLibPanelWidget, - }), - ], - }), - }); - - activateFullSceneTree(dashboard); - - await new Promise((r) => setTimeout(r, 1)); - - dashboard.onEnterEditMode(); - - return { dashboard, addLibPanelWidget }; -} diff --git a/public/app/features/dashboard-scene/scene/AddLibraryPanelWidget.tsx b/public/app/features/dashboard-scene/scene/AddLibraryPanelWidget.tsx deleted file mode 100644 index 9d9c7a755c7..00000000000 --- a/public/app/features/dashboard-scene/scene/AddLibraryPanelWidget.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import { css, cx, keyframes } from '@emotion/css'; -import React from 'react'; -import tinycolor from 'tinycolor2'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { SceneComponentProps, SceneGridLayout, SceneGridRow, SceneObjectBase, SceneObjectState } from '@grafana/scenes'; -import { LibraryPanel } from '@grafana/schema'; -import { IconButton, useStyles2 } from '@grafana/ui'; -import { Trans } from 'app/core/internationalization'; -import { - LibraryPanelsSearch, - LibraryPanelsSearchVariant, -} from 'app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch'; - -import { getDashboardSceneFor } from '../utils/utils'; - -import { DashboardGridItem } from './DashboardGridItem'; -import { DashboardScene } from './DashboardScene'; -import { LibraryVizPanel } from './LibraryVizPanel'; - -export interface AddLibraryPanelWidgetState extends SceneObjectState { - key: string; -} - -export class AddLibraryPanelWidget extends SceneObjectBase { - public constructor(state: AddLibraryPanelWidgetState) { - super({ - ...state, - }); - } - - private get _dashboard(): DashboardScene { - return getDashboardSceneFor(this); - } - - public getDashboard(): DashboardScene { - return this._dashboard; - } - - public onCancelAddPanel = (evt: React.MouseEvent) => { - evt.preventDefault(); - - if (!(this._dashboard.state.body instanceof SceneGridLayout)) { - throw new Error('Trying to remove the library panel widget in a layout that is not SceneGridLayout'); - } - - const sceneGridLayout = this._dashboard.state.body; - const children = []; - - for (const child of sceneGridLayout.state.children) { - if (child.state.key !== this.parent?.state.key) { - children.push(child); - } - - if (child instanceof SceneGridRow) { - const rowChildren = []; - - for (const rowChild of child.state.children) { - if (rowChild instanceof DashboardGridItem && rowChild.state.key !== this.parent?.state.key) { - rowChildren.push(rowChild); - } - } - - child.setState({ children: rowChildren }); - } - } - - sceneGridLayout.setState({ children }); - }; - - public onAddLibraryPanel = (panelInfo: LibraryPanel) => { - if (!(this._dashboard.state.body instanceof SceneGridLayout)) { - throw new Error('Trying to add a library panel in a layout that is not SceneGridLayout'); - } - - const body = new LibraryVizPanel({ - title: 'Panel Title', - uid: panelInfo.uid, - name: panelInfo.name, - panelKey: this.state.key, - }); - - if (this.parent instanceof DashboardGridItem) { - this.parent.setState({ body }); - } - }; - - static Component = ({ model }: SceneComponentProps) => { - const dashboard = model.getDashboard(); - const styles = useStyles2(getStyles); - - return ( -
-
-
- - Add panel from panel library - -
- -
- -
-
- ); - }; -} - -const getStyles = (theme: GrafanaTheme2) => { - const pulsate = keyframes({ - '0%': { - boxShadow: `0 0 0 2px ${theme.colors.background.canvas}, 0 0 0px 4px ${theme.colors.primary.main}`, - }, - '50%': { - boxShadow: `0 0 0 2px ${theme.components.dashboard.background}, 0 0 0px 4px ${tinycolor(theme.colors.primary.main) - .darken(20) - .toHexString()}`, - }, - '100%': { - boxShadow: `0 0 0 2px ${theme.components.dashboard.background}, 0 0 0px 4px ${theme.colors.primary.main}`, - }, - }); - - return { - // wrapper is used to make sure box-shadow animation isn't cut off in dashboard page - wrapper: css({ - height: '100%', - paddingTop: `${theme.spacing(0.5)}`, - }), - headerRow: css({ - display: 'flex', - alignItems: 'center', - height: '38px', - flexShrink: 0, - width: '100%', - fontSize: theme.typography.fontSize, - fontWeight: theme.typography.fontWeightMedium, - paddingLeft: `${theme.spacing(1)}`, - transition: 'background-color 0.1s ease-in-out', - cursor: 'move', - - '&:hover': { - background: `${theme.colors.background.secondary}`, - }, - }), - callToAction: css({ - overflow: 'hidden', - outline: '2px dotted transparent', - outlineOffset: '2px', - boxShadow: '0 0 0 2px black, 0 0 0px 4px #1f60c4', - animation: `${pulsate} 2s ease infinite`, - }), - }; -}; diff --git a/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx b/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx index 753a504de5c..a6a0eeea343 100644 --- a/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx @@ -22,13 +22,13 @@ import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants'; import { getMultiVariableValues } from '../utils/utils'; -import { AddLibraryPanelWidget } from './AddLibraryPanelWidget'; +import { AddLibraryPanelDrawer } from './AddLibraryPanelDrawer'; import { LibraryVizPanel } from './LibraryVizPanel'; import { repeatPanelMenuBehavior } from './PanelMenuBehavior'; import { DashboardRepeatsProcessedEvent } from './types'; interface DashboardGridItemState extends SceneGridItemStateLike { - body: VizPanel | LibraryVizPanel | AddLibraryPanelWidget; + body: VizPanel | LibraryVizPanel | AddLibraryPanelDrawer; repeatedPanels?: VizPanel[]; variableName?: string; itemHeight?: number; @@ -89,7 +89,7 @@ export class DashboardGridItem extends SceneObjectBase i } private _performRepeat() { - if (this.state.body instanceof AddLibraryPanelWidget) { + if (this.state.body instanceof AddLibraryPanelDrawer) { return; } if (!this.state.variableName || this._variableDependency.hasDependencyInLoadingState()) { @@ -191,7 +191,7 @@ export class DashboardGridItem extends SceneObjectBase i return ; } - if (body instanceof AddLibraryPanelWidget) { + if (body instanceof AddLibraryPanelDrawer) { return ; } } diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx index c805e815564..14908c5586a 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx @@ -670,21 +670,6 @@ describe('DashboardScene', () => { expect(scene.state.hasCopiedPanel).toBe(false); }); - it('Should create a new add library panel widget', () => { - scene.exitEditMode({ skipConfirm: true }); - expect(scene.state.isEditing).toBe(false); - - scene.onCreateLibPanelWidget(); - - const body = scene.state.body as SceneGridLayout; - const gridItem = body.state.children[0] as DashboardGridItem; - - expect(scene.state.isEditing).toBe(true); - expect(body.state.children.length).toBe(6); - expect(gridItem.state.body!.state.key).toBe('panel-7'); - expect(gridItem.state.y).toBe(0); - }); - it('Should remove a panel', () => { const vizPanel = ((scene.state.body as SceneGridLayout).state.children[0] as DashboardGridItem).state.body; scene.removePanel(vizPanel as VizPanel); diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 1b456b98d8d..f7823513b41 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -57,7 +57,7 @@ import { isPanelClone, } from '../utils/utils'; -import { AddLibraryPanelWidget } from './AddLibraryPanelWidget'; +import { AddLibraryPanelDrawer } from './AddLibraryPanelDrawer'; import { DashboardControls } from './DashboardControls'; import { DashboardGridItem } from './DashboardGridItem'; import { DashboardSceneRenderer } from './DashboardSceneRenderer'; @@ -117,7 +117,6 @@ export interface DashboardSceneState extends SceneObjectState { } export class DashboardScene extends SceneObjectBase { - static listenToChangesInProps = PERSISTED_PROPS; static Component = DashboardSceneRenderer; /** @@ -761,30 +760,9 @@ export class DashboardScene extends SceneObjectBase { locationService.partial({ editview: 'settings' }); }; - public onCreateLibPanelWidget() { - if (!(this.state.body instanceof SceneGridLayout)) { - throw new Error('Trying to add a panel in a layout that is not SceneGridLayout'); - } - - if (!this.state.isEditing) { - this.onEnterEditMode(); - } - - const sceneGridLayout = this.state.body; - - const panelId = dashboardSceneGraph.getNextPanelId(this); - - const newGridItem = new DashboardGridItem({ - height: NEW_PANEL_HEIGHT, - width: NEW_PANEL_WIDTH, - x: 0, - y: 0, - body: new AddLibraryPanelWidget({ key: getVizPanelKeyForPanelId(panelId) }), - key: `grid-item-${panelId}`, - }); - - sceneGridLayout.setState({ - children: [newGridItem, ...sceneGridLayout.state.children], + public onShowAddLibraryPanelDrawer() { + this.setState({ + overlay: new AddLibraryPanelDrawer({}), }); } diff --git a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx index 8f11489cb8d..49de18e6231 100644 --- a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx +++ b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx @@ -176,7 +176,7 @@ export function ToolbarActions({ dashboard }: Props) { testId={selectors.pages.AddDashboard.itemButton('Add new panel from panel library menu item')} label={t('dashboard.add-menu.import', 'Import from library')} onClick={() => { - dashboard.onCreateLibPanelWidget(); + dashboard.onShowAddLibraryPanelDrawer(); DashboardInteractions.toolbarAddButtonClicked({ item: 'add_library_panel' }); }} /> diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts index 136d33b281c..50b58dbe369 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts @@ -40,14 +40,13 @@ import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; import { DASHBOARD_DATASOURCE_PLUGIN_ID } from 'app/plugins/datasource/dashboard/types'; import { DashboardDataDTO } from 'app/types'; -import { AddLibraryPanelWidget } from '../scene/AddLibraryPanelWidget'; import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; import { DashboardGridItem } from '../scene/DashboardGridItem'; import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { PanelTimeRange } from '../scene/PanelTimeRange'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; import { NEW_LINK } from '../settings/links/utils'; -import { getQueryRunnerFor, getVizPanelKeyForPanelId } from '../utils/utils'; +import { getQueryRunnerFor } from '../utils/utils'; import { buildNewDashboardSaveModel } from './buildNewDashboardSaveModel'; import { GRAFANA_DATASOURCE_REF } from './const'; @@ -60,7 +59,6 @@ import { transformSaveModelToScene, convertOldSnapshotToScenesSnapshot, buildGridItemForLibPanel, - buildGridItemForLibraryPanelWidget, } from './transformSaveModelToScene'; describe('transformSaveModelToScene', () => { @@ -192,11 +190,6 @@ describe('transformSaveModelToScene', () => { gridPos: { x: 1, y: 0, w: 12, h: 8 }, }) as Panel; - const widgetLibPanel = { - title: 'Widget Panel', - type: 'add-library-panel', - }; - const libPanel = createPanelSaveModel({ title: 'Library Panel', gridPos: { x: 0, y: 0, w: 12, h: 8 }, @@ -211,7 +204,7 @@ describe('transformSaveModelToScene', () => { type: 'row', gridPos: { x: 0, y: 0, w: 12, h: 1 }, collapsed: true, - panels: [panel, widgetLibPanel, libPanel], + panels: [panel, libPanel], }) as unknown as RowPanel; const dashboard = { @@ -230,14 +223,12 @@ describe('transformSaveModelToScene', () => { expect(rowScene.state.title).toEqual(row.title); expect(rowScene.state.y).toEqual(row.gridPos!.y); expect(rowScene.state.isCollapsed).toEqual(row.collapsed); - expect(rowScene.state.children).toHaveLength(3); + expect(rowScene.state.children).toHaveLength(2); expect(rowScene.state.children[0]).toBeInstanceOf(DashboardGridItem); expect(rowScene.state.children[1]).toBeInstanceOf(DashboardGridItem); - expect(rowScene.state.children[2]).toBeInstanceOf(DashboardGridItem); // Panels are sorted by position in the row - expect((rowScene.state.children[0] as DashboardGridItem).state.body!).toBeInstanceOf(AddLibraryPanelWidget); - expect((rowScene.state.children[1] as DashboardGridItem).state.body!).toBeInstanceOf(LibraryVizPanel); - expect((rowScene.state.children[2] as DashboardGridItem).state.body!).toBeInstanceOf(VizPanel); + expect((rowScene.state.children[0] as DashboardGridItem).state.body!).toBeInstanceOf(LibraryVizPanel); + expect((rowScene.state.children[1] as DashboardGridItem).state.body!).toBeInstanceOf(VizPanel); }); it('should create panels within expanded row', () => { @@ -250,16 +241,7 @@ describe('transformSaveModelToScene', () => { y: 0, }, }); - const widgetLibPanelOutOfRow = { - title: 'Widget Panel', - type: 'add-library-panel', - gridPos: { - h: 8, - w: 12, - x: 12, - y: 0, - }, - }; + const libPanelOutOfRow = createPanelSaveModel({ title: 'Library Panel', gridPos: { x: 0, y: 8, w: 12, h: 8 }, @@ -268,6 +250,7 @@ describe('transformSaveModelToScene', () => { name: 'My Panel', }, }); + const rowWithPanel = createPanelSaveModel({ title: 'Row with panel', type: 'row', @@ -282,6 +265,7 @@ describe('transformSaveModelToScene', () => { // This panels array is not used if the row is not collapsed panels: [], }); + const panelInRow = createPanelSaveModel({ gridPos: { h: 8, @@ -291,16 +275,7 @@ describe('transformSaveModelToScene', () => { }, title: 'In row 1', }); - const widgetLibPanelInRow = { - title: 'Widget Panel', - type: 'add-library-panel', - gridPos: { - h: 8, - w: 12, - x: 12, - y: 17, - }, - }; + const libPanelInRow = createPanelSaveModel({ title: 'Library Panel', gridPos: { x: 0, y: 25, w: 12, h: 8 }, @@ -309,6 +284,7 @@ describe('transformSaveModelToScene', () => { name: 'My Panel', }, }); + const emptyRow = createPanelSaveModel({ collapsed: false, gridPos: { @@ -322,18 +298,10 @@ describe('transformSaveModelToScene', () => { title: 'Empty row', type: 'row', }); + const dashboard = { ...defaultDashboard, - panels: [ - panelOutOfRow, - widgetLibPanelOutOfRow, - libPanelOutOfRow, - rowWithPanel, - panelInRow, - widgetLibPanelInRow, - libPanelInRow, - emptyRow, - ], + panels: [panelOutOfRow, libPanelOutOfRow, rowWithPanel, panelInRow, libPanelInRow, emptyRow], }; const oldModel = new DashboardModel(dashboard); @@ -341,37 +309,31 @@ describe('transformSaveModelToScene', () => { const scene = createDashboardSceneFromDashboardModel(oldModel); const body = scene.state.body as SceneGridLayout; - expect(body.state.children).toHaveLength(5); + expect(body.state.children).toHaveLength(4); expect(body).toBeInstanceOf(SceneGridLayout); // Panel out of row expect(body.state.children[0]).toBeInstanceOf(DashboardGridItem); const panelOutOfRowVizPanel = body.state.children[0] as DashboardGridItem; expect((panelOutOfRowVizPanel.state.body as VizPanel)?.state.title).toBe(panelOutOfRow.title); - // widget lib panel out of row - expect(body.state.children[1]).toBeInstanceOf(DashboardGridItem); - const panelOutOfRowWidget = body.state.children[1] as DashboardGridItem; - expect(panelOutOfRowWidget.state.body!).toBeInstanceOf(AddLibraryPanelWidget); // lib panel out of row - expect(body.state.children[2]).toBeInstanceOf(DashboardGridItem); - const panelOutOfRowLibVizPanel = body.state.children[2] as DashboardGridItem; + expect(body.state.children[1]).toBeInstanceOf(DashboardGridItem); + const panelOutOfRowLibVizPanel = body.state.children[1] as DashboardGridItem; expect(panelOutOfRowLibVizPanel.state.body!).toBeInstanceOf(LibraryVizPanel); // Row with panels - expect(body.state.children[3]).toBeInstanceOf(SceneGridRow); - const rowWithPanelsScene = body.state.children[3] as SceneGridRow; + expect(body.state.children[2]).toBeInstanceOf(SceneGridRow); + const rowWithPanelsScene = body.state.children[2] as SceneGridRow; expect(rowWithPanelsScene.state.title).toBe(rowWithPanel.title); expect(rowWithPanelsScene.state.key).toBe('panel-10'); - expect(rowWithPanelsScene.state.children).toHaveLength(3); - const widget = rowWithPanelsScene.state.children[1] as DashboardGridItem; - expect(widget.state.body!).toBeInstanceOf(AddLibraryPanelWidget); - const libPanel = rowWithPanelsScene.state.children[2] as DashboardGridItem; + expect(rowWithPanelsScene.state.children).toHaveLength(2); + const libPanel = rowWithPanelsScene.state.children[1] as DashboardGridItem; expect(libPanel.state.body!).toBeInstanceOf(LibraryVizPanel); // Panel within row expect(rowWithPanelsScene.state.children[0]).toBeInstanceOf(DashboardGridItem); const panelInRowVizPanel = rowWithPanelsScene.state.children[0] as DashboardGridItem; expect((panelInRowVizPanel.state.body as VizPanel).state.title).toBe(panelInRow.title); // Empty row - expect(body.state.children[4]).toBeInstanceOf(SceneGridRow); - const emptyRowScene = body.state.children[4] as SceneGridRow; + expect(body.state.children[3]).toBeInstanceOf(SceneGridRow); + const emptyRowScene = body.state.children[3] as SceneGridRow; expect(emptyRowScene.state.title).toBe(emptyRow.title); expect(emptyRowScene.state.children).toHaveLength(0); }); @@ -540,17 +502,6 @@ describe('transformSaveModelToScene', () => { expect(runner.state.cacheTimeout).toBe('10'); expect(runner.state.queryCachingTTL).toBe(200000); }); - it('should convert saved lib widget to AddLibraryPanelWidget', () => { - const panel = { - id: 10, - type: 'add-library-panel', - }; - - const gridItem = buildGridItemForLibraryPanelWidget(new PanelModel(panel))!; - const libPanelWidget = gridItem.state.body as AddLibraryPanelWidget; - - expect(libPanelWidget.state.key).toEqual(getVizPanelKeyForPanelId(panel.id)); - }); it('should convert saved lib panel to LibraryVizPanel', () => { const panel = { diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index 68286638784..cdbcf461350 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -33,7 +33,6 @@ import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { trackDashboardLoaded } from 'app/features/dashboard/utils/tracking'; import { DashboardDTO } from 'app/types'; -import { AddLibraryPanelWidget } from '../scene/AddLibraryPanelWidget'; import { AlertStatesDataLayer } from '../scene/AlertStatesDataLayer'; import { DashboardAnnotationsDataLayer } from '../scene/DashboardAnnotationsDataLayer'; import { DashboardControls } from '../scene/DashboardControls'; @@ -112,18 +111,6 @@ export function createSceneObjectsForPanels(oldPanels: PanelModel[]): SceneGridI currentRowPanels = []; } } - } else if (panel.type === 'add-library-panel') { - const gridItem = buildGridItemForLibraryPanelWidget(panel); - - if (!gridItem) { - continue; - } - - if (currentRow) { - currentRowPanels.push(gridItem); - } else { - panels.push(gridItem); - } } else if (panel.libraryPanel?.uid && !('model' in panel.libraryPanel)) { const gridItem = buildGridItemForLibPanel(panel); @@ -170,15 +157,7 @@ function createRowFromPanelModel(row: PanelModel, content: SceneGridItemLike[]): saveModel = new PanelModel(saveModel); } - if (saveModel.type === 'add-library-panel') { - const gridItem = buildGridItemForLibraryPanelWidget(saveModel); - - if (!gridItem) { - throw new Error('Failed to build grid item for library panel widget'); - } - - return gridItem; - } else if (saveModel.libraryPanel?.uid && !('model' in saveModel.libraryPanel)) { + if (saveModel.libraryPanel?.uid && !('model' in saveModel.libraryPanel)) { const gridItem = buildGridItemForLibPanel(saveModel); if (!gridItem) { @@ -435,24 +414,6 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode } } -export function buildGridItemForLibraryPanelWidget(panel: PanelModel) { - if (panel.type !== 'add-library-panel') { - return null; - } - - const body = new AddLibraryPanelWidget({ - key: getVizPanelKeyForPanelId(panel.id), - }); - - return new DashboardGridItem({ - body, - y: panel.gridPos.y, - x: panel.gridPos.x, - width: panel.gridPos.w, - height: panel.gridPos.h, - }); -} - export function buildGridItemForLibPanel(panel: PanelModel) { if (!panel.libraryPanel) { return null; diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts index 98bf96bb5fb..6137dfc4a21 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts @@ -39,7 +39,6 @@ import snapshotableDashboardJson from './testfiles/snapshotable_dashboard.json'; import snapshotableWithRowsDashboardJson from './testfiles/snapshotable_with_rows.json'; import { buildGridItemForLibPanel, - buildGridItemForLibraryPanelWidget, buildGridItemForPanel, transformSaveModelToScene, } from './transformSaveModelToScene'; @@ -1027,8 +1026,6 @@ describe('transformSceneToSaveModel', () => { export function buildGridItemFromPanelSchema(panel: Partial) { if (panel.libraryPanel) { return buildGridItemForLibPanel(new PanelModel(panel))!; - } else if (panel.type === 'add-library-panel') { - return buildGridItemForLibraryPanelWidget(new PanelModel(panel))!; } return buildGridItemForPanel(new PanelModel(panel)); diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts index bdd6560536b..384aede6ea0 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts @@ -31,7 +31,6 @@ import { getPanelDataFrames } from 'app/features/dashboard/components/HelpWizard import { DASHBOARD_SCHEMA_VERSION } from 'app/features/dashboard/state/DashboardMigrator'; import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types'; -import { AddLibraryPanelWidget } from '../scene/AddLibraryPanelWidget'; import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; import { DashboardGridItem } from '../scene/DashboardGridItem'; import { DashboardScene } from '../scene/DashboardScene'; @@ -187,20 +186,6 @@ export function gridItemToPanel(gridItem: DashboardGridItem, isSnapshot = false) return libraryVizPanelToPanel(gridItem.state.body, { x, y, w, h }); } - // Handle library panel widget as well and exit early - if (gridItem.state.body instanceof AddLibraryPanelWidget) { - x = gridItem.state.x ?? 0; - y = gridItem.state.y ?? 0; - w = gridItem.state.width ?? 0; - h = gridItem.state.height ?? 0; - - return { - id: getPanelIdForVizPanel(gridItem.state.body), - type: 'add-library-panel', - gridPos: { x, y, w, h }, - }; - } - if (!(gridItem.state.body instanceof VizPanel)) { throw new Error('DashboardGridItem body expected to be VizPanel'); } diff --git a/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx b/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx index 3602a5f188d..b2fdb9f6267 100644 --- a/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx @@ -44,7 +44,7 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => { const onAddLibraryPanel = () => { DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_from_library' }); if (dashboard instanceof DashboardScene) { - dashboard.onCreateLibPanelWidget(); + dashboard.onShowAddLibraryPanelDrawer(); } else { onAddLibraryPanelImpl(dashboard); }