DashboardLoaders: Add support for loading library panels (#73406)

* DashboardLoaders: Add support for loading library panels

* Bump scenes

* Scene object for loading library panels

* Remove unused function

* Remove console.log

* Bump scenes

* Review
This commit is contained in:
Dominik Prokop 2023-08-21 17:43:48 +02:00 committed by GitHub
parent 88cdc38afa
commit f00f56bbff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 120 additions and 46 deletions

View File

@ -244,7 +244,7 @@
"@grafana/lezer-traceql": "0.0.4",
"@grafana/monaco-logql": "^0.0.7",
"@grafana/runtime": "workspace:*",
"@grafana/scenes": "0.24.0",
"@grafana/scenes": "0.24.2",
"@grafana/schema": "workspace:*",
"@grafana/ui": "workspace:*",
"@kusto/monaco-kusto": "^7.4.0",

View File

@ -5,14 +5,12 @@ import {
QueryVariableModel,
VariableModel,
} from '@grafana/data';
import { config } from '@grafana/runtime';
import {
VizPanel,
SceneTimePicker,
SceneGridLayout,
SceneGridRow,
SceneTimeRange,
SceneQueryRunner,
SceneVariableSet,
VariableValueSelectors,
SceneVariable,
@ -21,9 +19,7 @@ import {
QueryVariable,
ConstantVariable,
SceneRefreshPicker,
SceneDataTransformer,
SceneGridItem,
SceneDataProvider,
SceneObject,
SceneControlsSpacer,
VizPanelMenu,
@ -32,12 +28,12 @@ import {
import { StateManagerBase } from 'app/core/services/StateManagerBase';
import { dashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/types';
import { DashboardScene } from './DashboardScene';
import { LibraryVizPanel } from './LibraryVizPanel';
import { panelMenuBehavior } from './PanelMenuBehavior';
import { ShareQueryDataProvider } from './ShareQueryDataProvider';
import { getVizPanelKeyForPanelId } from './utils';
import { createPanelDataProvider } from './utils/createPanelDataProvider';
export interface DashboardLoaderState {
dashboard?: DashboardScene;
@ -129,6 +125,18 @@ export function createSceneObjectsForPanels(oldPanels: PanelModel[]): Array<Scen
currentRowPanels = [];
}
}
} else if (panel.libraryPanel?.uid && !('model' in panel.libraryPanel)) {
const gridItem = new SceneGridItem({
body: new LibraryVizPanel({
title: panel.title,
uid: panel.libraryPanel.uid,
}),
y: panel.gridPos.y,
x: panel.gridPos.x,
width: panel.gridPos.w,
height: panel.gridPos.h,
});
panels.push(gridItem);
} else {
const panelObject = createVizPanelFromPanelModel(panel);
@ -191,13 +199,13 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
title: oldModel.title,
uid: oldModel.uid,
body: new SceneGridLayout({
isLazy: true,
children: createSceneObjectsForPanels(oldModel.panels),
}),
$timeRange: new SceneTimeRange(oldModel.time),
$variables: variables,
$behaviors: [
new behaviors.CursorSync({
key: 'dashboard',
sync: oldModel.graphTooltip,
}),
],
@ -294,39 +302,6 @@ export function createVizPanelFromPanelModel(panel: PanelModel) {
});
}
export function createPanelDataProvider(panel: PanelModel): SceneDataProvider | undefined {
// Skip setting query runner for panels without queries
if (!panel.targets?.length) {
return undefined;
}
// Skip setting query runner for panel plugins with skipDataQuery
if (config.panels[panel.type]?.skipDataQuery) {
return undefined;
}
let dataProvider: SceneDataProvider | undefined = undefined;
if (panel.datasource?.uid === SHARED_DASHBOARD_QUERY) {
dataProvider = new ShareQueryDataProvider({ query: panel.targets[0] });
} else {
dataProvider = new SceneQueryRunner({
queries: panel.targets,
maxDataPoints: panel.maxDataPoints ?? undefined,
});
}
// Wrap inner data provider in a data transformer
if (panel.transformations?.length) {
dataProvider = new SceneDataTransformer({
$data: dataProvider,
transformations: panel.transformations,
});
}
return dataProvider;
}
let loader: DashboardLoader | null = null;
export function getDashboardLoader(): DashboardLoader {

View File

@ -0,0 +1,60 @@
import React from 'react';
import { SceneComponentProps, SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes';
import { PanelModel } from 'app/features/dashboard/state';
import { getLibraryPanel } from 'app/features/library-panels/state/api';
import { createPanelDataProvider } from './utils/createPanelDataProvider';
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;
panel?: VizPanel;
}
export class LibraryVizPanel extends SceneObjectBase<LibraryVizPanelState> {
static Component = LibraryPanelRenderer;
constructor({ uid, title }: Pick<LibraryVizPanelState, 'uid' | 'title'>) {
super({ uid, title });
this.addActivationHandler(this._onActivate);
}
private _onActivate = () => {
this.loadLibraryPanelFromPanelModel();
};
private async loadLibraryPanelFromPanelModel() {
const { title } = this.state;
let vizPanel = new VizPanel({ title });
try {
const libPanel = await getLibraryPanel(this.state.uid, true);
const libPanelModel = new PanelModel(libPanel.model);
vizPanel.setState({
options: libPanelModel.options ?? {},
fieldConfig: libPanelModel.fieldConfig,
pluginVersion: libPanelModel.pluginVersion,
displayMode: libPanelModel.transparent ? 'transparent' : undefined,
$data: createPanelDataProvider(libPanelModel),
});
} catch (err) {
vizPanel.setState({
pluginLoadError: 'Unable to load library panel: ' + this.state.uid,
});
}
this.setState({ panel: vizPanel });
}
}
function LibraryPanelRenderer({ model }: SceneComponentProps<LibraryVizPanel>) {
const { panel } = model.useState();
if (!panel) {
return null;
}
return <panel.Component model={panel} />;
}

View File

@ -0,0 +1,39 @@
import { config } from '@grafana/runtime';
import { SceneDataProvider, SceneDataTransformer, SceneQueryRunner } from '@grafana/scenes';
import { PanelModel } from 'app/features/dashboard/state';
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard';
import { ShareQueryDataProvider } from '../ShareQueryDataProvider';
export function createPanelDataProvider(panel: PanelModel): SceneDataProvider | undefined {
// Skip setting query runner for panels without queries
if (!panel.targets?.length) {
return undefined;
}
// Skip setting query runner for panel plugins with skipDataQuery
if (config.panels[panel.type]?.skipDataQuery) {
return undefined;
}
let dataProvider: SceneDataProvider | undefined = undefined;
if (panel.datasource?.uid === SHARED_DASHBOARD_QUERY) {
dataProvider = new ShareQueryDataProvider({ query: panel.targets[0] });
} else {
dataProvider = new SceneQueryRunner({
queries: panel.targets,
maxDataPoints: panel.maxDataPoints ?? undefined,
});
}
// Wrap inner data provider in a data transformer
if (panel.transformations?.length) {
dataProvider = new SceneDataTransformer({
$data: dataProvider,
transformations: panel.transformations,
});
}
return dataProvider;
}

View File

@ -3929,9 +3929,9 @@ __metadata:
languageName: unknown
linkType: soft
"@grafana/scenes@npm:0.24.0":
version: 0.24.0
resolution: "@grafana/scenes@npm:0.24.0"
"@grafana/scenes@npm:0.24.2":
version: 0.24.2
resolution: "@grafana/scenes@npm:0.24.2"
dependencies:
"@grafana/e2e-selectors": 10.0.2
react-grid-layout: 1.3.4
@ -3943,7 +3943,7 @@ __metadata:
"@grafana/runtime": 10.0.3
"@grafana/schema": 10.0.3
"@grafana/ui": 10.0.3
checksum: b9cfc535228f0426ee4e422d3e8872f56212f9785acfcdb86aefe6e9c2ff5c98ef750eef0d72cd0ebc2feb67719fac86877b2eae6d376bdd45691da9068b5194
checksum: 26797a4c90ff3b6d2e80f3da1d3be9721a371d9d9569e443dad14c81eba8413ef3fff78ab85602f9bfe3e243d7555a834134ac0c1c8542c23b9cc572bd40ad0a
languageName: node
linkType: hard
@ -19248,7 +19248,7 @@ __metadata:
"@grafana/lezer-traceql": 0.0.4
"@grafana/monaco-logql": ^0.0.7
"@grafana/runtime": "workspace:*"
"@grafana/scenes": 0.24.0
"@grafana/scenes": 0.24.2
"@grafana/schema": "workspace:*"
"@grafana/toolkit": "workspace:*"
"@grafana/tsconfig": ^1.3.0-rc1