mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
parent
298384cea9
commit
f50624d257
@ -30,6 +30,7 @@ import { djb2Hash } from '../utils/djb2Hash';
|
||||
import { DashboardControls } from './DashboardControls';
|
||||
import { DashboardScene, DashboardSceneState } from './DashboardScene';
|
||||
import { LibraryVizPanel } from './LibraryVizPanel';
|
||||
import { PanelRepeaterGridItem } from './PanelRepeaterGridItem';
|
||||
|
||||
jest.mock('../settings/version-history/HistorySrv');
|
||||
jest.mock('../serialization/transformSaveModelToScene');
|
||||
@ -541,7 +542,119 @@ describe('DashboardScene', () => {
|
||||
expect(gridRow.state.children.length).toBe(1);
|
||||
});
|
||||
|
||||
it('Should unlink a library panel', () => {
|
||||
it('Should duplicate a panel', () => {
|
||||
const vizPanel = ((scene.state.body as SceneGridLayout).state.children[0] as SceneGridItem).state.body;
|
||||
scene.duplicatePanel(vizPanel as VizPanel);
|
||||
|
||||
const body = scene.state.body as SceneGridLayout;
|
||||
const gridItem = body.state.children[5] as SceneGridItem;
|
||||
|
||||
expect(body.state.children.length).toBe(6);
|
||||
expect(gridItem.state.body!.state.key).toBe('panel-7');
|
||||
});
|
||||
|
||||
it('Should duplicate a library panel', () => {
|
||||
const libraryPanel = ((scene.state.body as SceneGridLayout).state.children[4] as SceneGridItem).state.body;
|
||||
const vizPanel = (libraryPanel as LibraryVizPanel).state.panel;
|
||||
scene.duplicatePanel(vizPanel as VizPanel);
|
||||
|
||||
const body = scene.state.body as SceneGridLayout;
|
||||
const gridItem = body.state.children[5] as SceneGridItem;
|
||||
|
||||
const libVizPanel = gridItem.state.body as LibraryVizPanel;
|
||||
|
||||
expect(body.state.children.length).toBe(6);
|
||||
expect(libVizPanel.state.panelKey).toBe('panel-7');
|
||||
expect(libVizPanel.state.panel?.state.key).toBe('panel-7');
|
||||
});
|
||||
|
||||
it('Should duplicate a repeated panel', () => {
|
||||
const scene = buildTestScene({
|
||||
body: new SceneGridLayout({
|
||||
children: [
|
||||
new PanelRepeaterGridItem({
|
||||
key: `grid-item-1`,
|
||||
width: 24,
|
||||
height: 8,
|
||||
repeatedPanels: [
|
||||
new VizPanel({
|
||||
title: 'Library Panel',
|
||||
key: 'panel-1',
|
||||
pluginId: 'table',
|
||||
}),
|
||||
],
|
||||
source: new VizPanel({
|
||||
title: 'Library Panel',
|
||||
key: 'panel-1',
|
||||
pluginId: 'table',
|
||||
}),
|
||||
variableName: 'custom',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const vizPanel = ((scene.state.body as SceneGridLayout).state.children[0] as PanelRepeaterGridItem).state
|
||||
.repeatedPanels![0];
|
||||
|
||||
scene.duplicatePanel(vizPanel as VizPanel);
|
||||
|
||||
const body = scene.state.body as SceneGridLayout;
|
||||
const gridItem = body.state.children[1] as SceneGridItem;
|
||||
|
||||
expect(body.state.children.length).toBe(2);
|
||||
expect(gridItem.state.body!.state.key).toBe('panel-2');
|
||||
});
|
||||
|
||||
it('Should duplicate a panel in a row', () => {
|
||||
const vizPanel = (
|
||||
((scene.state.body as SceneGridLayout).state.children[2] as SceneGridRow).state.children[0] as SceneGridItem
|
||||
).state.body;
|
||||
scene.duplicatePanel(vizPanel as VizPanel);
|
||||
|
||||
const body = scene.state.body as SceneGridLayout;
|
||||
const gridRow = body.state.children[2] as SceneGridRow;
|
||||
const gridItem = gridRow.state.children[2] as SceneGridItem;
|
||||
|
||||
expect(gridRow.state.children.length).toBe(3);
|
||||
expect(gridItem.state.body!.state.key).toBe('panel-7');
|
||||
});
|
||||
|
||||
it('Should duplicate a library panel in a row', () => {
|
||||
const libraryPanel = (
|
||||
((scene.state.body as SceneGridLayout).state.children[2] as SceneGridRow).state.children[1] as SceneGridItem
|
||||
).state.body;
|
||||
const vizPanel = (libraryPanel as LibraryVizPanel).state.panel;
|
||||
|
||||
scene.duplicatePanel(vizPanel as VizPanel);
|
||||
|
||||
const body = scene.state.body as SceneGridLayout;
|
||||
const gridRow = body.state.children[2] as SceneGridRow;
|
||||
const gridItem = gridRow.state.children[2] as SceneGridItem;
|
||||
|
||||
const libVizPanel = gridItem.state.body as LibraryVizPanel;
|
||||
|
||||
expect(gridRow.state.children.length).toBe(3);
|
||||
expect(libVizPanel.state.panelKey).toBe('panel-7');
|
||||
expect(libVizPanel.state.panel?.state.key).toBe('panel-7');
|
||||
});
|
||||
|
||||
it('Should fail to duplicate a panel if it does not have a grid item parent', () => {
|
||||
const vizPanel = new VizPanel({
|
||||
title: 'Panel Title',
|
||||
key: 'panel-5',
|
||||
pluginId: 'timeseries',
|
||||
});
|
||||
|
||||
scene.duplicatePanel(vizPanel);
|
||||
|
||||
const body = scene.state.body as SceneGridLayout;
|
||||
|
||||
// length remains unchanged
|
||||
expect(body.state.children.length).toBe(5);
|
||||
});
|
||||
|
||||
it('Should unlink a library panel', () => {
|
||||
const libPanel = new LibraryVizPanel({
|
||||
title: 'title',
|
||||
uid: 'abc',
|
||||
|
@ -43,7 +43,7 @@ import { DecoratedRevisionModel } from '../settings/VersionsEditView';
|
||||
import { DashboardEditView } from '../settings/utils';
|
||||
import { historySrv } from '../settings/version-history';
|
||||
import { DashboardModelCompatibilityWrapper } from '../utils/DashboardModelCompatibilityWrapper';
|
||||
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
||||
import { dashboardSceneGraph, getLibraryVizPanelFromVizPanel } from '../utils/dashboardSceneGraph';
|
||||
import { djb2Hash } from '../utils/djb2Hash';
|
||||
import { getDashboardUrl } from '../utils/urlBuilders';
|
||||
import {
|
||||
@ -459,7 +459,9 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
||||
return;
|
||||
}
|
||||
|
||||
const gridItem = vizPanel.parent;
|
||||
const libraryPanel = getLibraryVizPanelFromVizPanel(vizPanel);
|
||||
|
||||
const gridItem = libraryPanel ? libraryPanel.parent : vizPanel.parent;
|
||||
|
||||
if (!(gridItem instanceof SceneGridItem || gridItem instanceof PanelRepeaterGridItem)) {
|
||||
console.error('Trying to duplicate a panel in a layout that is not SceneGridItem or PanelRepeaterGridItem');
|
||||
@ -468,26 +470,45 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
||||
|
||||
let panelState;
|
||||
let panelData;
|
||||
if (gridItem instanceof PanelRepeaterGridItem) {
|
||||
const { key, ...gridRepeaterSourceState } = sceneUtils.cloneSceneObjectState(gridItem.state.source.state);
|
||||
panelState = { ...gridRepeaterSourceState };
|
||||
panelData = sceneGraph.getData(gridItem.state.source).clone();
|
||||
let newGridItem;
|
||||
const newPanelId = dashboardSceneGraph.getNextPanelId(this);
|
||||
|
||||
if (libraryPanel) {
|
||||
const gridItemToDuplicateState = sceneUtils.cloneSceneObjectState(gridItem.state);
|
||||
|
||||
newGridItem = new SceneGridItem({
|
||||
x: gridItemToDuplicateState.x,
|
||||
y: gridItemToDuplicateState.y,
|
||||
width: gridItemToDuplicateState.width,
|
||||
height: gridItemToDuplicateState.height,
|
||||
body: new LibraryVizPanel({
|
||||
title: libraryPanel.state.title,
|
||||
uid: libraryPanel.state.uid,
|
||||
name: libraryPanel.state.name,
|
||||
panelKey: getVizPanelKeyForPanelId(newPanelId),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
const { key, ...gridItemPanelState } = sceneUtils.cloneSceneObjectState(vizPanel.state);
|
||||
panelState = { ...gridItemPanelState };
|
||||
panelData = sceneGraph.getData(vizPanel).clone();
|
||||
if (gridItem instanceof PanelRepeaterGridItem) {
|
||||
panelState = sceneUtils.cloneSceneObjectState(gridItem.state.source.state);
|
||||
panelData = sceneGraph.getData(gridItem.state.source).clone();
|
||||
} else {
|
||||
panelState = sceneUtils.cloneSceneObjectState(vizPanel.state);
|
||||
panelData = sceneGraph.getData(vizPanel).clone();
|
||||
}
|
||||
|
||||
// when we duplicate a panel we don't want to clone the alert state
|
||||
delete panelData.state.data?.alertState;
|
||||
|
||||
newGridItem = new SceneGridItem({
|
||||
x: gridItem.state.x,
|
||||
y: gridItem.state.y,
|
||||
height: NEW_PANEL_HEIGHT,
|
||||
width: NEW_PANEL_WIDTH,
|
||||
body: new VizPanel({ ...panelState, $data: panelData, key: getVizPanelKeyForPanelId(newPanelId) }),
|
||||
});
|
||||
}
|
||||
|
||||
// when we duplicate a panel we don't want to clone the alert state
|
||||
delete panelData.state.data?.alertState;
|
||||
|
||||
const { key: gridItemKey, ...gridItemToDuplicateState } = sceneUtils.cloneSceneObjectState(gridItem.state);
|
||||
|
||||
const newGridItem = new SceneGridItem({
|
||||
...gridItemToDuplicateState,
|
||||
body: new VizPanel({ ...panelState, $data: panelData }),
|
||||
});
|
||||
|
||||
if (!(this.state.body instanceof SceneGridLayout)) {
|
||||
console.error('Trying to duplicate a panel in a layout that is not SceneGridLayout ');
|
||||
return;
|
||||
@ -495,6 +516,18 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
||||
|
||||
const sceneGridLayout = this.state.body;
|
||||
|
||||
if (gridItem.parent instanceof SceneGridRow) {
|
||||
const row = gridItem.parent;
|
||||
|
||||
row.setState({
|
||||
children: [...row.state.children, newGridItem],
|
||||
});
|
||||
|
||||
sceneGridLayout.forceRender();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sceneGridLayout.setState({
|
||||
children: [...sceneGridLayout.state.children, newGridItem],
|
||||
});
|
||||
|
@ -16,6 +16,7 @@ import { DashboardControls } from '../scene/DashboardControls';
|
||||
import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene';
|
||||
import { LibraryVizPanel } from '../scene/LibraryVizPanel';
|
||||
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
|
||||
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
||||
|
||||
import { dashboardSceneGraph, getNextPanelId } from './dashboardSceneGraph';
|
||||
import { findVizPanelByKey } from './utils';
|
||||
@ -123,7 +124,7 @@ describe('dashboardSceneGraph', () => {
|
||||
expect(id).toBe(4);
|
||||
});
|
||||
|
||||
it('should take library panels into account', () => {
|
||||
it('should take library panels, panels in rows and panel repeaters into account', () => {
|
||||
const scene = buildTestScene({
|
||||
body: new SceneGridLayout({
|
||||
children: [
|
||||
@ -151,6 +152,17 @@ describe('dashboardSceneGraph', () => {
|
||||
pluginId: 'table',
|
||||
}),
|
||||
}),
|
||||
new PanelRepeaterGridItem({
|
||||
source: new VizPanel({
|
||||
title: 'Panel C',
|
||||
key: 'panel-4',
|
||||
pluginId: 'table',
|
||||
}),
|
||||
variableName: 'repeat',
|
||||
repeatedPanels: [],
|
||||
repeatDirection: 'h',
|
||||
maxPerRow: 1,
|
||||
}),
|
||||
new SceneGridRow({
|
||||
key: 'key',
|
||||
title: 'row',
|
||||
@ -178,7 +190,7 @@ describe('dashboardSceneGraph', () => {
|
||||
|
||||
const id = getNextPanelId(scene);
|
||||
|
||||
expect(id).toBe(4);
|
||||
expect(id).toBe(5);
|
||||
});
|
||||
|
||||
it('should get next panel id in a layout with rows', () => {
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
import { LibraryVizPanel } from '../scene/LibraryVizPanel';
|
||||
import { VizPanelLinks } from '../scene/PanelLinks';
|
||||
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
||||
|
||||
import { getPanelIdForLibraryVizPanel, getPanelIdForVizPanel } from './utils';
|
||||
|
||||
@ -85,6 +86,21 @@ export function getNextPanelId(dashboard: DashboardScene): number {
|
||||
}
|
||||
|
||||
for (const child of body.state.children) {
|
||||
if (child instanceof PanelRepeaterGridItem) {
|
||||
const vizPanel = child.state.source;
|
||||
|
||||
if (vizPanel) {
|
||||
const panelId =
|
||||
vizPanel instanceof LibraryVizPanel
|
||||
? getPanelIdForLibraryVizPanel(vizPanel)
|
||||
: getPanelIdForVizPanel(vizPanel);
|
||||
|
||||
if (panelId > max) {
|
||||
max = panelId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (child instanceof SceneGridItem) {
|
||||
const vizPanel = child.state.body;
|
||||
|
||||
@ -130,6 +146,19 @@ export function getNextPanelId(dashboard: DashboardScene): number {
|
||||
return max + 1;
|
||||
}
|
||||
|
||||
// Returns the LibraryVizPanel that corresponds to the given VizPanel if it exists
|
||||
export const getLibraryVizPanelFromVizPanel = (vizPanel: VizPanel): LibraryVizPanel | null => {
|
||||
if (vizPanel.parent instanceof LibraryVizPanel) {
|
||||
return vizPanel.parent;
|
||||
}
|
||||
|
||||
if (vizPanel.parent instanceof PanelRepeaterGridItem && vizPanel.parent.state.source instanceof LibraryVizPanel) {
|
||||
return vizPanel.parent.state.source;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const dashboardSceneGraph = {
|
||||
getTimePicker,
|
||||
getRefreshPicker,
|
||||
|
Loading…
Reference in New Issue
Block a user