mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Scenes: Add 'Import from library' functionality (#83498)
* wip * tests + refactor ad panel func * Add row functionality * update row state only when there are children * Add new row + copy paste panels * Add library panel functionality * tests * PR mods * reafctor + tests * reafctor * fix test * refactor * fix bug on cancelling lib widget * dashboard now saves with lib panel widget * add lib panels widget works in rows as well * split add lib panel func to another PR * Add library panel functionality * refactor * take panelKey into account when getting next panel id in dashboard * fix tests
This commit is contained in:
parent
393b12f49f
commit
04539ffccb
@ -0,0 +1,252 @@
|
||||
import { SceneGridItem, 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 { 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 SceneGridItem({
|
||||
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 SceneGridItem;
|
||||
|
||||
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 SceneGridItem({
|
||||
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 SceneGridItem;
|
||||
|
||||
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 SceneGridItem({
|
||||
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 SceneGridItem;
|
||||
const gridItemTwo = body.state.children[1] as SceneGridItem;
|
||||
|
||||
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 SceneGridItem({
|
||||
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 SceneGridItem;
|
||||
|
||||
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 SceneGridItem({
|
||||
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 };
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
import { css, cx, keyframes } from '@emotion/css';
|
||||
import React from 'react';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import {
|
||||
SceneComponentProps,
|
||||
SceneGridItem,
|
||||
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 { 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 SceneGridItem && 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 SceneGridItem) {
|
||||
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`,
|
||||
}),
|
||||
};
|
||||
};
|
@ -264,6 +264,17 @@ describe('DashboardScene', () => {
|
||||
expect(gridItem.state.y).toBe(0);
|
||||
expect(scene.state.hasCopiedPanel).toBe(false);
|
||||
});
|
||||
|
||||
it('Should create a new add library panel widget', () => {
|
||||
scene.onCreateLibPanelWidget();
|
||||
|
||||
const body = scene.state.body as SceneGridLayout;
|
||||
const gridItem = body.state.children[0] as SceneGridItem;
|
||||
|
||||
expect(body.state.children.length).toBe(5);
|
||||
expect(gridItem.state.body!.state.key).toBe('panel-5');
|
||||
expect(gridItem.state.y).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -59,6 +59,7 @@ import {
|
||||
isPanelClone,
|
||||
} from '../utils/utils';
|
||||
|
||||
import { AddLibraryPanelWidget } from './AddLibraryPanelWidget';
|
||||
import { DashboardControls } from './DashboardControls';
|
||||
import { DashboardSceneUrlSync } from './DashboardSceneUrlSync';
|
||||
import { PanelRepeaterGridItem } from './PanelRepeaterGridItem';
|
||||
@ -612,6 +613,29 @@ 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');
|
||||
}
|
||||
|
||||
const sceneGridLayout = this.state.body;
|
||||
|
||||
const panelId = dashboardSceneGraph.getNextPanelId(this);
|
||||
|
||||
const newGridItem = new SceneGridItem({
|
||||
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 onCreateNewRow() {
|
||||
const row = getDefaultRow(this);
|
||||
|
||||
|
@ -18,6 +18,7 @@ describe('NavToolbarActions', () => {
|
||||
expect(screen.queryByLabelText('Add visualization')).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('Add row')).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('Paste panel')).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('Add library panel')).not.toBeInTheDocument();
|
||||
expect(await screen.findByText('Edit')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Share')).toBeInTheDocument();
|
||||
});
|
||||
@ -32,6 +33,7 @@ describe('NavToolbarActions', () => {
|
||||
expect(await screen.findByLabelText('Add visualization')).toBeInTheDocument();
|
||||
expect(await screen.findByLabelText('Add row')).toBeInTheDocument();
|
||||
expect(await screen.findByLabelText('Paste panel')).toBeInTheDocument();
|
||||
expect(await screen.findByLabelText('Add library panel')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Edit')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Share')).not.toBeInTheDocument();
|
||||
});
|
||||
|
@ -71,6 +71,22 @@ export function ToolbarActions({ dashboard }: Props) {
|
||||
),
|
||||
});
|
||||
|
||||
toolbarActions.push({
|
||||
group: 'icon-actions',
|
||||
condition: isEditing && !editview && !isViewingPanel && !isEditingPanel,
|
||||
render: () => (
|
||||
<ToolbarButton
|
||||
key="add-library-panel"
|
||||
tooltip={'Add library panel'}
|
||||
icon="library-panel"
|
||||
onClick={() => {
|
||||
dashboard.onCreateLibPanelWidget();
|
||||
DashboardInteractions.toolbarAddButtonClicked({ item: 'add_library_panel' });
|
||||
}}
|
||||
/>
|
||||
),
|
||||
});
|
||||
|
||||
toolbarActions.push({
|
||||
group: 'icon-actions',
|
||||
condition: isEditing && !editview && !isViewingPanel && !isEditingPanel,
|
||||
|
@ -42,11 +42,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 { LibraryVizPanel } from '../scene/LibraryVizPanel';
|
||||
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
||||
import { PanelTimeRange } from '../scene/PanelTimeRange';
|
||||
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
||||
import { NEW_LINK } from '../settings/links/utils';
|
||||
import { getQueryRunnerFor } from '../utils/utils';
|
||||
import { getQueryRunnerFor, getVizPanelKeyForPanelId } from '../utils/utils';
|
||||
|
||||
import { buildNewDashboardSaveModel } from './buildNewDashboardSaveModel';
|
||||
import { GRAFANA_DATASOURCE_REF } from './const';
|
||||
@ -58,6 +60,8 @@ import {
|
||||
createSceneVariableFromVariableModel,
|
||||
transformSaveModelToScene,
|
||||
convertOldSnapshotToScenesSnapshot,
|
||||
buildGridItemForLibPanel,
|
||||
buildGridItemForLibraryPanelWidget,
|
||||
} from './transformSaveModelToScene';
|
||||
|
||||
describe('transformSaveModelToScene', () => {
|
||||
@ -453,6 +457,37 @@ 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 = {
|
||||
title: 'Panel',
|
||||
gridPos: { x: 0, y: 0, w: 12, h: 8 },
|
||||
transparent: true,
|
||||
libraryPanel: {
|
||||
uid: '123',
|
||||
name: 'My Panel',
|
||||
folderUid: '456',
|
||||
},
|
||||
};
|
||||
|
||||
const gridItem = buildGridItemForLibPanel(new PanelModel(panel))!;
|
||||
const libVizPanel = gridItem.state.body as LibraryVizPanel;
|
||||
|
||||
expect(libVizPanel.state.uid).toEqual(panel.libraryPanel.uid);
|
||||
expect(libVizPanel.state.name).toEqual(panel.libraryPanel.name);
|
||||
expect(libVizPanel.state.title).toEqual(panel.title);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when creating variables objects', () => {
|
||||
|
@ -34,6 +34,7 @@ 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';
|
||||
@ -110,6 +111,12 @@ export function createSceneObjectsForPanels(oldPanels: PanelModel[]): SceneGridI
|
||||
currentRowPanels = [];
|
||||
}
|
||||
}
|
||||
} else if (panel.type === 'add-library-panel') {
|
||||
const gridItem = buildGridItemForLibraryPanelWidget(panel);
|
||||
|
||||
if (gridItem) {
|
||||
panels.push(gridItem);
|
||||
}
|
||||
} else if (panel.libraryPanel?.uid && !('model' in panel.libraryPanel)) {
|
||||
const gridItem = buildGridItemForLibPanel(panel);
|
||||
if (gridItem) {
|
||||
@ -399,6 +406,24 @@ 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 SceneGridItem({
|
||||
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;
|
||||
|
@ -41,6 +41,7 @@ import snapshotableDashboardJson from './testfiles/snapshotable_dashboard.json';
|
||||
import snapshotableWithRowsDashboardJson from './testfiles/snapshotable_with_rows.json';
|
||||
import {
|
||||
buildGridItemForLibPanel,
|
||||
buildGridItemForLibraryPanelWidget,
|
||||
buildGridItemForPanel,
|
||||
transformSaveModelToScene,
|
||||
} from './transformSaveModelToScene';
|
||||
@ -351,6 +352,30 @@ describe('transformSceneToSaveModel', () => {
|
||||
expect(result.transformations).toBeUndefined();
|
||||
expect(result.fieldConfig).toBeUndefined();
|
||||
});
|
||||
|
||||
it('given a library panel widget', () => {
|
||||
const panel = buildGridItemFromPanelSchema({
|
||||
id: 4,
|
||||
gridPos: {
|
||||
h: 8,
|
||||
w: 12,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
type: 'add-library-panel',
|
||||
});
|
||||
|
||||
const result = gridItemToPanel(panel);
|
||||
|
||||
expect(result.id).toBe(4);
|
||||
expect(result.gridPos).toEqual({
|
||||
h: 8,
|
||||
w: 12,
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
expect(result.type).toBe('add-library-panel');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Annotations', () => {
|
||||
@ -897,6 +922,9 @@ describe('transformSceneToSaveModel', () => {
|
||||
export function buildGridItemFromPanelSchema(panel: Partial<Panel>): SceneGridItemLike {
|
||||
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));
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ 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 { DashboardScene } from '../scene/DashboardScene';
|
||||
import { LibraryVizPanel } from '../scene/LibraryVizPanel';
|
||||
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
||||
@ -167,6 +168,20 @@ export function gridItemToPanel(gridItem: SceneGridItemLike, isSnapshot = false)
|
||||
} as Panel;
|
||||
}
|
||||
|
||||
// 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('SceneGridItem body expected to be VizPanel');
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user