mirror of
https://github.com/grafana/grafana.git
synced 2026-07-30 00:08:10 -05:00
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
This commit is contained in:
@@ -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`,
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -23,13 +23,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;
|
||||
@@ -131,7 +131,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()) {
|
||||
@@ -233,7 +233,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' });
|
||||
}}
|
||||
/>
|
||||
|
||||
+21
-70
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user