diff --git a/package.json b/package.json index 2eea4b64be7..28ba057e09e 100644 --- a/package.json +++ b/package.json @@ -284,8 +284,8 @@ "@grafana/plugin-ui": "0.10.5", "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", - "@grafana/scenes": "6.10.4", - "@grafana/scenes-react": "6.10.4", + "@grafana/scenes": "^6.12.0", + "@grafana/scenes-react": "^6.12.0", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.test.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.test.tsx index fb0d3eb0524..7f2b930b208 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.test.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.test.tsx @@ -144,6 +144,9 @@ const dashboard = { to: 'now', }, timepicker: { refresh_intervals: ['5s', '30s', '1m'] }, + templating: { + list: [], + }, meta: { canSave: true, folderId: 1, diff --git a/public/app/features/dashboard-scene/scene/DashboardReloadBehavior.ts b/public/app/features/dashboard-scene/scene/DashboardReloadBehavior.ts index ba2bffd15cd..bd75781f091 100644 --- a/public/app/features/dashboard-scene/scene/DashboardReloadBehavior.ts +++ b/public/app/features/dashboard-scene/scene/DashboardReloadBehavior.ts @@ -5,7 +5,6 @@ import { sceneGraph, SceneObjectBase, SceneObjectState, - SceneScopesBridge, SceneTimeRangeLike, VariableDependencyConfig, } from '@grafana/scenes'; @@ -22,7 +21,6 @@ export interface DashboardReloadBehaviorState extends SceneObjectState { export class DashboardReloadBehavior extends SceneObjectBase { private _timeRange: SceneTimeRangeLike | undefined; - private _scopesBridge: SceneScopesBridge | undefined; private _dashboardScene: DashboardScene | undefined; constructor(state: DashboardReloadBehaviorState) { @@ -40,17 +38,11 @@ export class DashboardReloadBehavior extends SceneObjectBase { - if (shouldReload) { - this.reloadDashboard(); - } + dependsOnScopes: true, }); this._subs.add( @@ -82,9 +74,11 @@ export class DashboardReloadBehavior extends SceneObjectBase { + const scopes = sceneGraph.getScopes(this) ?? []; + getDashboardScenePageStateManager().reloadDashboard({ version: this.state.version!, - scopes: this._scopesBridge?.getValue().map((scope) => scope.metadata.name) ?? [], + scopes: scopes.map((scope) => scope.metadata.name), // We're not using the getUrlState from timeRange since it makes more sense to pass the absolute timestamps as opposed to relative time timeRange: { from: this._timeRange!.state.value.from.toISOString(), diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 3b986d7d85e..1206b787fdd 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -8,7 +8,6 @@ import { SceneObjectBase, SceneObjectRef, SceneObjectState, - SceneScopesBridge, SceneTimeRange, sceneUtils, SceneVariable, @@ -142,7 +141,6 @@ export interface DashboardSceneState extends SceneObjectState { panelsPerRow?: number; /** options pane */ editPane: DashboardEditPane; - scopesBridge: SceneScopesBridge | undefined; /** Manages dragging/dropping of layout items */ layoutOrchestrator?: DashboardLayoutOrchestrator; } @@ -199,7 +197,6 @@ export class DashboardScene extends SceneObjectBase impleme links: state.links ?? [], ...state, editPane: new DashboardEditPane(), - scopesBridge: config.featureToggles.scopeFilters ? new SceneScopesBridge({}) : undefined, layoutOrchestrator: new DashboardLayoutOrchestrator(), }); @@ -212,8 +209,6 @@ export class DashboardScene extends SceneObjectBase impleme } private _activationHandler() { - this.state.scopesBridge?.setEnabled(true); - let prevSceneContext = window.__grafanaSceneContext; const isNew = locationService.getLocation().pathname === '/dashboard/new'; @@ -222,7 +217,6 @@ export class DashboardScene extends SceneObjectBase impleme this._initializePanelSearch(); if (this.state.isEditing) { - this.state.scopesBridge?.setReadOnly(true); this._initialUrlState = locationService.getLocation(); this._changeTracker.startTrackingChanges(); } @@ -247,8 +241,6 @@ export class DashboardScene extends SceneObjectBase impleme // Deactivation logic return () => { - this.state.scopesBridge?.setReadOnly(false); - this.state.scopesBridge?.setEnabled(false); window.__grafanaSceneContext = prevSceneContext; clearKeyBindings(); this._changeTracker.terminate(); @@ -281,9 +273,6 @@ export class DashboardScene extends SceneObjectBase impleme // Propagate change edit mode change to children this.state.body.editModeChanged?.(true); - // Propagate edit mode to scopes - this.state.scopesBridge?.setReadOnly(true); - this._changeTracker.startTrackingChanges(); }; @@ -324,7 +313,6 @@ export class DashboardScene extends SceneObjectBase impleme if (!this.state.isDirty || skipConfirm) { this.exitEditModeConfirmed(restoreInitialState || this.state.isDirty); - this.state.scopesBridge?.setReadOnly(false); return; } @@ -336,7 +324,6 @@ export class DashboardScene extends SceneObjectBase impleme yesText: 'Discard', onConfirm: () => { this.exitEditModeConfirmed(); - this.state.scopesBridge?.setReadOnly(false); }, }) ); diff --git a/public/app/features/dashboard-scene/scene/DashboardSceneRenderer.tsx b/public/app/features/dashboard-scene/scene/DashboardSceneRenderer.tsx index d13c6734a62..bd300a5bbd3 100644 --- a/public/app/features/dashboard-scene/scene/DashboardSceneRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardSceneRenderer.tsx @@ -1,7 +1,8 @@ -import { useEffect, useMemo } from 'react'; +import { useContext, useEffect, useMemo } from 'react'; import { useLocation, useParams } from 'react-router-dom-v5-compat'; import { PageLayoutType } from '@grafana/data'; +import { ScopesContext } from '@grafana/runtime'; import { SceneComponentProps } from '@grafana/scenes'; import { Page } from 'app/core/components/Page/Page'; import { getNavModel } from 'app/core/selectors/navModel'; @@ -22,11 +23,11 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps state.navIndex); const pageNav = model.getPageNav(location, navIndex); const bodyToRender = model.getBodyToRender(); @@ -47,10 +48,21 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps { + if (scopesContext && isEditing) { + scopesContext.setReadOnly(true); + + return () => { + scopesContext.setReadOnly(false); + }; + } + + return; + }, [scopesContext, isEditing]); + if (editview) { return ( <> - {scopesBridge && } {overlay && } @@ -69,7 +81,6 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps {layoutOrchestrator && } - {scopesBridge && } {editPanel && } {!editPanel && ( ; + } + return null; } diff --git a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts index be9370dac31..9747eb8600d 100644 --- a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts +++ b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts @@ -4,6 +4,7 @@ import { MultiValueVariable, SceneVariables, sceneUtils, + SceneVariable, } from '@grafana/scenes'; import { VariableModel, @@ -47,6 +48,7 @@ import { export function sceneVariablesSetToVariables(set: SceneVariables, keepQueryOptions?: boolean) { const variables: VariableModel[] = []; + for (const variable of set.state.variables) { const commonProperties = { name: variable.state.name, @@ -191,6 +193,8 @@ export function sceneVariablesSetToVariables(set: SceneVariables, keepQueryOptio filters: validateFiltersOrigin(variable.state.filters), defaultKeys: variable.state.defaultKeys, }); + } else if (variable.state.type === 'system') { + // Not persisted } else { throw new Error('Unsupported variable type'); } @@ -435,6 +439,8 @@ export function sceneVariablesSetToSchemaV2Variables( }, }; variables.push(adhocVariable); + } else if (variable.state.type === 'system') { + // Do nothing } else { throw new Error('Unsupported variable type: ' + variable.state.type); } @@ -459,3 +465,7 @@ function validateFiltersOrigin(filters?: SceneAdHocFilterWithLabels[]): AdHocFil }) || [] ); } + +export function isVariableEditable(variable: SceneVariable) { + return variable.state.type !== 'system'; +} diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts index 99c858ee5df..df62ce83f26 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts @@ -15,6 +15,7 @@ import { SceneTimeRange, SceneVariable, SceneVariableSet, + ScopesVariable, TextBoxVariable, } from '@grafana/scenes'; import { @@ -227,17 +228,10 @@ export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo Boolean(v)); + if (config.featureToggles.scopeFilters) { + variableObjects.push(new ScopesVariable({ enable: true })); + } + return new SceneVariableSet({ variables: variableObjects, }); diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index 401868932d3..c832d48709d 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -181,17 +181,10 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel, const uid = oldModel.uid; const serializerVersion = config.featureToggles.dashboardNewLayouts ? 'v2' : 'v1'; - if (oldModel.templating?.list?.length) { - if (oldModel.meta.isSnapshot) { - variables = createVariablesForSnapshot(oldModel); - } else { - variables = createVariablesForDashboard(oldModel); - } + if (oldModel.meta.isSnapshot) { + variables = createVariablesForSnapshot(oldModel); } else { - // Create empty variable set - variables = new SceneVariableSet({ - variables: [], - }); + variables = createVariablesForDashboard(oldModel); } if (oldModel.annotations?.list?.length && !oldModel.isSnapshot()) { diff --git a/public/app/features/dashboard-scene/settings/variables/VariableEditorList.tsx b/public/app/features/dashboard-scene/settings/variables/VariableEditorList.tsx index b12cb469f28..498067122df 100644 --- a/public/app/features/dashboard-scene/settings/variables/VariableEditorList.tsx +++ b/public/app/features/dashboard-scene/settings/variables/VariableEditorList.tsx @@ -9,6 +9,7 @@ import { reportInteraction } from '@grafana/runtime'; import { SceneVariable, SceneVariableState } from '@grafana/scenes'; import { useStyles2, Stack, Button, EmptyState, TextLink } from '@grafana/ui'; +import { isVariableEditable } from '../../serialization/sceneVariablesSetToVariables'; import { VariablesDependenciesButton } from '../../variables/VariablesDependenciesButton'; import { UsagesToNetwork, VariableUsageTree } from '../../variables/utils'; @@ -46,7 +47,9 @@ export function VariableEditorList({ onChangeOrder(result.source.index, result.destination.index); }; - return variables.length <= 0 ? ( + const editableVariables = variables.filter(isVariableEditable); + + return editableVariables.length <= 0 ? ( ) : ( @@ -71,6 +74,10 @@ export function VariableEditorList({ {(provided) => ( {variables.map((variableScene, index) => { + if (!isVariableEditable(variableScene)) { + return null; + } + const variableState = variableScene.state; return ( Boolean(v)); + if (config.featureToggles.scopeFilters) { + variableObjects.push(new ScopesVariable({ enable: true })); + } + return new SceneVariableSet({ variables: variableObjects, }); diff --git a/public/app/features/scopes/tests/utils/render.tsx b/public/app/features/scopes/tests/utils/render.tsx index 391331eb5f1..e4c6993928b 100644 --- a/public/app/features/scopes/tests/utils/render.tsx +++ b/public/app/features/scopes/tests/utils/render.tsx @@ -197,7 +197,7 @@ export async function renderDashboard( ); - await waitFor(() => expect(sceneGraph.getScopesBridge(scene)).toBeDefined()); + await waitFor(() => expect(sceneGraph.getScopes(scene)).toBeDefined()); return { scene, diff --git a/yarn.lock b/yarn.lock index 0f9565dd714..20b60a87324 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3474,11 +3474,11 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes-react@npm:6.10.4": - version: 6.10.4 - resolution: "@grafana/scenes-react@npm:6.10.4" +"@grafana/scenes-react@npm:^6.12.0": + version: 6.12.0 + resolution: "@grafana/scenes-react@npm:6.12.0" dependencies: - "@grafana/scenes": "npm:6.10.4" + "@grafana/scenes": "npm:6.12.0" lru-cache: "npm:^10.2.2" react-use: "npm:^17.4.0" peerDependencies: @@ -3490,13 +3490,13 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/73aee4eca47a27e1fbb7a8f09ddf6a365b121cb361b01c7daabf698677f192c188de0dd64a424db4195139f625660f40371619abbb95a999e14c94e313e25755 + checksum: 10/56113394d169c96537a46ca5dc4c62eda54c6addd348c9e3321a122a6d01c19a14c57b04945ee3d836638f3dc1c1941f7647fa649495226432664e0ff8bd0af0 languageName: node linkType: hard -"@grafana/scenes@npm:6.10.4": - version: 6.10.4 - resolution: "@grafana/scenes@npm:6.10.4" +"@grafana/scenes@npm:6.12.0, @grafana/scenes@npm:^6.12.0": + version: 6.12.0 + resolution: "@grafana/scenes@npm:6.12.0" dependencies: "@floating-ui/react": "npm:^0.26.16" "@leeoniya/ufuzzy": "npm:^1.0.16" @@ -3514,7 +3514,7 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/ffd51ad71fe3b89c3cd16bfaa0f11e003bf11e4a4fb7513c5c55fdc5e569f51dcf9fb70cb125e63b5b2269d68e86a961769e07a8078cad70163de2c2ed779ef7 + checksum: 10/1bcddada8bf626d552aaa29ae4f916a50968bb36761285b0517500f50d8f52314148a8ed73419f381f8e5b901308cdf2450f0b82754691af512a2cff9f52b6bb languageName: node linkType: hard @@ -17717,8 +17717,8 @@ __metadata: "@grafana/plugin-ui": "npm:0.10.5" "@grafana/prometheus": "workspace:*" "@grafana/runtime": "workspace:*" - "@grafana/scenes": "npm:6.10.4" - "@grafana/scenes-react": "npm:6.10.4" + "@grafana/scenes": "npm:^6.12.0" + "@grafana/scenes-react": "npm:^6.12.0" "@grafana/schema": "workspace:*" "@grafana/sql": "workspace:*" "@grafana/test-utils": "workspace:*"