Dynamic dashboards: Implement cloneLayout for responsive grid (#100855)
* implement clone layout for responsive grid * Add tests * Fix lint * Make sure we base new keys on old keys * Start tests from panel 1 instead of 0
This commit is contained in:
+16
@@ -210,6 +210,22 @@ describe('DefaultGridLayoutManager', () => {
|
||||
|
||||
expect(gridRow.state.children.length).toBe(3);
|
||||
});
|
||||
|
||||
it('Should clone the layout correctly', () => {
|
||||
const { manager } = setup();
|
||||
const clone = manager.cloneLayout('foo', true) as DefaultGridLayoutManager;
|
||||
const panelA = findVizPanelByKey(clone, 'foo/grid-item-0/panel-0');
|
||||
expect(panelA?.state.title).toBe('Panel A');
|
||||
|
||||
const panelB = findVizPanelByKey(clone, 'foo/grid-item-1/panel-1');
|
||||
expect(panelB?.state.title).toBe('Panel B');
|
||||
|
||||
const panelC = findVizPanelByKey(clone, 'foo/panel-2/grid-item-3/panel-3');
|
||||
expect(panelC?.state.title).toBe('Panel C');
|
||||
|
||||
const panelD = findVizPanelByKey(clone, 'foo/panel-2/grid-item-4/panel-4');
|
||||
expect(panelD?.state.title).toBe('Panel D');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { SceneCSSGridLayout, SceneQueryRunner, VizPanel } from '@grafana/scenes';
|
||||
|
||||
import { findVizPanelByKey } from '../../utils/utils';
|
||||
import { DashboardScene } from '../DashboardScene';
|
||||
|
||||
import { ResponsiveGridItem } from './ResponsiveGridItem';
|
||||
import { ResponsiveGridLayoutManager } from './ResponsiveGridLayoutManager';
|
||||
|
||||
describe('ResponsiveGridLayoutManager', () => {
|
||||
it('Should clone the layout', () => {
|
||||
const { manager } = setup();
|
||||
const clone = manager.cloneLayout('foo', true) as ResponsiveGridLayoutManager;
|
||||
|
||||
expect(clone).not.toBe(manager);
|
||||
expect(clone.state.layout).not.toBe(manager.state.layout);
|
||||
expect(clone.state.layout.state.children).not.toBe(manager.state.layout.state.children);
|
||||
expect(clone.state.layout.state.children.length).toBe(manager.state.layout.state.children.length);
|
||||
|
||||
const panelA = findVizPanelByKey(clone, 'foo/grid-item-1/panel-1');
|
||||
expect(panelA?.state.title).toBe('Panel A');
|
||||
|
||||
const panelB = findVizPanelByKey(clone, 'foo/grid-item-2/panel-2');
|
||||
expect(panelB?.state.title).toBe('Panel B');
|
||||
});
|
||||
});
|
||||
|
||||
function setup() {
|
||||
const gridItems = [
|
||||
new ResponsiveGridItem({
|
||||
key: 'grid-item-1',
|
||||
body: new VizPanel({
|
||||
title: 'Panel A',
|
||||
key: 'panel-1',
|
||||
pluginId: 'table',
|
||||
$data: new SceneQueryRunner({ key: 'data-query-runner', queries: [{ refId: 'A' }] }),
|
||||
}),
|
||||
}),
|
||||
new ResponsiveGridItem({
|
||||
key: 'grid-item-2',
|
||||
body: new VizPanel({
|
||||
title: 'Panel B',
|
||||
key: 'panel-2',
|
||||
pluginId: 'table',
|
||||
}),
|
||||
}),
|
||||
];
|
||||
|
||||
const manager = new ResponsiveGridLayoutManager({ layout: new SceneCSSGridLayout({ children: gridItems }) });
|
||||
|
||||
new DashboardScene({ body: manager });
|
||||
|
||||
return { manager };
|
||||
}
|
||||
+29
-1
@@ -2,8 +2,14 @@ import { SceneComponentProps, SceneCSSGridLayout, SceneObjectBase, SceneObjectSt
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor';
|
||||
|
||||
import { joinCloneKeys } from '../../utils/clone';
|
||||
import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph';
|
||||
import { getDashboardSceneFor, getGridItemKeyForPanelId, getVizPanelKeyForPanelId } from '../../utils/utils';
|
||||
import {
|
||||
getDashboardSceneFor,
|
||||
getGridItemKeyForPanelId,
|
||||
getPanelIdForVizPanel,
|
||||
getVizPanelKeyForPanelId,
|
||||
} from '../../utils/utils';
|
||||
import { RowsLayoutManager } from '../layout-rows/RowsLayoutManager';
|
||||
import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager';
|
||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||
@@ -115,6 +121,28 @@ export class ResponsiveGridLayoutManager
|
||||
return false;
|
||||
}
|
||||
|
||||
public cloneLayout(ancestorKey: string, isSource: boolean): DashboardLayoutManager {
|
||||
return this.clone({
|
||||
layout: this.state.layout.clone({
|
||||
children: this.state.layout.state.children.map((gridItem) => {
|
||||
if (gridItem instanceof ResponsiveGridItem) {
|
||||
// Get the original panel ID from the gridItem's key
|
||||
const panelId = getPanelIdForVizPanel(gridItem.state.body);
|
||||
const gridItemKey = joinCloneKeys(ancestorKey, getGridItemKeyForPanelId(panelId));
|
||||
|
||||
return gridItem.clone({
|
||||
key: gridItemKey,
|
||||
body: gridItem.state.body.clone({
|
||||
key: joinCloneKeys(gridItemKey, getVizPanelKeyForPanelId(panelId)),
|
||||
}),
|
||||
});
|
||||
}
|
||||
throw new Error('Unexpected child type');
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
public addNewRow() {
|
||||
const shouldAddRow = this.hasVizPanels();
|
||||
const rowsLayout = RowsLayoutManager.createFromLayout(this);
|
||||
|
||||
@@ -77,6 +77,10 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
|
||||
return false;
|
||||
}
|
||||
|
||||
public cloneLayout(ancestorKey: string, isSource: boolean): DashboardLayoutManager {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public addNewRow() {
|
||||
this.setState({ rows: [...this.state.rows, new RowItem()] });
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ export class TabsLayoutManager extends SceneObjectBase<TabsLayoutManagerState> i
|
||||
return panels;
|
||||
}
|
||||
|
||||
public cloneLayout(ancestorKey: string, isSource: boolean): DashboardLayoutManager {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public hasVizPanels(): boolean {
|
||||
for (const tab of this.state.tabs) {
|
||||
if (tab.getLayout().hasVizPanels()) {
|
||||
|
||||
@@ -80,7 +80,7 @@ export interface DashboardLayoutManager<S = {}> extends SceneObject {
|
||||
* @param ancestorKey
|
||||
* @param isSource
|
||||
*/
|
||||
cloneLayout?(ancestorKey: string, isSource: boolean): DashboardLayoutManager;
|
||||
cloneLayout(ancestorKey: string, isSource: boolean): DashboardLayoutManager;
|
||||
}
|
||||
|
||||
export interface LayoutManagerSerializer {
|
||||
|
||||
Reference in New Issue
Block a user