diff --git a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.test.ts b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.test.ts index c1a7172044f..276c82e9d5c 100644 --- a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.test.ts +++ b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.test.ts @@ -1,10 +1,11 @@ import { AppEvents } from '@grafana/data'; -import { SceneGridLayout, SceneQueryRunner, VizPanel } from '@grafana/scenes'; +import { SceneGridLayout, SceneGridRow, SceneQueryRunner, VizPanel } from '@grafana/scenes'; import appEvents from 'app/core/app_events'; import { KioskMode } from 'app/types'; import { DashboardGridItem } from './DashboardGridItem'; import { DashboardScene } from './DashboardScene'; +import { RowRepeaterBehavior } from './RowRepeaterBehavior'; import { DashboardRepeatsProcessedEvent } from './types'; describe('DashboardSceneUrlSync', () => { @@ -108,6 +109,35 @@ describe('DashboardSceneUrlSync', () => { scene.publishEvent(new DashboardRepeatsProcessedEvent({ source: scene })); expect(scene.state.viewPanelScene?.getUrlKey()).toBe('panel-1-clone-1'); }); + + it('should subscribe and update view panel if panel is in a repeated row', () => { + const scene = buildTestScene(); + + // fake adding row panel + const layout = scene.state.body as SceneGridLayout; + layout.setState({ + children: [ + new SceneGridRow({ + $behaviors: [new RowRepeaterBehavior({ variableName: 'test' })], + children: [ + new VizPanel({ + title: 'Panel A', + key: 'panel-1', + pluginId: 'table', + }), + ], + }), + ], + }); + + scene.urlSync?.updateFromUrl({ viewPanel: 'panel-1' }); + + expect(scene.state.viewPanelScene?.getUrlKey()).toBeUndefined(); + + // Verify it subscribes to DashboardRepeatsProcessedEvent + scene.publishEvent(new DashboardRepeatsProcessedEvent({ source: scene })); + expect(scene.state.viewPanelScene?.getUrlKey()).toBe('panel-1'); + }); }); function buildTestScene() { diff --git a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts index 16189017918..a169d05a60f 100644 --- a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts +++ b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts @@ -18,7 +18,13 @@ import { buildPanelEditScene } from '../panel-edit/PanelEditor'; import { createDashboardEditViewFor } from '../settings/utils'; import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer'; import { ShareModal } from '../sharing/ShareModal'; -import { findVizPanelByKey, getDashboardSceneFor, getLibraryPanel, isPanelClone } from '../utils/utils'; +import { + findVizPanelByKey, + getDashboardSceneFor, + getLibraryPanel, + isPanelClone, + isWithinUnactivatedRepeatRow, +} from '../utils/utils'; import { DashboardScene, DashboardSceneState } from './DashboardScene'; import { LibraryVizPanel } from './LibraryVizPanel'; @@ -121,6 +127,11 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { return; } + if (isWithinUnactivatedRepeatRow(panel)) { + this._handleViewRepeatClone(values.viewPanel); + return; + } + update.viewPanelScene = new ViewPanelScene({ panelRef: panel.getRef() }); } else if (viewPanelScene && values.viewPanel === null) { update.viewPanelScene = undefined; diff --git a/public/app/features/dashboard-scene/utils/utils.ts b/public/app/features/dashboard-scene/utils/utils.ts index 105a0844e6d..0da64d3f52a 100644 --- a/public/app/features/dashboard-scene/utils/utils.ts +++ b/public/app/features/dashboard-scene/utils/utils.ts @@ -18,6 +18,7 @@ import { DashboardScene } from '../scene/DashboardScene'; import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks'; import { panelMenuBehavior } from '../scene/PanelMenuBehavior'; +import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; import { RowActions } from '../scene/row-actions/RowActions'; import { dashboardSceneGraph } from './dashboardSceneGraph'; @@ -261,3 +262,22 @@ export function getLibraryPanel(vizPanel: VizPanel): LibraryVizPanel | undefined } return; } + +/** + * If the panel is within a repeated row, it must wait until the row resolves the variables. + * This ensures that the scoped variable for the row is assigned and the panel is initialized with them. + */ +export function isWithinUnactivatedRepeatRow(panel: VizPanel): boolean { + let row; + + try { + row = sceneGraph.getAncestor(panel, SceneGridRow); + } catch (err) { + return false; + } + + const hasBehavior = !!(row.state.$behaviors && row.state.$behaviors.find((b) => b instanceof RowRepeaterBehavior)); + const hasVariables = !!(row.state.$variables && row.state.$variables.state.variables.length > 0); + + return hasBehavior && !hasVariables; +}