[v11.0.x] DashboardDataSourceBehaviour: Handle loading library panel (#87023)

DashboardDataSourceBehaviour: Handle loading library panel (#86980)

* DashboardDataSourceBehaviour: Handle loading library panel

* Remove timeout

* FIx test

(cherry picked from commit 67968df70e)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
grafana-delivery-bot[bot]
2024-04-29 11:12:20 +02:00
committed by GitHub
co-authored by Dominik Prokop
parent 5749f36f52
commit c5f186fa22
3 changed files with 116 additions and 7 deletions
@@ -21,6 +21,7 @@ import { activateFullSceneTree } from '../utils/test-utils';
import { DashboardDatasourceBehaviour } from './DashboardDatasourceBehaviour';
import { DashboardGridItem } from './DashboardGridItem';
import { DashboardScene } from './DashboardScene';
import { LibraryVizPanel } from './LibraryVizPanel';
const grafanaDs = {
id: 1,
@@ -487,6 +488,87 @@ describe('DashboardDatasourceBehaviour', () => {
}
});
});
describe('Library panels', () => {
it('should wait for library panel to be loaded', async () => {
const sourcePanel = new LibraryVizPanel({
name: 'My Library Panel',
title: 'Panel title',
uid: 'fdcvggvfy2qdca',
panelKey: 'lib-panel',
panel: new VizPanel({
key: 'panel-1',
title: 'Panel A',
pluginId: 'table',
}),
});
// query references inexistent panel
const dashboardDSPanel = new VizPanel({
title: 'Panel B',
pluginId: 'table',
key: 'panel-2',
$data: new SceneQueryRunner({
datasource: { uid: SHARED_DASHBOARD_QUERY },
queries: [{ refId: 'A', panelId: 1 }],
$behaviors: [new DashboardDatasourceBehaviour({})],
}),
});
const scene = new DashboardScene({
title: 'hello',
uid: 'dash-1',
meta: {
canEdit: true,
},
body: new SceneGridLayout({
children: [
new DashboardGridItem({
key: 'griditem-1',
x: 0,
y: 0,
width: 10,
height: 12,
body: sourcePanel,
}),
new DashboardGridItem({
key: 'griditem-2',
x: 0,
y: 0,
width: 10,
height: 12,
body: dashboardDSPanel,
}),
],
}),
});
activateFullSceneTree(scene);
// spy on runQueries
const spy = jest.spyOn(dashboardDSPanel.state.$data as SceneQueryRunner, 'runQueries');
await new Promise((r) => setTimeout(r, 1));
expect(spy).not.toHaveBeenCalled();
// Simulate library panel being loaded
sourcePanel.setState({
isLoaded: true,
panel: new VizPanel({
title: 'Panel A',
pluginId: 'table',
key: 'panel-1',
$data: new SceneQueryRunner({
datasource: { uid: 'grafana' },
queries: [{ refId: 'A', queryType: 'randomWalk' }],
}),
}),
});
expect(spy).toHaveBeenCalledTimes(1);
});
});
});
async function buildTestScene() {
@@ -1,9 +1,12 @@
import { Unsubscribable } from 'rxjs';
import { SceneObjectBase, SceneObjectState, SceneQueryRunner, VizPanel } from '@grafana/scenes';
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard';
import { findVizPanelByKey, getDashboardSceneFor, getQueryRunnerFor, getVizPanelKeyForPanelId } from '../utils/utils';
import { DashboardScene } from './DashboardScene';
import { LibraryVizPanel, LibraryVizPanelState } from './LibraryVizPanel';
interface DashboardDatasourceBehaviourState extends SceneObjectState {}
@@ -18,6 +21,7 @@ export class DashboardDatasourceBehaviour extends SceneObjectBase<DashboardDatas
private _activationHandler() {
const queryRunner = this.parent;
let dashboard: DashboardScene;
let libraryPanelSub: Unsubscribable;
if (!(queryRunner instanceof SceneQueryRunner)) {
throw new Error('DashboardDatasourceBehaviour must be attached to a SceneQueryRunner');
@@ -50,15 +54,38 @@ export class DashboardDatasourceBehaviour extends SceneObjectBase<DashboardDatas
const sourcePanelQueryRunner = getQueryRunnerFor(panel);
if (!(sourcePanelQueryRunner instanceof SceneQueryRunner)) {
throw new Error('Could not find SceneQueryRunner for panel');
}
if (this.prevRequestId && this.prevRequestId !== sourcePanelQueryRunner.state.data?.request?.requestId) {
queryRunner.runQueries();
if (!(panel.parent instanceof LibraryVizPanel)) {
throw new Error('Could not find SceneQueryRunner for panel');
} else {
// Library panels load and create internal viz panel asynchroniously. Here we are subscribing to
// library panel state, and run dashboard queries when the source panel query runner is ready.
libraryPanelSub = panel.parent.subscribeToState((n, p) => {
this.handleLibPanelStateUpdates(n, p, queryRunner);
});
}
} else {
if (this.prevRequestId && this.prevRequestId !== sourcePanelQueryRunner.state.data?.request?.requestId) {
queryRunner.runQueries();
}
}
return () => {
this.prevRequestId = sourcePanelQueryRunner.state.data?.request?.requestId;
this.prevRequestId = sourcePanelQueryRunner?.state.data?.request?.requestId;
if (libraryPanelSub) {
libraryPanelSub.unsubscribe();
}
};
}
private handleLibPanelStateUpdates(n: LibraryVizPanelState, p: LibraryVizPanelState, queryRunner: SceneQueryRunner) {
if (n.panel && n.panel !== p.panel) {
const libPanelQueryRunner = getQueryRunnerFor(n.panel);
if (!(libPanelQueryRunner instanceof SceneQueryRunner)) {
throw new Error('Could not find SceneQueryRunner for panel');
}
queryRunner.runQueries();
}
}
}
@@ -20,7 +20,7 @@ import { VizPanelLinks, VizPanelLinksMenu } from './PanelLinks';
import { panelLinksBehavior, panelMenuBehavior } from './PanelMenuBehavior';
import { PanelNotices } from './PanelNotices';
interface LibraryVizPanelState extends SceneObjectState {
export interface LibraryVizPanelState extends SceneObjectState {
// Library panels use title from dashboard JSON's panel model, not from library panel definition, hence we pass it.
title: string;
uid: string;