mirror of https://github.com/grafana/grafana.git
DashboardScene serialization: Handle variables and library panels (#76117)
* DashboardScene serialization: Library panels * DashboardScene serialization: Variables * TS fix * Review * betetrer -u
This commit is contained in:
parent
1a96f3742f
commit
2cfbe087fc
|
|
@ -3020,7 +3020,8 @@ exports[`better eslint`] = {
|
|||
],
|
||||
"public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/utils/test-utils.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
|
|
|
|||
|
|
@ -10,14 +10,15 @@ 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;
|
||||
name: string;
|
||||
panel?: VizPanel;
|
||||
}
|
||||
|
||||
export class LibraryVizPanel extends SceneObjectBase<LibraryVizPanelState> {
|
||||
static Component = LibraryPanelRenderer;
|
||||
|
||||
constructor({ uid, title }: Pick<LibraryVizPanelState, 'uid' | 'title'>) {
|
||||
super({ uid, title });
|
||||
constructor({ uid, title, key, name }: Pick<LibraryVizPanelState, 'uid' | 'title' | 'key' | 'name'>) {
|
||||
super({ uid, title, key, name });
|
||||
|
||||
this.addActivationHandler(this._onActivate);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,6 +191,48 @@ exports[`transformSceneToSaveModel Given a scene with rows Should transform back
|
|||
],
|
||||
"schemaVersion": 36,
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"text": [
|
||||
"A",
|
||||
"B",
|
||||
],
|
||||
"value": [
|
||||
"A",
|
||||
"B",
|
||||
],
|
||||
},
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"name": "server",
|
||||
"options": [],
|
||||
"query": "A,B,C,D,E,F,E,G,H,I,J,K,L",
|
||||
"skipUrlSync": false,
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": [
|
||||
"1",
|
||||
"2",
|
||||
],
|
||||
"value": [
|
||||
"1",
|
||||
"2",
|
||||
],
|
||||
},
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"name": "pod",
|
||||
"options": [],
|
||||
"query": "Bob : 1, Rob : 2,Sod : 3, Hod : 4, Cod : 5",
|
||||
"skipUrlSync": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now",
|
||||
|
|
@ -405,6 +447,9 @@ exports[`transformSceneToSaveModel Given a simple scene Should transform back to
|
|||
],
|
||||
"schemaVersion": 36,
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": [],
|
||||
},
|
||||
"time": {
|
||||
"from": "now-5m",
|
||||
"to": "now",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,258 @@
|
|||
import { of } from 'rxjs';
|
||||
|
||||
import {
|
||||
DataSourceApi,
|
||||
FieldType,
|
||||
getDefaultTimeRange,
|
||||
LoadingState,
|
||||
PanelData,
|
||||
PluginType,
|
||||
ScopedVars,
|
||||
toDataFrame,
|
||||
VariableSupportType,
|
||||
} from '@grafana/data';
|
||||
import { setRunRequest } from '@grafana/runtime';
|
||||
import { ConstantVariable, CustomVariable, DataSourceVariable, QueryVariable, SceneVariableSet } from '@grafana/scenes';
|
||||
import { DataSourceRef } from '@grafana/schema';
|
||||
|
||||
import { sceneVariablesSetToVariables } from './sceneVariablesSetToVariables';
|
||||
|
||||
const runRequestMock = jest.fn().mockReturnValue(
|
||||
of<PanelData>({
|
||||
state: LoadingState.Done,
|
||||
series: [
|
||||
toDataFrame({
|
||||
fields: [{ name: 'text', type: FieldType.string, values: ['val1', 'val2', 'val11'] }],
|
||||
}),
|
||||
],
|
||||
timeRange: getDefaultTimeRange(),
|
||||
})
|
||||
);
|
||||
|
||||
setRunRequest(runRequestMock);
|
||||
|
||||
const getDataSourceMock = jest.fn();
|
||||
|
||||
const fakeDsMock: DataSourceApi = {
|
||||
name: 'fake-std',
|
||||
type: 'fake-std',
|
||||
getRef: () => ({ type: 'fake-std', uid: 'fake-std' }),
|
||||
query: () =>
|
||||
Promise.resolve({
|
||||
data: [],
|
||||
}),
|
||||
testDatasource: () => Promise.resolve({ status: 'success', message: 'abc' }),
|
||||
meta: {
|
||||
id: 'fake-std',
|
||||
type: PluginType.datasource,
|
||||
module: 'fake-std',
|
||||
baseUrl: '',
|
||||
name: 'fake-std',
|
||||
info: {
|
||||
author: { name: '' },
|
||||
description: '',
|
||||
links: [],
|
||||
logos: { large: '', small: '' },
|
||||
updated: '',
|
||||
version: '',
|
||||
screenshots: [],
|
||||
},
|
||||
},
|
||||
// Standard variable support
|
||||
variables: {
|
||||
getType: () => VariableSupportType.Standard,
|
||||
toDataQuery: (q) => ({ ...q, refId: 'FakeDataSource-refId' }),
|
||||
},
|
||||
id: 1,
|
||||
uid: 'fake-std',
|
||||
};
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getDataSourceSrv: () => ({
|
||||
get: (ds: DataSourceRef, vars: ScopedVars): Promise<DataSourceApi> => {
|
||||
getDataSourceMock(ds, vars);
|
||||
return Promise.resolve(fakeDsMock);
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('sceneVariablesSetToVariables', () => {
|
||||
it('should handle QueryVariable', () => {
|
||||
const variable = new QueryVariable({
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
value: ['selected-value'],
|
||||
text: ['selected-value-text'],
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
query: 'query',
|
||||
includeAll: true,
|
||||
allValue: 'test-all',
|
||||
isMulti: true,
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToVariables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"allValue": "test-all",
|
||||
"current": {
|
||||
"text": [
|
||||
"selected-value-text",
|
||||
],
|
||||
"value": [
|
||||
"selected-value",
|
||||
],
|
||||
},
|
||||
"datasource": {
|
||||
"type": "fake-std",
|
||||
"uid": "fake-std",
|
||||
},
|
||||
"description": "test-desc",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "test-label",
|
||||
"multi": true,
|
||||
"name": "test",
|
||||
"options": [],
|
||||
"query": "query",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 1,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle DatasourceVariable', () => {
|
||||
const variable = new DataSourceVariable({
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
value: ['selected-ds-1', 'selected-ds-2'],
|
||||
text: ['selected-ds-1-text', 'selected-ds-2-text'],
|
||||
pluginId: 'fake-std',
|
||||
includeAll: true,
|
||||
allValue: 'test-all',
|
||||
isMulti: true,
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToVariables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"allValue": "test-all",
|
||||
"current": {
|
||||
"text": [
|
||||
"selected-ds-1-text",
|
||||
"selected-ds-2-text",
|
||||
],
|
||||
"value": [
|
||||
"selected-ds-1",
|
||||
"selected-ds-2",
|
||||
],
|
||||
},
|
||||
"description": "test-desc",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "test-label",
|
||||
"multi": true,
|
||||
"name": "test",
|
||||
"options": [],
|
||||
"query": "fake-std",
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle CustomVariable', () => {
|
||||
const variable = new CustomVariable({
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
value: ['test', 'test2'],
|
||||
text: ['test', 'test2'],
|
||||
query: 'test,test1,test2',
|
||||
options: [
|
||||
{ label: 'test', value: 'test' },
|
||||
{ label: 'test1', value: 'test1' },
|
||||
{ label: 'test2', value: 'test2' },
|
||||
],
|
||||
includeAll: true,
|
||||
allValue: 'test-all',
|
||||
isMulti: true,
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToVariables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"allValue": "test-all",
|
||||
"current": {
|
||||
"text": [
|
||||
"test",
|
||||
"test2",
|
||||
],
|
||||
"value": [
|
||||
"test",
|
||||
"test2",
|
||||
],
|
||||
},
|
||||
"description": "test-desc",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "test-label",
|
||||
"multi": true,
|
||||
"name": "test",
|
||||
"options": [],
|
||||
"query": "test,test1,test2",
|
||||
"skipUrlSync": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle ConstantVariable', () => {
|
||||
const variable = new ConstantVariable({
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
value: 'constant value',
|
||||
skipUrlSync: true,
|
||||
});
|
||||
const set = new SceneVariableSet({
|
||||
variables: [variable],
|
||||
});
|
||||
|
||||
const result = sceneVariablesSetToVariables(set);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"current": {
|
||||
"text": "constant value",
|
||||
"value": "constant value",
|
||||
},
|
||||
"description": "test-desc",
|
||||
"hide": 2,
|
||||
"label": "test-label",
|
||||
"name": "test",
|
||||
"query": "constant value",
|
||||
"skipUrlSync": true,
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
import { SceneVariableSet, QueryVariable, CustomVariable, DataSourceVariable, ConstantVariable } from '@grafana/scenes';
|
||||
import { VariableModel, VariableHide } from '@grafana/schema';
|
||||
|
||||
export function sceneVariablesSetToVariables(set: SceneVariableSet) {
|
||||
const variables: VariableModel[] = [];
|
||||
for (const variable of set.state.variables) {
|
||||
const commonProperties = {
|
||||
name: variable.state.name,
|
||||
label: variable.state.label,
|
||||
description: variable.state.description,
|
||||
skipUrlSync: Boolean(variable.state.skipUrlSync),
|
||||
hide: variable.state.hide || VariableHide.dontHide,
|
||||
};
|
||||
if (variable instanceof QueryVariable) {
|
||||
variables.push({
|
||||
...commonProperties,
|
||||
current: {
|
||||
// @ts-expect-error
|
||||
value: variable.state.value,
|
||||
// @ts-expect-error
|
||||
text: variable.state.text,
|
||||
},
|
||||
options: [],
|
||||
query: variable.state.query,
|
||||
datasource: variable.state.datasource,
|
||||
sort: variable.state.sort,
|
||||
refresh: variable.state.refresh,
|
||||
regex: variable.state.regex,
|
||||
allValue: variable.state.allValue,
|
||||
includeAll: variable.state.includeAll,
|
||||
multi: variable.state.isMulti,
|
||||
skipUrlSync: Boolean(variable.state.skipUrlSync),
|
||||
hide: variable.state.hide || VariableHide.dontHide,
|
||||
});
|
||||
} else if (variable instanceof CustomVariable) {
|
||||
variables.push({
|
||||
...commonProperties,
|
||||
current: {
|
||||
// @ts-expect-error
|
||||
text: variable.state.value,
|
||||
// @ts-expect-error
|
||||
value: variable.state.value,
|
||||
},
|
||||
options: [],
|
||||
query: variable.state.query,
|
||||
multi: variable.state.isMulti,
|
||||
allValue: variable.state.allValue,
|
||||
includeAll: variable.state.includeAll,
|
||||
});
|
||||
} else if (variable instanceof DataSourceVariable) {
|
||||
variables.push({
|
||||
...commonProperties,
|
||||
current: {
|
||||
// @ts-expect-error
|
||||
value: variable.state.value,
|
||||
// @ts-expect-error
|
||||
text: variable.state.text,
|
||||
},
|
||||
options: [],
|
||||
regex: variable.state.regex,
|
||||
query: variable.state.pluginId,
|
||||
multi: variable.state.isMulti,
|
||||
allValue: variable.state.allValue,
|
||||
includeAll: variable.state.includeAll,
|
||||
});
|
||||
} else if (variable instanceof ConstantVariable) {
|
||||
variables.push({
|
||||
...commonProperties,
|
||||
current: {
|
||||
// @ts-expect-error
|
||||
value: variable.state.value,
|
||||
// @ts-expect-error
|
||||
text: variable.state.value,
|
||||
},
|
||||
// @ts-expect-error
|
||||
query: variable.state.value,
|
||||
hide: VariableHide.hideVariable,
|
||||
});
|
||||
} else {
|
||||
throw new Error('Unsupported variable type');
|
||||
}
|
||||
}
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
|
@ -92,17 +92,10 @@ export function createSceneObjectsForPanels(oldPanels: PanelModel[]): SceneGridI
|
|||
}
|
||||
}
|
||||
} 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);
|
||||
const gridItem = buildGridItemForLibPanel(panel);
|
||||
if (gridItem) {
|
||||
panels.push(gridItem);
|
||||
}
|
||||
} else {
|
||||
const panelObject = buildGridItemForPanel(panel);
|
||||
|
||||
|
|
@ -296,6 +289,24 @@ export function createSceneVariableFromVariableModel(variable: VariableModel): S
|
|||
}
|
||||
}
|
||||
|
||||
export function buildGridItemForLibPanel(panel: PanelModel) {
|
||||
if (!panel.libraryPanel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SceneGridItem({
|
||||
body: new LibraryVizPanel({
|
||||
title: panel.title,
|
||||
uid: panel.libraryPanel.uid,
|
||||
name: panel.libraryPanel.name,
|
||||
key: getVizPanelKeyForPanelId(panel.id),
|
||||
}),
|
||||
y: panel.gridPos.y,
|
||||
x: panel.gridPos.x,
|
||||
width: panel.gridPos.w,
|
||||
height: panel.gridPos.h,
|
||||
});
|
||||
}
|
||||
export function buildGridItemForPanel(panel: PanelModel): SceneGridItemLike {
|
||||
const vizPanelState: VizPanelState = {
|
||||
key: getVizPanelKeyForPanelId(panel.id),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,11 @@ import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
|||
|
||||
import dashboard_to_load1 from './testfiles/dashboard_to_load1.json';
|
||||
import repeatingRowsAndPanelsDashboardJson from './testfiles/repeating_rows_and_panels.json';
|
||||
import { buildGridItemForPanel, transformSaveModelToScene } from './transformSaveModelToScene';
|
||||
import {
|
||||
buildGridItemForLibPanel,
|
||||
buildGridItemForPanel,
|
||||
transformSaveModelToScene,
|
||||
} from './transformSaveModelToScene';
|
||||
import { gridItemToPanel, transformSceneToSaveModel } from './transformSceneToSaveModel';
|
||||
|
||||
describe('transformSceneToSaveModel', () => {
|
||||
|
|
@ -103,6 +107,47 @@ describe('transformSceneToSaveModel', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Library panels', () => {
|
||||
it('given a library panel', () => {
|
||||
const panel = buildGridItemFromPanelSchema({
|
||||
id: 4,
|
||||
gridPos: {
|
||||
h: 8,
|
||||
w: 12,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
libraryPanel: {
|
||||
name: 'Some lib panel panel',
|
||||
uid: 'lib-panel-uid',
|
||||
},
|
||||
title: 'A panel',
|
||||
transformations: [],
|
||||
fieldConfig: {
|
||||
defaults: {},
|
||||
overrides: [],
|
||||
},
|
||||
});
|
||||
|
||||
const result = gridItemToPanel(panel);
|
||||
|
||||
expect(result.id).toBe(4);
|
||||
expect(result.libraryPanel).toEqual({
|
||||
name: 'Some lib panel panel',
|
||||
uid: 'lib-panel-uid',
|
||||
});
|
||||
expect(result.gridPos).toEqual({
|
||||
h: 8,
|
||||
w: 12,
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
expect(result.title).toBe('A panel');
|
||||
expect(result.transformations).toBeUndefined();
|
||||
expect(result.fieldConfig).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Annotations', () => {
|
||||
it('should transform annotations to save model', () => {
|
||||
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
|
||||
|
|
@ -318,5 +363,8 @@ describe('transformSceneToSaveModel', () => {
|
|||
});
|
||||
|
||||
export function buildGridItemFromPanelSchema(panel: Partial<Panel>): SceneGridItemLike {
|
||||
if (panel.libraryPanel) {
|
||||
return buildGridItemForLibPanel(new PanelModel(panel))!;
|
||||
}
|
||||
return buildGridItemForPanel(new PanelModel(panel));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
SceneDataLayerProvider,
|
||||
SceneQueryRunner,
|
||||
SceneDataTransformer,
|
||||
SceneVariableSet,
|
||||
} from '@grafana/scenes';
|
||||
import {
|
||||
AnnotationQuery,
|
||||
|
|
@ -18,24 +19,31 @@ import {
|
|||
FieldConfigSource,
|
||||
Panel,
|
||||
RowPanel,
|
||||
VariableModel,
|
||||
} from '@grafana/schema';
|
||||
import { sortedDeepCloneWithoutNulls } from 'app/core/utils/object';
|
||||
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard';
|
||||
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
import { LibraryVizPanel } from '../scene/LibraryVizPanel';
|
||||
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
||||
import { PanelTimeRange } from '../scene/PanelTimeRange';
|
||||
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
||||
import { ShareQueryDataProvider } from '../scene/ShareQueryDataProvider';
|
||||
import { getPanelIdForVizPanel } from '../utils/utils';
|
||||
|
||||
import { sceneVariablesSetToVariables } from './sceneVariablesSetToVariables';
|
||||
|
||||
export function transformSceneToSaveModel(scene: DashboardScene): Dashboard {
|
||||
const state = scene.state;
|
||||
const timeRange = state.$timeRange!.state;
|
||||
const data = state.$data;
|
||||
const variablesSet = state.$variables;
|
||||
const body = state.body;
|
||||
const panels: Panel[] = [];
|
||||
|
||||
let variables: VariableModel[] = [];
|
||||
|
||||
if (body instanceof SceneGridLayout) {
|
||||
for (const child of body.state.children) {
|
||||
if (child instanceof SceneGridItem) {
|
||||
|
|
@ -55,10 +63,13 @@ export function transformSceneToSaveModel(scene: DashboardScene): Dashboard {
|
|||
let annotations: AnnotationQuery[] = [];
|
||||
if (data instanceof SceneDataLayers) {
|
||||
const layers = data.state.layers;
|
||||
|
||||
annotations = dataLayersToAnnotations(layers);
|
||||
}
|
||||
|
||||
if (variablesSet instanceof SceneVariableSet) {
|
||||
variables = sceneVariablesSetToVariables(variablesSet);
|
||||
}
|
||||
|
||||
const dashboard: Dashboard = {
|
||||
...defaultDashboard,
|
||||
title: state.title,
|
||||
|
|
@ -71,6 +82,9 @@ export function transformSceneToSaveModel(scene: DashboardScene): Dashboard {
|
|||
annotations: {
|
||||
list: annotations,
|
||||
},
|
||||
templating: {
|
||||
list: variables,
|
||||
},
|
||||
timezone: timeRange.timeZone,
|
||||
fiscalYearStartMonth: timeRange.fiscalYearStartMonth,
|
||||
weekStart: timeRange.weekStart,
|
||||
|
|
@ -87,6 +101,24 @@ export function gridItemToPanel(gridItem: SceneGridItemLike): Panel {
|
|||
h = 0;
|
||||
|
||||
if (gridItem instanceof SceneGridItem) {
|
||||
// Handle library panels, early exit
|
||||
if (gridItem.state.body instanceof LibraryVizPanel) {
|
||||
x = gridItem.state.x ?? 0;
|
||||
y = gridItem.state.y ?? 0;
|
||||
w = gridItem.state.width ?? 0;
|
||||
h = gridItem.state.height ?? 0;
|
||||
|
||||
return {
|
||||
id: getPanelIdForVizPanel(gridItem.state.body),
|
||||
title: gridItem.state.body.state.title,
|
||||
gridPos: { x, y, w, h },
|
||||
libraryPanel: {
|
||||
name: gridItem.state.body.state.name,
|
||||
uid: gridItem.state.body.state.uid,
|
||||
},
|
||||
} as Panel;
|
||||
}
|
||||
|
||||
if (!(gridItem.state.body instanceof VizPanel)) {
|
||||
throw new Error('SceneGridItem body expected to be VizPanel');
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue