[v11.0.x] DashboardScene: Move add library panel view from grid item to drawer (#86409)

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 5837def850)

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
pull/86480/head
grafana-delivery-bot[bot] 1 year ago committed by GitHub
parent df2fee63a5
commit 6881eabedf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 74
      public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx
  2. 69
      public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx
  3. 253
      public/app/features/dashboard-scene/scene/AddLibraryPanelWidget.test.tsx
  4. 163
      public/app/features/dashboard-scene/scene/AddLibraryPanelWidget.tsx
  5. 8
      public/app/features/dashboard-scene/scene/DashboardGridItem.tsx
  6. 15
      public/app/features/dashboard-scene/scene/DashboardScene.test.tsx
  7. 30
      public/app/features/dashboard-scene/scene/DashboardScene.tsx
  8. 2
      public/app/features/dashboard-scene/scene/NavToolbarActions.tsx
  9. 91
      public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts
  10. 41
      public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts
  11. 3
      public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts
  12. 15
      public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts
  13. 2
      public/app/features/dashboard/dashgrid/DashboardEmpty.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 };
}

@ -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<AddLibraryPanelDrawerState> {
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<AddLibraryPanelDrawer>) => {
const title = t('library-panel.add-widget.title', 'Add panel from panel library');
return (
<Drawer title={title} onClose={model.onClose}>
<LibraryPanelsSearch
onClick={model.onAddLibraryPanel}
variant={LibraryPanelsSearchVariant.Tight}
showPanelFilter
/>
</Drawer>
);
};
}

@ -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<HTMLButtonElement>;
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 };
}

@ -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<AddLibraryPanelWidgetState> {
public constructor(state: AddLibraryPanelWidgetState) {
super({
...state,
});
}
private get _dashboard(): DashboardScene {
return getDashboardSceneFor(this);
}
public getDashboard(): DashboardScene {
return this._dashboard;
}
public onCancelAddPanel = (evt: React.MouseEvent<HTMLButtonElement>) => {
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<AddLibraryPanelWidget>) => {
const dashboard = model.getDashboard();
const styles = useStyles2(getStyles);
return (
<div className={styles.wrapper}>
<div className={cx('panel-container', styles.callToAction)}>
<div className={cx(styles.headerRow, `grid-drag-handle-${dashboard.state.body.state.key}`)}>
<span>
<Trans i18nKey="library-panel.add-widget.title">Add panel from panel library</Trans>
</span>
<div className="flex-grow-1" />
<IconButton
aria-label="Close 'Add Panel' widget"
name="times"
onClick={model.onCancelAddPanel}
tooltip="Close widget"
/>
</div>
<LibraryPanelsSearch
onClick={model.onAddLibraryPanel}
variant={LibraryPanelsSearchVariant.Tight}
showPanelFilter
/>
</div>
</div>
);
};
}
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`,
}),
};
};

@ -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<DashboardGridItemState> 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<DashboardGridItemState> i
return <body.Component model={body} key={body.state.key} />;
}
if (body instanceof AddLibraryPanelWidget) {
if (body instanceof AddLibraryPanelDrawer) {
return <body.Component model={body} key={body.state.key} />;
}
}

@ -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);

@ -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<DashboardSceneState> {
static listenToChangesInProps = PERSISTED_PROPS;
static Component = DashboardSceneRenderer;
/**
@ -761,30 +760,9 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
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({}),
});
}

@ -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' });
}}
/>

@ -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 = {

@ -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;

@ -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<Panel>) {
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));

@ -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');
}

@ -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);
}

Loading…
Cancel
Save