diff --git a/public/app/features/dashboard-scene/scene/DashboardGridItem.test.tsx b/public/app/features/dashboard-scene/scene/DashboardGridItem.test.tsx index b09427095b3..e6d202b0801 100644 --- a/public/app/features/dashboard-scene/scene/DashboardGridItem.test.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardGridItem.test.tsx @@ -1,11 +1,18 @@ import { VariableRefresh } from '@grafana/data'; import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks'; import { setPluginImportUtils } from '@grafana/runtime'; -import { SceneGridLayout, VizPanel } from '@grafana/scenes'; +import { SceneGridLayout, SceneVariableSet, TestVariable, VizPanel } from '@grafana/scenes'; +import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from 'app/features/variables/constants'; import { activateFullSceneTree, buildPanelRepeaterScene } from '../utils/test-utils'; import { DashboardGridItem, DashboardGridItemState } from './DashboardGridItem'; +import { DashboardScene } from './DashboardScene'; + +jest.mock('@grafana/runtime', () => ({ + ...jest.requireActual('@grafana/runtime'), + getPluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }), +})); setPluginImportUtils({ importPanelPlugin: (id: string) => Promise.resolve(getPanelPlugin({})), @@ -85,6 +92,142 @@ describe('PanelRepeaterGridItem', () => { expect(repeater.state.repeatedPanels?.length).toBe(1); }); + it('Should redo the repeat when editing panel and then returning to dashboard', async () => { + const panel = new DashboardGridItem({ + variableName: 'server', + repeatedPanels: [], + body: new VizPanel({ + title: 'Panel $server', + }), + }); + + const variable = new TestVariable({ + name: 'server', + query: 'A.*', + value: ALL_VARIABLE_VALUE, + text: ALL_VARIABLE_TEXT, + isMulti: true, + includeAll: true, + delayMs: 0, + optionsToReturn: [ + { label: 'A', value: '1' }, + { label: 'B', value: '2' }, + { label: 'C', value: '3' }, + { label: 'D', value: '4' }, + { label: 'E', value: '5' }, + ], + }); + + const scene = new DashboardScene({ + $variables: new SceneVariableSet({ + variables: [variable], + }), + body: new SceneGridLayout({ + children: [panel], + }), + }); + + const deactivate = activateFullSceneTree(scene); + + await new Promise((r) => setTimeout(r, 10)); + + expect(panel.state.repeatedPanels?.length).toBe(5); + + const vizPanel = panel.state.body as VizPanel; + + expect(vizPanel.state.title).toBe('Panel $server'); + + // mimic going to panel edit + deactivate(); + + await new Promise((r) => setTimeout(r, 10)); + + vizPanel.setState({ title: 'Changed' }); + //mimic returning to dashboard from panel edit cloning panel + panel.setState({ body: vizPanel.clone() }); + + // mimic returning to dashboard + activateFullSceneTree(scene); + + await new Promise((r) => setTimeout(r, 10)); + + expect(panel.state.repeatedPanels?.length).toBe(5); + expect((panel.state.repeatedPanels![0] as VizPanel).state.title).toBe('Changed'); + }); + + it('Should only redo the repeat of an edited panel, not all panels in dashboard', async () => { + const panel = new DashboardGridItem({ + variableName: 'server', + repeatedPanels: [], + body: new VizPanel({ + title: 'Panel $server', + }), + }); + + const panel2 = new DashboardGridItem({ + variableName: 'server', + repeatedPanels: [], + body: new VizPanel({ + title: 'Panel $server 2', + }), + }); + + const variable = new TestVariable({ + name: 'server', + query: 'A.*', + value: ALL_VARIABLE_VALUE, + text: ALL_VARIABLE_TEXT, + isMulti: true, + includeAll: true, + delayMs: 0, + optionsToReturn: [ + { label: 'A', value: '1' }, + { label: 'B', value: '2' }, + { label: 'C', value: '3' }, + { label: 'D', value: '4' }, + { label: 'E', value: '5' }, + ], + }); + + const scene = new DashboardScene({ + $variables: new SceneVariableSet({ + variables: [variable], + }), + body: new SceneGridLayout({ + children: [panel, panel2], + }), + }); + + const deactivate = activateFullSceneTree(scene); + + await new Promise((r) => setTimeout(r, 10)); + + expect(panel.state.repeatedPanels?.length).toBe(5); + + const vizPanel = panel.state.body as VizPanel; + + expect(vizPanel.state.title).toBe('Panel $server'); + + // mimic going to panel edit + deactivate(); + + await new Promise((r) => setTimeout(r, 10)); + + vizPanel.setState({ title: 'Changed' }); + //mimic returning to dashboard from panel edit cloning panel + panel.setState({ body: vizPanel.clone() }); + + const performRepeatMock = jest.spyOn(panel, 'performRepeat'); + // mimic returning to dashboard + activateFullSceneTree(scene); + + await new Promise((r) => setTimeout(r, 10)); + + expect(performRepeatMock).toHaveBeenCalledTimes(1); // only for the edited panel + expect(panel.state.repeatedPanels?.length).toBe(5); + expect((panel.state.repeatedPanels![0] as VizPanel).state.title).toBe('Changed'); + }); + it('Should display a panel when there are variable errors', () => { const { scene, repeater } = buildPanelRepeaterScene({ variableQueryTime: 0, diff --git a/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx b/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx index 670a57b9f98..cfdbaea407c 100644 --- a/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx @@ -45,6 +45,7 @@ export type RepeatDirection = 'v' | 'h'; export class DashboardGridItem extends SceneObjectBase implements SceneGridItemLike { private _libPanelSubscription: Unsubscribable | undefined; private _prevRepeatValues?: VariableValueSingle[]; + private _oldBody?: VizPanel | LibraryVizPanel | AddLibraryPanelDrawer; protected _variableDependency = new DashboardGridItemVariableDependencyHandler(this); @@ -57,6 +58,11 @@ export class DashboardGridItem extends SceneObjectBase i private _activationHandler() { if (this.state.variableName) { this._subs.add(this.subscribeToState((newState, prevState) => this._handleGridResize(newState, prevState))); + if (this._oldBody !== this.state.body) { + this._prevRepeatValues = undefined; + } + + this._oldBody = this.state.body; this.performRepeat(); }