diff --git a/.betterer.results b/.betterer.results index d20cfea18c1..e768dc7999e 100644 --- a/.betterer.results +++ b/.betterer.results @@ -2999,6 +2999,9 @@ exports[`better eslint`] = { "public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], + "public/app/features/dashboard-scene/scene/setDashboardPanelContext.test.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], "public/app/features/dashboard-scene/serialization/angularMigration.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] diff --git a/docs/sources/developers/kinds/core/dashboard/schema-reference.md b/docs/sources/developers/kinds/core/dashboard/schema-reference.md index 37206a725c2..42b36f9f486 100644 --- a/docs/sources/developers/kinds/core/dashboard/schema-reference.md +++ b/docs/sources/developers/kinds/core/dashboard/schema-reference.md @@ -117,6 +117,7 @@ FROM: AnnotationQuery in grafana-data/src/types/annotations.ts | `enable` | boolean | **Yes** | `true` | When enabled the annotation query is issued with every dashboard refresh | | `iconColor` | string | **Yes** | | Color to use for the annotation event markers | | `name` | string | **Yes** | | Name of annotation. | +| `builtIn` | number | No | `0` | Set to 1 for the standard annotation query all dashboards have by default. | | `filter` | [AnnotationPanelFilter](#annotationpanelfilter) | No | | | | `hide` | boolean | No | `false` | Annotation queries can be toggled on or off at the top of the dashboard.
When hide is true, the toggle is not shown in the dashboard. | | `target` | [AnnotationTarget](#annotationtarget) | No | | TODO: this should be a regular DataQuery that depends on the selected dashboard
these match the properties of the "grafana" datasouce that is default in most dashboards | diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue index 0b32ebff3da..1fb83373166 100644 --- a/kinds/dashboard/dashboard_kind.cue +++ b/kinds/dashboard/dashboard_kind.cue @@ -176,6 +176,9 @@ lineage: schemas: [{ // TODO -- this should not exist here, it is based on the --grafana-- datasource type?: string @grafanamaturity(NeedsExpertReview) + // Set to 1 for the standard annotation query all dashboards have by default. + builtIn?: number | *0 + // unless datasources have migrated to the target+mapping, // they just spread their query into the base object :( ... diff --git a/package.json b/package.json index 59dbf0ae61e..6cb3d51ca65 100644 --- a/package.json +++ b/package.json @@ -251,7 +251,7 @@ "@grafana/lezer-traceql": "0.0.7", "@grafana/monaco-logql": "^0.0.7", "@grafana/runtime": "workspace:*", - "@grafana/scenes": "^1.15.0", + "@grafana/scenes": "^1.18.0", "@grafana/schema": "workspace:*", "@grafana/ui": "workspace:*", "@kusto/monaco-kusto": "^7.4.0", diff --git a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts index 99c9b16090d..d16491351f7 100644 --- a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts +++ b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts @@ -77,6 +77,10 @@ export const defaultAnnotationContainer: Partial = { * FROM: AnnotationQuery in grafana-data/src/types/annotations.ts */ export interface AnnotationQuery { + /** + * Set to 1 for the standard annotation query all dashboards have by default. + */ + builtIn?: number; /** * Datasource where the annotations data is */ @@ -113,6 +117,7 @@ export interface AnnotationQuery { } export const defaultAnnotationQuery: Partial = { + builtIn: 0, enable: true, hide: false, }; diff --git a/pkg/kinds/dashboard/dashboard_spec_gen.go b/pkg/kinds/dashboard/dashboard_spec_gen.go index ca5b81891a3..e5be145cffa 100644 --- a/pkg/kinds/dashboard/dashboard_spec_gen.go +++ b/pkg/kinds/dashboard/dashboard_spec_gen.go @@ -187,6 +187,9 @@ type AnnotationPanelFilter struct { // TODO docs // FROM: AnnotationQuery in grafana-data/src/types/annotations.ts type AnnotationQuery struct { + // Set to 1 for the standard annotation query all dashboards have by default. + BuiltIn *float32 `json:"builtIn,omitempty"` + // Ref to a DataSource instance Datasource DataSourceRef `json:"datasource"` diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index a31f99b2423..2ccfde78141 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -213,4 +213,8 @@ export class DashboardScene extends SceneObjectBase { panelId: (panel && getPanelIdForVizPanel(panel)) ?? 0, }; } + + canEditDashboard() { + return Boolean(this.state.meta.canEdit || this.state.meta.canMakeEditable); + } } diff --git a/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx b/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx index b15c8ef7894..ce09b6269f6 100644 --- a/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx +++ b/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx @@ -42,7 +42,7 @@ export class LibraryVizPanel extends SceneObjectBase { }); } catch (err) { vizPanel.setState({ - pluginLoadError: 'Unable to load library panel: ' + this.state.uid, + _pluginLoadError: 'Unable to load library panel: ' + this.state.uid, }); } diff --git a/public/app/features/dashboard-scene/scene/setDashboardPanelContext.test.ts b/public/app/features/dashboard-scene/scene/setDashboardPanelContext.test.ts new file mode 100644 index 00000000000..149dce0d119 --- /dev/null +++ b/public/app/features/dashboard-scene/scene/setDashboardPanelContext.test.ts @@ -0,0 +1,238 @@ +import { EventBusSrv } from '@grafana/data'; +import { BackendSrv, setBackendSrv } from '@grafana/runtime'; +import { PanelContext } from '@grafana/ui'; + +import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene'; +import { findVizPanelByKey } from '../utils/utils'; + +import { getAdHocFilterSetFor, setDashboardPanelContext } from './setDashboardPanelContext'; + +const postFn = jest.fn(); +const putFn = jest.fn(); +const deleteFn = jest.fn(); + +setBackendSrv({ + post: postFn, + put: putFn, + delete: deleteFn, +} as any as BackendSrv); + +describe('setDashboardPanelContext', () => { + describe('canAddAnnotations', () => { + it('Can add when builtIn is enabled and permissions allow', () => { + const { context } = buildTestScene({ builtInAnnotationsEnabled: true, dashboardCanEdit: true, canAdd: true }); + expect(context.canAddAnnotations!()).toBe(true); + }); + + it('Can not when builtIn is disabled', () => { + const { context } = buildTestScene({ builtInAnnotationsEnabled: false, dashboardCanEdit: true, canAdd: true }); + expect(context.canAddAnnotations!()).toBe(false); + }); + + it('Can not when permission do not allow', () => { + const { context } = buildTestScene({ builtInAnnotationsEnabled: true, dashboardCanEdit: true, canAdd: false }); + expect(context.canAddAnnotations!()).toBe(false); + }); + }); + + describe('canEditAnnotations', () => { + it('Can edit global event when user has org permission', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, orgCanEdit: true }); + expect(context.canEditAnnotations!()).toBe(true); + }); + + it('Can not edit global event when has no org permission', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, orgCanEdit: false }); + expect(context.canEditAnnotations!()).toBe(false); + }); + + it('Can edit dashboard event when has dashboard permission', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, canEdit: true }); + expect(context.canEditAnnotations!('dash-uid')).toBe(true); + }); + + it('Can not edit dashboard event when has no dashboard permission', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, canEdit: false }); + expect(context.canEditAnnotations!('dash-uid')).toBe(false); + }); + }); + + describe('canDeleteAnnotations', () => { + it('Can delete global event when user has org permission', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, canDelete: true }); + expect(context.canDeleteAnnotations!()).toBe(true); + }); + + it('Can not delete global event when has no org permission', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, canDelete: false }); + expect(context.canDeleteAnnotations!()).toBe(false); + }); + + it('Can delete dashboard event when has dashboard permission', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, canDelete: true }); + expect(context.canDeleteAnnotations!('dash-uid')).toBe(true); + }); + + it('Can not delete dashboard event when has no dashboard permission', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, canDelete: false }); + expect(context.canDeleteAnnotations!('dash-uid')).toBe(false); + }); + }); + + describe('onAnnotationCreate', () => { + it('should create annotation', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, canAdd: true }); + + context.onAnnotationCreate!({ from: 100, to: 200, description: 'save it', tags: [] }); + + expect(postFn).toHaveBeenCalledWith('/api/annotations', { + dashboardUID: 'dash-1', + isRegion: true, + panelId: 4, + tags: [], + text: 'save it', + time: 100, + timeEnd: 200, + }); + }); + }); + + describe('onAnnotationUpdate', () => { + it('should update annotation', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, canAdd: true }); + + context.onAnnotationUpdate!({ from: 100, to: 200, id: 'event-id-123', description: 'updated', tags: [] }); + + expect(putFn).toHaveBeenCalledWith('/api/annotations/event-id-123', { + id: 'event-id-123', + dashboardUID: 'dash-1', + isRegion: true, + panelId: 4, + tags: [], + text: 'updated', + time: 100, + timeEnd: 200, + }); + }); + }); + + describe('onAnnotationDelete', () => { + it('should update annotation', () => { + const { context } = buildTestScene({ dashboardCanEdit: true, canAdd: true }); + + context.onAnnotationDelete!('I-do-not-want-you'); + + expect(deleteFn).toHaveBeenCalledWith('/api/annotations/I-do-not-want-you'); + }); + }); + + describe('onAddAdHocFilter', () => { + it('Should add new filter set', () => { + const { scene, context } = buildTestScene({}); + + context.onAddAdHocFilter!({ key: 'hello', value: 'world', operator: '=' }); + + const set = getAdHocFilterSetFor(scene, { uid: 'my-ds-uid' }); + + expect(set.state.filters).toEqual([{ key: 'hello', value: 'world', operator: '=' }]); + }); + + it('Should update and add filter to existing set', () => { + const { scene, context } = buildTestScene({ existingFilterSet: true }); + + const set = getAdHocFilterSetFor(scene, { uid: 'my-ds-uid' }); + + set.setState({ filters: [{ key: 'existing', value: 'world', operator: '=' }] }); + + context.onAddAdHocFilter!({ key: 'hello', value: 'world', operator: '=' }); + + expect(set.state.filters.length).toBe(2); + + // Can update existing filter value without adding a new filter + context.onAddAdHocFilter!({ key: 'hello', value: 'world2', operator: '=' }); + // Verify existing filter value updated + expect(set.state.filters[1].value).toBe('world2'); + }); + }); +}); + +interface SceneOptions { + builtInAnnotationsEnabled?: boolean; + dashboardCanEdit?: boolean; + canAdd?: boolean; + canEdit?: boolean; + canDelete?: boolean; + orgCanEdit?: boolean; + existingFilterSet?: boolean; +} + +function buildTestScene(options: SceneOptions) { + const scene = transformSaveModelToScene({ + dashboard: { + title: 'hello', + uid: 'dash-1', + schemaVersion: 38, + annotations: { + list: [ + { + builtIn: 1, + datasource: { + type: 'grafana', + uid: '-- Grafana --', + }, + enable: options.builtInAnnotationsEnabled ?? false, + hide: true, + iconColor: 'rgba(0, 211, 255, 1)', + name: 'Annotations & Alerts', + target: { refId: 'A' }, + type: 'dashboard', + }, + ], + }, + panels: [ + { + type: 'timeseries', + id: 4, + datasource: { uid: 'my-ds-uid', type: 'prometheus' }, + targets: [], + }, + ], + templating: { + list: options.existingFilterSet + ? [ + { + type: 'adhoc', + name: 'Filters', + datasource: { uid: 'my-ds-uid' }, + }, + ] + : [], + }, + }, + meta: { + canEdit: options.dashboardCanEdit, + annotationsPermissions: { + dashboard: { + canAdd: options.canAdd ?? false, + canEdit: options.canEdit ?? false, + canDelete: options.canDelete ?? false, + }, + organization: { + canAdd: false, + canEdit: options.orgCanEdit ?? false, + canDelete: options.canDelete ?? false, + }, + }, + }, + }); + + const vizPanel = findVizPanelByKey(scene, 'panel-4')!; + const context: PanelContext = { + eventBus: new EventBusSrv(), + eventsScope: 'global', + }; + + setDashboardPanelContext(vizPanel, context); + + return { scene, vizPanel, context }; +} diff --git a/public/app/features/dashboard-scene/scene/setDashboardPanelContext.ts b/public/app/features/dashboard-scene/scene/setDashboardPanelContext.ts new file mode 100644 index 00000000000..5689ee6c4e8 --- /dev/null +++ b/public/app/features/dashboard-scene/scene/setDashboardPanelContext.ts @@ -0,0 +1,188 @@ +import { AnnotationChangeEvent, AnnotationEventUIModel, CoreApp, DataFrame } from '@grafana/data'; +import { AdHocFilterSet, dataLayers, SceneDataLayers, VizPanel } from '@grafana/scenes'; +import { DataSourceRef } from '@grafana/schema'; +import { AdHocFilterItem, PanelContext } from '@grafana/ui'; +import { deleteAnnotation, saveAnnotation, updateAnnotation } from 'app/features/annotations/api'; + +import { getDashboardSceneFor, getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils'; + +import { DashboardScene } from './DashboardScene'; + +export function setDashboardPanelContext(vizPanel: VizPanel, context: PanelContext) { + context.app = CoreApp.Dashboard; + + context.canAddAnnotations = () => { + const dashboard = getDashboardSceneFor(vizPanel); + const builtInLayer = getBuiltInAnnotationsLayer(dashboard); + + // When there is no builtin annotations query we disable the ability to add annotations + if (!builtInLayer || !dashboard.canEditDashboard()) { + return false; + } + + // If RBAC is enabled there are additional conditions to check. + return Boolean(dashboard.state.meta.annotationsPermissions?.dashboard.canAdd); + }; + + context.canEditAnnotations = (dashboardUID?: string) => { + const dashboard = getDashboardSceneFor(vizPanel); + + if (!dashboard.canEditDashboard()) { + return false; + } + + if (dashboardUID) { + return Boolean(dashboard.state.meta.annotationsPermissions?.dashboard.canEdit); + } + + return Boolean(dashboard.state.meta.annotationsPermissions?.organization.canEdit); + }; + + context.canDeleteAnnotations = (dashboardUID?: string) => { + const dashboard = getDashboardSceneFor(vizPanel); + + if (!dashboard.canEditDashboard()) { + return false; + } + + if (dashboardUID) { + return Boolean(dashboard.state.meta.annotationsPermissions?.dashboard.canDelete); + } + + return Boolean(dashboard.state.meta.annotationsPermissions?.organization.canDelete); + }; + + context.onAnnotationCreate = async (event: AnnotationEventUIModel) => { + const dashboard = getDashboardSceneFor(vizPanel); + + const isRegion = event.from !== event.to; + const anno = { + dashboardUID: dashboard.state.uid, + panelId: getPanelIdForVizPanel(vizPanel), + isRegion, + time: event.from, + timeEnd: isRegion ? event.to : 0, + tags: event.tags, + text: event.description, + }; + + await saveAnnotation(anno); + + reRunBuiltInAnnotationsLayer(dashboard); + + context.eventBus.publish(new AnnotationChangeEvent(anno)); + }; + + context.onAnnotationUpdate = async (event: AnnotationEventUIModel) => { + const dashboard = getDashboardSceneFor(vizPanel); + + const isRegion = event.from !== event.to; + const anno = { + id: event.id, + dashboardUID: dashboard.state.uid, + panelId: getPanelIdForVizPanel(vizPanel), + isRegion, + time: event.from, + timeEnd: isRegion ? event.to : 0, + tags: event.tags, + text: event.description, + }; + + await updateAnnotation(anno); + + reRunBuiltInAnnotationsLayer(dashboard); + + context.eventBus.publish(new AnnotationChangeEvent(anno)); + }; + + context.onAnnotationDelete = async (id: string) => { + await deleteAnnotation({ id }); + + reRunBuiltInAnnotationsLayer(getDashboardSceneFor(vizPanel)); + + context.eventBus.publish(new AnnotationChangeEvent({ id })); + }; + + context.onAddAdHocFilter = (newFilter: AdHocFilterItem) => { + const dashboard = getDashboardSceneFor(vizPanel); + + const queryRunner = getQueryRunnerFor(vizPanel); + if (!queryRunner) { + return; + } + + const filterSet = getAdHocFilterSetFor(dashboard, queryRunner.state.datasource); + updateAdHocFilterSet(filterSet, newFilter); + }; + + context.onUpdateData = (frames: DataFrame[]): Promise => { + // TODO + //return onUpdatePanelSnapshotData(this.props.panel, frames); + return Promise.resolve(true); + }; +} + +function getBuiltInAnnotationsLayer(scene: DashboardScene): dataLayers.AnnotationsDataLayer | undefined { + // When there is no builtin annotations query we disable the ability to add annotations + if (scene.state.$data instanceof SceneDataLayers) { + for (const layer of scene.state.$data.state.layers) { + if (layer instanceof dataLayers.AnnotationsDataLayer) { + if (layer.state.isEnabled && layer.state.query.builtIn) { + return layer; + } + } + } + } + + return undefined; +} + +function reRunBuiltInAnnotationsLayer(scene: DashboardScene) { + const layer = getBuiltInAnnotationsLayer(scene); + if (layer) { + layer.runLayer(); + } +} + +export function getAdHocFilterSetFor(scene: DashboardScene, ds: DataSourceRef | null | undefined) { + const controls = scene.state.controls ?? []; + + for (const control of controls) { + if (control instanceof AdHocFilterSet) { + if (control.state.datasource === ds || control.state.datasource?.uid === ds?.uid) { + return control; + } + } + } + + const newSet = new AdHocFilterSet({ datasource: ds }); + + // Add it to the scene + scene.setState({ + controls: [controls[0], newSet, ...controls.slice(1)], + }); + + return newSet; +} + +function updateAdHocFilterSet(filterSet: AdHocFilterSet, newFilter: AdHocFilterItem) { + // Check if we need to update an existing filter + for (const filter of filterSet.state.filters) { + if (filter.key === newFilter.key) { + filterSet.setState({ + filters: filterSet.state.filters.map((f) => { + if (f.key === newFilter.key) { + return newFilter; + } + return f; + }), + }); + return; + } + } + + // Add new filter + filterSet.setState({ + filters: [...filterSet.state.filters, newFilter], + }); +} diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts index 21470a4c2f7..17cc17ea60f 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts @@ -91,8 +91,8 @@ describe('transformSaveModelToScene', () => { expect(scene.state?.$timeRange?.state.weekStart).toEqual('saturday'); expect(scene.state?.$variables?.state.variables).toHaveLength(1); expect(scene.state.controls).toBeDefined(); - expect(scene.state.controls![2]).toBeInstanceOf(AdHocFilterSet); - expect((scene.state.controls![2] as AdHocFilterSet).state.name).toBe('CoolFilters'); + expect(scene.state.controls![1]).toBeInstanceOf(AdHocFilterSet); + expect((scene.state.controls![1] as AdHocFilterSet).state.name).toBe('CoolFilters'); }); it('should apply cursor sync behavior', () => { @@ -668,7 +668,7 @@ describe('transformSaveModelToScene', () => { const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} }); expect(scene.state.$data).toBeInstanceOf(SceneDataLayers); - expect(scene.state.controls![0]).toBeInstanceOf(SceneDataLayerControls); + expect(scene.state.controls![2]).toBeInstanceOf(SceneDataLayerControls); const dataLayers = scene.state.$data as SceneDataLayers; expect(dataLayers.state.layers).toHaveLength(4); diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index feb796f7b89..036aeaa44d2 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -43,6 +43,7 @@ import { panelMenuBehavior } from '../scene/PanelMenuBehavior'; import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem'; import { PanelTimeRange } from '../scene/PanelTimeRange'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; +import { setDashboardPanelContext } from '../scene/setDashboardPanelContext'; import { createPanelDataProvider } from '../utils/createPanelDataProvider'; import { getVizPanelKeyForPanelId } from '../utils/utils'; @@ -200,9 +201,9 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel) } const controls: SceneObject[] = [ - new SceneDataLayerControls(), new VariableValueSelectors({}), ...filtersSets, + new SceneDataLayerControls(), new SceneControlsSpacer(), new SceneTimePicker({}), new SceneRefreshPicker({ @@ -340,6 +341,7 @@ export function buildGridItemForPanel(panel: PanelModel): SceneGridItemLike { menu: new VizPanelMenu({ $behaviors: [panelMenuBehavior], }), + extendPanelContext: setDashboardPanelContext, _UNSAFE_customMigrationHandler: getAngularPanelMigrationHandler(panel), }; diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts index 795a7a50b67..c3ad9af03a4 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts @@ -336,7 +336,6 @@ export function trimDashboardForSnapshot(title: string, time: TimeRange, dash: D enable: annotation.enable, iconColor: annotation.iconColor, type: annotation.type, - // @ts-expect-error builtIn: annotation.builtIn, hide: annotation.hide, // TODO: Remove when we migrate snapshots to snapshot queries. diff --git a/yarn.lock b/yarn.lock index 2a636b6a515..1f2cf16e54c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3226,9 +3226,9 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes@npm:^1.15.0": - version: 1.17.0 - resolution: "@grafana/scenes@npm:1.17.0" +"@grafana/scenes@npm:^1.18.0": + version: 1.18.0 + resolution: "@grafana/scenes@npm:1.18.0" dependencies: "@grafana/e2e-selectors": 10.0.2 react-grid-layout: 1.3.4 @@ -3240,7 +3240,7 @@ __metadata: "@grafana/runtime": 10.0.3 "@grafana/schema": 10.0.3 "@grafana/ui": 10.0.3 - checksum: 0bd8603ae59c199c595a45a7bc31e318a1928b18307469bcd5ee5f83c3aaee89c5c27a1341a479440d928505681ab6a2479809a5cac56e8e93ba7acf06adff93 + checksum: 395e4ef7a01b963df5f3aac3adc2e82c8d409e2679c8ed428137f66ebbbc84570458d1a36db31080622848e86c8e2e82f780e2a1257de0ea61152a41e8650516 languageName: node linkType: hard @@ -17521,7 +17521,7 @@ __metadata: "@grafana/lezer-traceql": 0.0.7 "@grafana/monaco-logql": ^0.0.7 "@grafana/runtime": "workspace:*" - "@grafana/scenes": ^1.15.0 + "@grafana/scenes": ^1.18.0 "@grafana/schema": "workspace:*" "@grafana/tsconfig": ^1.3.0-rc1 "@grafana/ui": "workspace:*"