Scenes: Remove normal and library panels from layout or rows (#83969)

* Remove normal/lib panels from layout or rows

* refactor
This commit is contained in:
Victor Marin 2024-03-07 10:24:12 +02:00 committed by GitHub
parent 8e827afb8c
commit 20d201ca6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 126 additions and 55 deletions

View File

@ -261,7 +261,7 @@ describe('DashboardScene', () => {
const gridItem = body.state.children[0] as SceneGridItem;
expect(body.state.children.length).toBe(6);
expect(gridItem.state.body!.state.key).toBe('panel-5');
expect(gridItem.state.body!.state.key).toBe('panel-7');
});
it('Should create and add a new row to the dashboard', () => {
@ -271,7 +271,7 @@ describe('DashboardScene', () => {
const gridRow = body.state.children[0] as SceneGridRow;
expect(body.state.children.length).toBe(4);
expect(gridRow.state.key).toBe('panel-5');
expect(gridRow.state.key).toBe('panel-7');
expect(gridRow.state.children[0].state.key).toBe('griditem-1');
expect(gridRow.state.children[1].state.key).toBe('griditem-2');
});
@ -405,7 +405,7 @@ describe('DashboardScene', () => {
expect(buildGridItemForPanel).toHaveBeenCalledTimes(1);
expect(body.state.children.length).toBe(6);
expect(gridItem.state.body!.state.key).toBe('panel-5');
expect(gridItem.state.body!.state.key).toBe('panel-7');
expect(gridItem.state.y).toBe(0);
expect(scene.state.hasCopiedPanel).toBe(false);
});
@ -433,8 +433,8 @@ describe('DashboardScene', () => {
expect(buildGridItemForLibPanel).toHaveBeenCalledTimes(1);
expect(body.state.children.length).toBe(6);
expect(libVizPanel.state.panelKey).toBe('panel-5');
expect(libVizPanel.state.panel?.state.key).toBe('panel-5');
expect(libVizPanel.state.panelKey).toBe('panel-7');
expect(libVizPanel.state.panel?.state.key).toBe('panel-7');
expect(gridItem.state.y).toBe(0);
expect(scene.state.hasCopiedPanel).toBe(false);
});
@ -446,9 +446,50 @@ describe('DashboardScene', () => {
const gridItem = body.state.children[0] as SceneGridItem;
expect(body.state.children.length).toBe(6);
expect(gridItem.state.body!.state.key).toBe('panel-5');
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 SceneGridItem).state.body;
scene.removePanel(vizPanel as VizPanel);
const body = scene.state.body as SceneGridLayout;
expect(body.state.children.length).toBe(4);
});
it('Should remove a panel within a row', () => {
const vizPanel = (
((scene.state.body as SceneGridLayout).state.children[2] as SceneGridRow).state.children[0] as SceneGridItem
).state.body;
scene.removePanel(vizPanel as VizPanel);
const body = scene.state.body as SceneGridLayout;
const gridRow = body.state.children[2] as SceneGridRow;
expect(gridRow.state.children.length).toBe(1);
});
it('Should remove 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.removePanel(vizPanel as VizPanel);
const body = scene.state.body as SceneGridLayout;
expect(body.state.children.length).toBe(4);
});
it('Should remove a library panel within 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.removePanel(vizPanel as VizPanel);
const body = scene.state.body as SceneGridLayout;
const gridRow = body.state.children[2] as SceneGridRow;
expect(gridRow.state.children.length).toBe(1);
});
});
});
@ -639,6 +680,19 @@ function buildTestScene(overrides?: Partial<DashboardSceneState>) {
pluginId: 'table',
}),
}),
new SceneGridItem({
body: new LibraryVizPanel({
uid: 'uid',
name: 'libraryPanel',
panelKey: 'panel-5',
title: 'Library Panel',
panel: new VizPanel({
title: 'Library Panel',
key: 'panel-5',
pluginId: 'table',
}),
}),
}),
],
}),
new SceneGridItem({
@ -653,11 +707,11 @@ function buildTestScene(overrides?: Partial<DashboardSceneState>) {
body: new LibraryVizPanel({
uid: 'uid',
name: 'libraryPanel',
panelKey: 'panel-4',
panelKey: 'panel-6',
title: 'Library Panel',
panel: new VizPanel({
title: 'Library Panel',
key: 'panel-4',
key: 'panel-6',
pluginId: 'table',
}),
}),

View File

@ -560,6 +560,49 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
store.delete(LS_PANEL_COPY_KEY);
}
public removePanel(panel: VizPanel) {
const panels: SceneObject[] = [];
const key = panel.parent instanceof LibraryVizPanel ? panel.parent.parent?.state.key : panel.parent?.state.key;
if (!key) {
return;
}
let row: SceneGridRow | undefined;
try {
row = sceneGraph.getAncestor(panel, SceneGridRow);
} catch {
row = undefined;
}
if (row) {
row.forEachChild((child: SceneObject) => {
if (child.state.key !== key) {
panels.push(child);
}
});
row.setState({ children: panels });
this.state.body.forceRender();
return;
}
this.state.body.forEachChild((child: SceneObject) => {
if (child.state.key !== key) {
panels.push(child);
}
});
const layout = this.state.body;
if (layout instanceof SceneGridLayout || layout instanceof SceneFlexLayout) {
layout.setState({ children: panels });
}
}
public showModal(modal: SceneObject) {
this.setState({ overlay: modal });
}

View File

@ -8,16 +8,7 @@ import {
urlUtil,
} from '@grafana/data';
import { config, getPluginLinkExtensions, locationService } from '@grafana/runtime';
import {
LocalValueVariable,
SceneFlexLayout,
SceneGridLayout,
SceneGridRow,
SceneObject,
VizPanel,
VizPanelMenu,
sceneGraph,
} from '@grafana/scenes';
import { LocalValueVariable, SceneGridRow, VizPanel, VizPanelMenu, sceneGraph } from '@grafana/scenes';
import { DataQuery, OptionsWithLegend } from '@grafana/schema';
import appEvents from 'app/core/app_events';
import { t } from 'app/core/internationalization';
@ -203,7 +194,7 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
iconClassName: 'trash-alt',
onClick: () => {
DashboardInteractions.panelMenuItemClicked('remove');
removePanel(dashboard, panel, true);
onRemovePanel(dashboard, panel);
},
shortcut: 'p r',
});
@ -377,7 +368,7 @@ function createExtensionContext(panel: VizPanel, dashboard: DashboardScene): Plu
};
}
export function removePanel(dashboard: DashboardScene, panel: VizPanel, ask: boolean) {
export function onRemovePanel(dashboard: DashboardScene, panel: VizPanel) {
const vizPanelData = sceneGraph.getData(panel);
let panelHasAlert = false;
@ -385,40 +376,23 @@ export function removePanel(dashboard: DashboardScene, panel: VizPanel, ask: boo
panelHasAlert = true;
}
if (ask !== false) {
const text2 =
panelHasAlert && !config.unifiedAlertingEnabled
? 'Panel includes an alert rule. removing the panel will also remove the alert rule'
: undefined;
const confirmText = panelHasAlert ? 'YES' : undefined;
const text2 =
panelHasAlert && !config.unifiedAlertingEnabled
? 'Panel includes an alert rule. removing the panel will also remove the alert rule'
: undefined;
const confirmText = panelHasAlert ? 'YES' : undefined;
appEvents.publish(
new ShowConfirmModalEvent({
title: 'Remove panel',
text: 'Are you sure you want to remove this panel?',
text2: text2,
icon: 'trash-alt',
confirmText: confirmText,
yesText: 'Remove',
onConfirm: () => removePanel(dashboard, panel, false),
})
);
return;
}
const panels: SceneObject[] = [];
dashboard.state.body.forEachChild((child: SceneObject) => {
if (child.state.key !== panel.parent?.state.key) {
panels.push(child);
}
});
const layout = dashboard.state.body;
if (layout instanceof SceneGridLayout || SceneFlexLayout) {
layout.setState({ children: panels });
}
appEvents.publish(
new ShowConfirmModalEvent({
title: 'Remove panel',
text: 'Are you sure you want to remove this panel?',
text2: text2,
icon: 'trash-alt',
confirmText: confirmText,
yesText: 'Remove',
onConfirm: () => dashboard.removePanel(panel),
})
);
}
const onCreateAlert = async (panel: VizPanel) => {

View File

@ -8,7 +8,7 @@ import { getEditPanelUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPan
import { getPanelIdForVizPanel } from '../utils/utils';
import { DashboardScene } from './DashboardScene';
import { removePanel, toggleVizPanelLegend } from './PanelMenuBehavior';
import { onRemovePanel, toggleVizPanelLegend } from './PanelMenuBehavior';
export function setupKeyboardShortcuts(scene: DashboardScene) {
const keybindings = new KeybindingSet();
@ -120,7 +120,7 @@ export function setupKeyboardShortcuts(scene: DashboardScene) {
keybindings.addBinding({
key: 'p r',
onTrigger: withFocusedPanel(scene, (vizPanel: VizPanel) => {
removePanel(scene, vizPanel, true);
onRemovePanel(scene, vizPanel);
}),
});