[v11.0.x] Scenes/Dashboard: Fix issue where changes to panel in row weren't detected in panel edit (#87293)

Dashboard: Fix issue where changes to panel in row weren't detected in panel edit (#87195)

Closes #87171

(cherry picked from commit b3f173f153)

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>
pull/87297/head
grafana-delivery-bot[bot] 1 year ago committed by GitHub
parent 31e116f3d8
commit 2eeab8c02a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 103
      public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts
  2. 80
      public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts

@ -16,7 +16,7 @@ import {
} from '@grafana/data';
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
import { getPluginLinkExtensions, setPluginImportUtils } from '@grafana/runtime';
import { MultiValueVariable, SceneGridLayout, SceneGridRow, VizPanel } from '@grafana/scenes';
import { MultiValueVariable, SceneGridLayout, SceneGridRow, SceneTimeRange, VizPanel } from '@grafana/scenes';
import { Dashboard, LoadingState, Panel, RowPanel, VariableRefresh } from '@grafana/schema';
import { PanelModel } from 'app/features/dashboard/state';
import { getTimeRange } from 'app/features/dashboard/utils/timeRange';
@ -26,11 +26,12 @@ import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard';
import { buildPanelEditScene } from '../panel-edit/PanelEditor';
import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet';
import { DashboardGridItem } from '../scene/DashboardGridItem';
import { DashboardScene } from '../scene/DashboardScene';
import { LibraryVizPanel } from '../scene/LibraryVizPanel';
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
import { NEW_LINK } from '../settings/links/utils';
import { activateFullSceneTree, buildPanelRepeaterScene } from '../utils/test-utils';
import { findVizPanelByKey, getVizPanelKeyForPanelId } from '../utils/utils';
import { getVizPanelKeyForPanelId } from '../utils/utils';
import { GRAFANA_DATASOURCE_REF } from './const';
import dashboard_to_load1 from './testfiles/dashboard_to_load1.json';
@ -788,7 +789,7 @@ describe('transformSceneToSaveModel', () => {
activateFullSceneTree(scene);
expect(repeater.state.repeatedPanels?.length).toBe(2);
const result = panelRepeaterToPanels(repeater, true);
const result = panelRepeaterToPanels(repeater, undefined, true);
expect(result).toHaveLength(2);
@ -844,7 +845,7 @@ describe('transformSceneToSaveModel', () => {
);
activateFullSceneTree(scene);
const result = panelRepeaterToPanels(repeater, true);
const result = panelRepeaterToPanels(repeater, undefined, true);
expect(result).toHaveLength(1);
@ -869,7 +870,7 @@ describe('transformSceneToSaveModel', () => {
activateFullSceneTree(scene);
let panels: Panel[] = [];
gridRowToSaveModel(row, panels, true);
gridRowToSaveModel(row, panels, undefined, true);
expect(panels).toHaveLength(2);
expect(panels[0].repeat).toBe('handler');
@ -897,7 +898,7 @@ describe('transformSceneToSaveModel', () => {
activateFullSceneTree(scene);
let panels: Panel[] = [];
gridRowToSaveModel(row, panels, true);
gridRowToSaveModel(row, panels, undefined, true);
expect(panels[0].repeat).toBe('handler');
@ -1009,21 +1010,87 @@ describe('transformSceneToSaveModel', () => {
describe('Given a scene with an open panel editor', () => {
it('should persist changes to panel model', async () => {
const scene = transformSaveModelToScene({ dashboard: repeatingRowsAndPanelsDashboardJson as any, meta: {} });
const panel = new VizPanel({
key: 'panel-1',
pluginId: 'text',
});
const gridItem = new DashboardGridItem({ body: panel });
const editScene = buildPanelEditScene(panel);
const scene = new DashboardScene({
editPanel: editScene,
isEditing: true,
body: new SceneGridLayout({
children: [gridItem],
}),
$timeRange: new SceneTimeRange({
from: 'now-6h',
to: 'now',
timeZone: '',
}),
});
editScene!.state.vizManager.state.panel.setState({
options: {
mode: 'markdown',
code: {
language: 'plaintext',
showLineNumbers: false,
showMiniMap: false,
},
content: 'new content',
},
});
activateFullSceneTree(scene);
await new Promise((r) => setTimeout(r, 1));
scene.onEnterEditMode();
const panel = findVizPanelByKey(scene, '15')!;
scene.setState({ editPanel: buildPanelEditScene(panel) });
panel.onOptionsChange({
mode: 'markdown',
code: {
language: 'plaintext',
showLineNumbers: false,
showMiniMap: false,
const saveModel = transformSceneToSaveModel(scene);
expect((saveModel.panels![0] as any).options.content).toBe('new content');
});
it('should persist changes to panel model in row', async () => {
const panel = new VizPanel({
key: 'panel-1',
pluginId: 'text',
options: {
content: 'old content',
},
});
const gridItem = new DashboardGridItem({ body: panel });
const editScene = buildPanelEditScene(panel);
const scene = new DashboardScene({
editPanel: editScene,
isEditing: true,
body: new SceneGridLayout({
children: [
new SceneGridRow({
key: '23',
isCollapsed: false,
children: [gridItem],
}),
],
}),
$timeRange: new SceneTimeRange({
from: 'now-6h',
to: 'now',
timeZone: '',
}),
});
activateFullSceneTree(scene);
editScene!.state.vizManager.state.panel.setState({
options: {
mode: 'markdown',
code: {
language: 'plaintext',
showLineNumbers: false,
showMiniMap: false,
},
content: 'new content',
},
content: 'new content',
});
const saveModel = transformSceneToSaveModel(scene);
expect((saveModel.panels![1] as any).options.content).toBe('new content');
});

@ -33,7 +33,7 @@ import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types';
import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet';
import { DashboardGridItem } from '../scene/DashboardGridItem';
import { DashboardScene } from '../scene/DashboardScene';
import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene';
import { LibraryVizPanel } from '../scene/LibraryVizPanel';
import { PanelTimeRange } from '../scene/PanelTimeRange';
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
@ -57,25 +57,11 @@ export function transformSceneToSaveModel(scene: DashboardScene, isSnapshot = fa
if (body instanceof SceneGridLayout) {
for (const child of body.state.children) {
if (child instanceof DashboardGridItem) {
let child_ = child;
// If we're saving while the panel editor is open, we need to persist those changes in the panel model
if (
child.state.body instanceof VizPanel &&
state.editPanel?.state.vizManager &&
state.editPanel.state.vizManager.state.sourcePanel.resolve() === child.state.body
) {
const childClone = child.clone();
if (childClone.state.body instanceof VizPanel) {
state.editPanel.state.vizManager.commitChangesTo(childClone.state.body);
child_ = childClone;
}
}
// handle panel repeater scenario
if (child_.state.variableName) {
panels = panels.concat(panelRepeaterToPanels(child_, isSnapshot));
if (child.state.variableName) {
panels = panels.concat(panelRepeaterToPanels(child, state, isSnapshot));
} else {
panels.push(gridItemToPanel(child_, isSnapshot));
panels.push(gridItemToPanel(child, state, isSnapshot));
}
}
@ -84,7 +70,7 @@ export function transformSceneToSaveModel(scene: DashboardScene, isSnapshot = fa
if (child.state.key!.indexOf('-clone-') > 0 && !isSnapshot) {
continue;
}
gridRowToSaveModel(child, panels, isSnapshot);
gridRowToSaveModel(child, panels, state, isSnapshot);
}
}
}
@ -169,7 +155,11 @@ export function libraryVizPanelToPanel(libPanel: LibraryVizPanel, gridPos: GridP
} as Panel;
}
export function gridItemToPanel(gridItem: DashboardGridItem, isSnapshot = false): Panel {
export function gridItemToPanel(
gridItem: DashboardGridItem,
sceneState?: DashboardSceneState,
isSnapshot = false
): Panel {
let vizPanel: VizPanel | undefined;
let x = 0,
y = 0,
@ -186,21 +176,36 @@ export function gridItemToPanel(gridItem: DashboardGridItem, isSnapshot = false)
return libraryVizPanelToPanel(gridItem.state.body, { x, y, w, h });
}
if (!(gridItem.state.body instanceof VizPanel)) {
let gridItem_ = gridItem;
// If we're saving while the panel editor is open, we need to persist those changes in the panel model
if (
sceneState &&
sceneState.editPanel?.state.vizManager &&
sceneState.editPanel.state.vizManager.state.sourcePanel.resolve() === gridItem.state.body
) {
const gridItemClone = gridItem.clone();
if (gridItemClone.state.body instanceof VizPanel) {
sceneState.editPanel.state.vizManager.commitChangesTo(gridItemClone.state.body);
gridItem_ = gridItemClone;
}
}
if (!(gridItem_.state.body instanceof VizPanel)) {
throw new Error('DashboardGridItem body expected to be VizPanel');
}
vizPanel = gridItem.state.body;
x = gridItem.state.x ?? 0;
y = gridItem.state.y ?? 0;
w = gridItem.state.width ?? 0;
h = gridItem.state.height ?? 0;
vizPanel = gridItem_.state.body;
x = gridItem_.state.x ?? 0;
y = gridItem_.state.y ?? 0;
w = gridItem_.state.width ?? 0;
h = gridItem_.state.height ?? 0;
if (!vizPanel) {
throw new Error('Unsupported grid item type');
}
const panel: Panel = vizPanelToPanel(vizPanel, { x, y, h, w }, isSnapshot, gridItem);
const panel: Panel = vizPanelToPanel(vizPanel, { x, y, h, w }, isSnapshot, gridItem_);
return panel;
}
@ -328,9 +333,13 @@ function vizPanelDataToPanel(
return panel;
}
export function panelRepeaterToPanels(repeater: DashboardGridItem, isSnapshot = false): Panel[] {
export function panelRepeaterToPanels(
repeater: DashboardGridItem,
sceneState?: DashboardSceneState,
isSnapshot = false
): Panel[] {
if (!isSnapshot) {
return [gridItemToPanel(repeater)];
return [gridItemToPanel(repeater, sceneState)];
} else {
if (repeater.state.body instanceof LibraryVizPanel) {
const { x = 0, y = 0, width: w = 0, height: h = 0 } = repeater.state;
@ -387,7 +396,12 @@ export function panelRepeaterToPanels(repeater: DashboardGridItem, isSnapshot =
}
}
export function gridRowToSaveModel(gridRow: SceneGridRow, panelsArray: Array<Panel | RowPanel>, isSnapshot = false) {
export function gridRowToSaveModel(
gridRow: SceneGridRow,
panelsArray: Array<Panel | RowPanel>,
sceneState?: DashboardSceneState,
isSnapshot = false
) {
const collapsed = Boolean(gridRow.state.isCollapsed);
const rowPanel: RowPanel = {
type: 'row',
@ -437,10 +451,10 @@ export function gridRowToSaveModel(gridRow: SceneGridRow, panelsArray: Array<Pan
if (c instanceof DashboardGridItem) {
if (c.state.variableName) {
// Perform snapshot only for uncollapsed rows
panelsInsideRow = panelsInsideRow.concat(panelRepeaterToPanels(c, !collapsed));
panelsInsideRow = panelsInsideRow.concat(panelRepeaterToPanels(c, sceneState, !collapsed));
} else {
// Perform snapshot only for uncollapsed panels
panelsInsideRow.push(gridItemToPanel(c, !collapsed));
panelsInsideRow.push(gridItemToPanel(c, sceneState, !collapsed));
}
}
});
@ -449,7 +463,7 @@ export function gridRowToSaveModel(gridRow: SceneGridRow, panelsArray: Array<Pan
if (!(c instanceof DashboardGridItem)) {
throw new Error('Row child expected to be DashboardGridItem');
}
return gridItemToPanel(c);
return gridItemToPanel(c, sceneState);
});
}

Loading…
Cancel
Save