Skip to content

Commit

Permalink
[v11.0.x] DashboardDataSourceBehaviour: Handle loading library panel (#…
Browse files Browse the repository at this point in the history
…87023)

DashboardDataSourceBehaviour: Handle loading library panel (#86980)

* DashboardDataSourceBehaviour: Handle loading library panel

* Remove timeout

* FIx test

(cherry picked from commit 67968df)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
  • Loading branch information
grafana-delivery-bot[bot] and dprokop committed Apr 29, 2024
1 parent 5749f36 commit c5f186f
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 7 deletions.
Expand Up @@ -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,
Expand Down Expand Up @@ -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() {
Expand Down
@@ -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 {}

Expand All @@ -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');
Expand Down Expand Up @@ -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();
}
}
}
Expand Up @@ -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;
Expand Down

0 comments on commit c5f186f

Please sign in to comment.