DashboardScene: Integrate annotations (#74610)

* WIP Dashboard to Scenes: Annotations

* Bump scenes

* Enable annotations and controls

* Betterer

* Update snapshots

* Test fix
This commit is contained in:
Dominik Prokop
2023-09-20 11:50:35 +02:00
committed by GitHub
parent ae32d43e2b
commit 157859aede
13 changed files with 365 additions and 14 deletions

View File

@@ -1794,12 +1794,15 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "0"] [0, 0, 0, "Do not use any type assertions.", "0"]
], ],
"public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts:5381": [ "public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
], ],
"public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts:5381": [ "public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"] [0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
], ],
"public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts:5381": [ "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.", "0"]

View File

@@ -251,7 +251,7 @@
"@grafana/lezer-traceql": "0.0.6", "@grafana/lezer-traceql": "0.0.6",
"@grafana/monaco-logql": "^0.0.7", "@grafana/monaco-logql": "^0.0.7",
"@grafana/runtime": "workspace:*", "@grafana/runtime": "workspace:*",
"@grafana/scenes": "^1.3.1", "@grafana/scenes": "^1.3.3",
"@grafana/schema": "workspace:*", "@grafana/schema": "workspace:*",
"@grafana/ui": "workspace:*", "@grafana/ui": "workspace:*",
"@kusto/monaco-kusto": "^7.4.0", "@kusto/monaco-kusto": "^7.4.0",

View File

@@ -13,6 +13,16 @@ import { setupLoadDashboardMock } from '../utils/test-utils';
import { DashboardScenePage, Props } from './DashboardScenePage'; import { DashboardScenePage, Props } from './DashboardScenePage';
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: () => {
return {
get: jest.fn().mockResolvedValue({}),
getInstanceSettings: jest.fn().mockResolvedValue({ uid: 'ds1' }),
};
},
}));
function setup() { function setup() {
const context = getGrafanaContextMock(); const context = getGrafanaContextMock();
const props: Props = { const props: Props = {

View File

@@ -1,5 +1,6 @@
import { StateManagerBase } from 'app/core/services/StateManagerBase'; import { StateManagerBase } from 'app/core/services/StateManagerBase';
import { dashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv'; import { dashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { buildPanelEditScene, PanelEditor } from '../panel-edit/PanelEditor'; import { buildPanelEditScene, PanelEditor } from '../panel-edit/PanelEditor';
import { DashboardScene } from '../scene/DashboardScene'; import { DashboardScene } from '../scene/DashboardScene';
@@ -66,6 +67,7 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
} }
public clearState() { public clearState() {
getDashboardSrv().setCurrent(undefined);
this.setState({ dashboard: undefined, loadError: undefined, isLoading: false, panelEditor: undefined }); this.setState({ dashboard: undefined, loadError: undefined, isLoading: false, panelEditor: undefined });
} }
} }

View File

@@ -1,13 +1,16 @@
import { Unsubscribable } from 'rxjs'; import { Observable, ReplaySubject, Unsubscribable } from 'rxjs';
import { getDefaultTimeRange } from '@grafana/data';
import { import {
SceneDataProvider, SceneDataProvider,
SceneDataProviderResult,
SceneDataState, SceneDataState,
SceneDataTransformer, SceneDataTransformer,
SceneDeactivationHandler, SceneDeactivationHandler,
SceneObject, SceneObject,
SceneObjectBase, SceneObjectBase,
} from '@grafana/scenes'; } from '@grafana/scenes';
import { LoadingState } from '@grafana/schema';
import { DashboardQuery } from 'app/plugins/datasource/dashboard/types'; import { DashboardQuery } from 'app/plugins/datasource/dashboard/types';
import { getVizPanelKeyForPanelId } from '../utils/utils'; import { getVizPanelKeyForPanelId } from '../utils/utils';
@@ -19,6 +22,7 @@ export interface ShareQueryDataProviderState extends SceneDataState {
export class ShareQueryDataProvider extends SceneObjectBase<ShareQueryDataProviderState> implements SceneDataProvider { export class ShareQueryDataProvider extends SceneObjectBase<ShareQueryDataProviderState> implements SceneDataProvider {
private _querySub: Unsubscribable | undefined; private _querySub: Unsubscribable | undefined;
private _sourceDataDeactivationHandler?: SceneDeactivationHandler; private _sourceDataDeactivationHandler?: SceneDeactivationHandler;
private _results = new ReplaySubject<SceneDataProviderResult>();
constructor(state: ShareQueryDataProviderState) { constructor(state: ShareQueryDataProviderState) {
super(state); super(state);
@@ -40,6 +44,10 @@ export class ShareQueryDataProvider extends SceneObjectBase<ShareQueryDataProvid
}); });
} }
public getResultsStream(): Observable<SceneDataProviderResult> {
return this._results;
}
private _subscribeToSource() { private _subscribeToSource() {
const { query } = this.state; const { query } = this.state;
@@ -78,7 +86,18 @@ export class ShareQueryDataProvider extends SceneObjectBase<ShareQueryDataProvid
} }
} }
this._querySub = sourceData.subscribeToState((state) => this.setState({ data: state.data })); this._querySub = sourceData.subscribeToState((state) => {
this._results.next({
origin: this,
data: state.data || {
state: LoadingState.Done,
series: [],
timeRange: getDefaultTimeRange(),
},
});
this.setState({ data: state.data });
});
// Copy the initial state // Copy the initial state
this.setState({ data: sourceData.state.data }); this.setState({ data: sourceData.state.data });

View File

@@ -1,7 +1,85 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`transformSceneToSaveModel Annotations should transform annotations to save model 1`] = `
[
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana",
},
"enable": true,
"hide": false,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard",
},
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata",
},
"enable": true,
"hide": false,
"iconColor": "red",
"name": "Enabled",
"target": {
"lines": 4,
"refId": "Anno",
"scenarioId": "annotations",
},
},
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata",
},
"enable": false,
"hide": false,
"iconColor": "yellow",
"name": "Disabled",
"target": {
"lines": 5,
"refId": "Anno",
"scenarioId": "annotations",
},
},
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata",
},
"enable": true,
"hide": true,
"iconColor": "dark-purple",
"name": "Hidden",
"target": {
"lines": 6,
"refId": "Anno",
"scenarioId": "annotations",
},
},
]
`;
exports[`transformSceneToSaveModel Given a scene with rows Should transform back to peristed model 1`] = ` exports[`transformSceneToSaveModel Given a scene with rows Should transform back to peristed model 1`] = `
{ {
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --",
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard",
},
],
},
"editable": true, "editable": true,
"fiscalYearStartMonth": 0, "fiscalYearStartMonth": 0,
"graphTooltip": 0, "graphTooltip": 0,
@@ -120,6 +198,67 @@ exports[`transformSceneToSaveModel Given a scene with rows Should transform back
exports[`transformSceneToSaveModel Given a simple scene Should transform back to peristed model 1`] = ` exports[`transformSceneToSaveModel Given a simple scene Should transform back to peristed model 1`] = `
{ {
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana",
},
"enable": true,
"hide": false,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard",
},
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata",
},
"enable": true,
"hide": false,
"iconColor": "red",
"name": "Enabled",
"target": {
"lines": 4,
"refId": "Anno",
"scenarioId": "annotations",
},
},
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata",
},
"enable": false,
"hide": false,
"iconColor": "yellow",
"name": "Disabled",
"target": {
"lines": 5,
"refId": "Anno",
"scenarioId": "annotations",
},
},
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata",
},
"enable": true,
"hide": true,
"iconColor": "dark-purple",
"name": "Hidden",
"target": {
"lines": 6,
"refId": "Anno",
"scenarioId": "annotations",
},
},
],
},
"editable": true, "editable": true,
"fiscalYearStartMonth": 0, "fiscalYearStartMonth": 0,
"graphTooltip": 0, "graphTooltip": 0,

View File

@@ -8,10 +8,53 @@
"uid": "grafana" "uid": "grafana"
}, },
"enable": true, "enable": true,
"hide": true, "hide": false,
"iconColor": "rgba(0, 211, 255, 1)", "iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts", "name": "Annotations & Alerts",
"type": "dashboard" "type": "dashboard"
},
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata"
},
"enable": true,
"iconColor": "red",
"name": "Enabled",
"target": {
"lines": 4,
"refId": "Anno",
"scenarioId": "annotations"
}
},
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata"
},
"enable": false,
"iconColor": "yellow",
"name": "Disabled",
"target": {
"lines": 5,
"refId": "Anno",
"scenarioId": "annotations"
}
},
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata"
},
"enable": true,
"hide": true,
"iconColor": "dark-purple",
"name": "Hidden",
"target": {
"lines": 6,
"refId": "Anno",
"scenarioId": "annotations"
}
} }
] ]
}, },

View File

@@ -5,6 +5,8 @@ import {
CustomVariable, CustomVariable,
DataSourceVariable, DataSourceVariable,
QueryVariable, QueryVariable,
SceneDataLayerControls,
SceneDataLayers,
SceneDataTransformer, SceneDataTransformer,
SceneGridItem, SceneGridItem,
SceneGridLayout, SceneGridLayout,
@@ -23,6 +25,7 @@ import { PanelTimeRange } from '../scene/PanelTimeRange';
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
import { ShareQueryDataProvider } from '../scene/ShareQueryDataProvider'; import { ShareQueryDataProvider } from '../scene/ShareQueryDataProvider';
import dashboard_to_load1 from './testfiles/dashboard_to_load1.json';
import repeatingRowsAndPanelsDashboardJson from './testfiles/repeating_rows_and_panels.json'; import repeatingRowsAndPanelsDashboardJson from './testfiles/repeating_rows_and_panels.json';
import { import {
createDashboardSceneFromDashboardModel, createDashboardSceneFromDashboardModel,
@@ -637,6 +640,33 @@ describe('transformSaveModelToScene', () => {
expect(lastRow.state.isCollapsed).toBe(true); expect(lastRow.state.isCollapsed).toBe(true);
}); });
}); });
describe('Annotation queries', () => {
it('Should build correct scene model', () => {
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
expect(scene.state.$data).toBeInstanceOf(SceneDataLayers);
expect(scene.state.controls![0]).toBeInstanceOf(SceneDataLayerControls);
const dataLayers = scene.state.$data as SceneDataLayers;
expect(dataLayers.state.layers).toHaveLength(4);
expect(dataLayers.state.layers[0].state.name).toBe('Annotations & Alerts');
expect(dataLayers.state.layers[0].state.isEnabled).toBe(true);
expect(dataLayers.state.layers[0].state.isHidden).toBe(false);
expect(dataLayers.state.layers[1].state.name).toBe('Enabled');
expect(dataLayers.state.layers[1].state.isEnabled).toBe(true);
expect(dataLayers.state.layers[1].state.isHidden).toBe(false);
expect(dataLayers.state.layers[2].state.name).toBe('Disabled');
expect(dataLayers.state.layers[2].state.isEnabled).toBe(false);
expect(dataLayers.state.layers[2].state.isHidden).toBe(false);
expect(dataLayers.state.layers[3].state.name).toBe('Hidden');
expect(dataLayers.state.layers[3].state.isEnabled).toBe(true);
expect(dataLayers.state.layers[3].state.isHidden).toBe(true);
});
});
}); });
function buildGridItemForTest(saveModel: Partial<Panel>): { gridItem: SceneGridItem; vizPanel: VizPanel } { function buildGridItemForTest(saveModel: Partial<Panel>): { gridItem: SceneGridItem; vizPanel: VizPanel } {

View File

@@ -26,7 +26,12 @@ import {
behaviors, behaviors,
VizPanelState, VizPanelState,
SceneGridItemLike, SceneGridItemLike,
SceneDataLayers,
dataLayers,
SceneDataLayerProvider,
SceneDataLayerControls,
} from '@grafana/scenes'; } from '@grafana/scenes';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { DashboardDTO } from 'app/types'; import { DashboardDTO } from 'app/types';
@@ -51,6 +56,9 @@ export function transformSaveModelToScene(rsp: DashboardDTO): DashboardScene {
autoMigrateOldPanels: true, autoMigrateOldPanels: true,
}); });
// Setting for built-in annotations query to run
getDashboardSrv().setCurrent(oldModel);
return createDashboardSceneFromDashboardModel(oldModel); return createDashboardSceneFromDashboardModel(oldModel);
} }
@@ -148,6 +156,7 @@ function createRowFromPanelModel(row: PanelModel, content: SceneGridItemLike[]):
export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel) { export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel) {
let variables: SceneVariableSet | undefined = undefined; let variables: SceneVariableSet | undefined = undefined;
let layers: SceneDataLayerProvider[] = [];
if (oldModel.templating?.list?.length) { if (oldModel.templating?.list?.length) {
const variableObjects = oldModel.templating.list const variableObjects = oldModel.templating.list
@@ -168,7 +177,20 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
}); });
} }
if (oldModel.annotations?.list?.length) {
layers = oldModel.annotations?.list.map((a) => {
// Each annotation query is an individual data layer
return new dataLayers.AnnotationsDataLayer({
query: a,
name: a.name,
isEnabled: Boolean(a.enable),
isHidden: Boolean(a.hide),
});
});
}
const controls: SceneObject[] = [ const controls: SceneObject[] = [
new SceneDataLayerControls(),
new VariableValueSelectors({}), new VariableValueSelectors({}),
new SceneControlsSpacer(), new SceneControlsSpacer(),
new SceneTimePicker({}), new SceneTimePicker({}),
@@ -193,6 +215,12 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
sync: oldModel.graphTooltip, sync: oldModel.graphTooltip,
}), }),
], ],
$data:
layers.length > 0
? new SceneDataLayers({
layers,
})
: undefined,
controls: controls, controls: controls,
}); });
} }

View File

@@ -1,4 +1,11 @@
import { MultiValueVariable, SceneGridItemLike, SceneGridLayout, SceneGridRow, SceneVariable } from '@grafana/scenes'; import {
MultiValueVariable,
SceneDataLayers,
SceneGridItemLike,
SceneGridLayout,
SceneGridRow,
SceneVariable,
} from '@grafana/scenes';
import { Panel, RowPanel } from '@grafana/schema'; import { Panel, RowPanel } from '@grafana/schema';
import { PanelModel } from 'app/features/dashboard/state'; import { PanelModel } from 'app/features/dashboard/state';
@@ -94,6 +101,36 @@ describe('transformSceneToSaveModel', () => {
expect(saveModel.gridPos?.h).toBe(8); expect(saveModel.gridPos?.h).toBe(8);
}); });
}); });
describe('Annotations', () => {
it('should transform annotations to save model', () => {
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
const saveModel = transformSceneToSaveModel(scene);
expect(saveModel.annotations?.list?.length).toBe(4);
expect(saveModel.annotations?.list).toMatchSnapshot();
});
it('should transform annotations to save model after state changes', () => {
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
const layers = (scene.state.$data as SceneDataLayers)?.state.layers;
const enabledLayer = layers[1];
const hiddenLayer = layers[3];
enabledLayer.setState({
isEnabled: false,
});
hiddenLayer.setState({
isHidden: false,
});
const saveModel = transformSceneToSaveModel(scene);
expect(saveModel.annotations?.list?.length).toBe(4);
expect(saveModel.annotations?.list?.[1].enable).toEqual(false);
expect(saveModel.annotations?.list?.[3].hide).toEqual(false);
});
});
}); });
export function buildGridItemFromPanelSchema(panel: Partial<Panel>): SceneGridItemLike { export function buildGridItemFromPanelSchema(panel: Partial<Panel>): SceneGridItemLike {

View File

@@ -1,5 +1,14 @@
import { SceneGridItem, SceneGridItemLike, SceneGridLayout, SceneGridRow, VizPanel } from '@grafana/scenes'; import {
import { Dashboard, defaultDashboard, FieldConfigSource, Panel, RowPanel } from '@grafana/schema'; SceneDataLayers,
SceneGridItem,
SceneGridItemLike,
SceneGridLayout,
SceneGridRow,
VizPanel,
dataLayers,
SceneDataLayerProvider,
} from '@grafana/scenes';
import { AnnotationQuery, Dashboard, defaultDashboard, FieldConfigSource, Panel, RowPanel } from '@grafana/schema';
import { sortedDeepCloneWithoutNulls } from 'app/core/utils/object'; import { sortedDeepCloneWithoutNulls } from 'app/core/utils/object';
import { DashboardScene } from '../scene/DashboardScene'; import { DashboardScene } from '../scene/DashboardScene';
@@ -11,6 +20,7 @@ import { getPanelIdForVizPanel } from '../utils/utils';
export function transformSceneToSaveModel(scene: DashboardScene): Dashboard { export function transformSceneToSaveModel(scene: DashboardScene): Dashboard {
const state = scene.state; const state = scene.state;
const timeRange = state.$timeRange!.state; const timeRange = state.$timeRange!.state;
const data = state.$data;
const body = state.body; const body = state.body;
const panels: Panel[] = []; const panels: Panel[] = [];
@@ -30,6 +40,13 @@ export function transformSceneToSaveModel(scene: DashboardScene): Dashboard {
} }
} }
let annotations: AnnotationQuery[] = [];
if (data instanceof SceneDataLayers) {
const layers = data.state.layers;
annotations = dataLayersToAnnotations(layers);
}
const dashboard: Dashboard = { const dashboard: Dashboard = {
...defaultDashboard, ...defaultDashboard,
title: state.title, title: state.title,
@@ -39,6 +56,9 @@ export function transformSceneToSaveModel(scene: DashboardScene): Dashboard {
to: timeRange.to, to: timeRange.to,
}, },
panels, panels,
annotations: {
list: annotations,
},
}; };
return sortedDeepCloneWithoutNulls(dashboard); return sortedDeepCloneWithoutNulls(dashboard);
@@ -141,3 +161,20 @@ export function gridRowToSaveModel(gridRow: SceneGridRow, panelsArray: Array<Pan
panelsArray.push(...panelsInsideRow); panelsArray.push(...panelsInsideRow);
} }
} }
export function dataLayersToAnnotations(layers: SceneDataLayerProvider[]) {
const annotations: AnnotationQuery[] = [];
for (const layer of layers) {
if (!(layer instanceof dataLayers.AnnotationsDataLayer)) {
continue;
}
annotations.push({
...layer.state.query,
enable: Boolean(layer.state.isEnabled),
hide: Boolean(layer.state.isHidden),
});
}
return annotations;
}

View File

@@ -24,6 +24,9 @@ export function createPanelDataProvider(panel: PanelModel): SceneDataProvider |
dataProvider = new SceneQueryRunner({ dataProvider = new SceneQueryRunner({
queries: panel.targets, queries: panel.targets,
maxDataPoints: panel.maxDataPoints ?? undefined, maxDataPoints: panel.maxDataPoints ?? undefined,
dataLayerFilter: {
panelId: panel.id,
},
}); });
} }

View File

@@ -4010,9 +4010,9 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@grafana/scenes@npm:^1.3.1": "@grafana/scenes@npm:^1.3.3":
version: 1.3.1 version: 1.3.3
resolution: "@grafana/scenes@npm:1.3.1" resolution: "@grafana/scenes@npm:1.3.3"
dependencies: dependencies:
"@grafana/e2e-selectors": 10.0.2 "@grafana/e2e-selectors": 10.0.2
react-grid-layout: 1.3.4 react-grid-layout: 1.3.4
@@ -4024,7 +4024,7 @@ __metadata:
"@grafana/runtime": 10.0.3 "@grafana/runtime": 10.0.3
"@grafana/schema": 10.0.3 "@grafana/schema": 10.0.3
"@grafana/ui": 10.0.3 "@grafana/ui": 10.0.3
checksum: fe348e4eaaa3604d0d1ec745b14ae1c0752ce1aa481e5f05a10cdf9392448386e600c2a94f2ae23af83f59daf34cd11fb7a336da863b8624b98c8d3c2732b3c3 checksum: 3c630aaeca7bc240b1db1a6cc7856808d940c354a558e6f1c5c0fac7268c5f57e0b6f8f55a1d9d1dbfbb02011f1beecc5fe1aae1209bc4430599e2660fd76f6d
languageName: node languageName: node
linkType: hard linkType: hard
@@ -19701,7 +19701,7 @@ __metadata:
"@grafana/lezer-traceql": 0.0.6 "@grafana/lezer-traceql": 0.0.6
"@grafana/monaco-logql": ^0.0.7 "@grafana/monaco-logql": ^0.0.7
"@grafana/runtime": "workspace:*" "@grafana/runtime": "workspace:*"
"@grafana/scenes": ^1.3.1 "@grafana/scenes": ^1.3.3
"@grafana/schema": "workspace:*" "@grafana/schema": "workspace:*"
"@grafana/tsconfig": ^1.3.0-rc1 "@grafana/tsconfig": ^1.3.0-rc1
"@grafana/ui": "workspace:*" "@grafana/ui": "workspace:*"