diff --git a/package.json b/package.json index 999ac1def91..cb61ffb430b 100644 --- a/package.json +++ b/package.json @@ -284,8 +284,8 @@ "@grafana/plugin-ui": "0.10.6", "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", - "@grafana/scenes": "^6.12.0", - "@grafana/scenes-react": "^6.12.0", + "@grafana/scenes": "^6.18.0", + "@grafana/scenes-react": "^6.18.0", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", diff --git a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts index c32616a0b9c..5e8b9cd57ca 100644 --- a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts +++ b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts @@ -112,7 +112,7 @@ describe('DashboardScenePageStateManager v1', () => { const loader = new DashboardScenePageStateManager({}); await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal }); - expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash', undefined); + expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash'); // should use cache second time await loader.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal }); @@ -664,8 +664,8 @@ describe('DashboardScenePageStateManager v2', () => { getDashSpy ); - const options = { version: 2, scopes: [], timeRange: { from: 'now-1h', to: 'now' }, variables: {} }; - await loader.reloadDashboard(options); + const params = { version: 2, scopes: [], from: 'now-1h', to: 'now' }; + await loader.reloadDashboard(params); expect(getDashSpy).toHaveBeenCalledTimes(2); expect(loader.state.dashboard?.state.version).toBe(2); @@ -695,28 +695,11 @@ describe('DashboardScenePageStateManager v2', () => { expect(getDashSpy).toHaveBeenCalledTimes(1); const initialDashboard = loader.state.dashboard; - const mockDashboard: DashboardWithAccessInfo = { - access: {}, - apiVersion: 'v2alpha1', - kind: 'DashboardWithAccessInfo', - metadata: { - name: 'fake-dash', - creationTimestamp: '', - resourceVersion: '1', - generation: 1, - }, - spec: { ...defaultDashboardV2Spec() }, - }; + const params = { version: 1, scopes: [], from: 'now-1h', to: 'now' }; + await loader.reloadDashboard(params); - const fetchDashboardSpy = jest.spyOn(loader, 'fetchDashboard').mockResolvedValue(mockDashboard); - - const options = { version: 1, scopes: [], timeRange: { from: 'now-1h', to: 'now' }, variables: {} }; - await loader.reloadDashboard(options); - - expect(fetchDashboardSpy).toHaveBeenCalledTimes(1); + expect(getDashSpy).toHaveBeenCalledTimes(2); expect(loader.state.dashboard).toBe(initialDashboard); - - fetchDashboardSpy.mockRestore(); }); it('should not use cache if cache version and current dashboard state version differ', async () => { @@ -742,32 +725,32 @@ describe('DashboardScenePageStateManager v2', () => { expect(getDashSpy).toHaveBeenCalledTimes(1); - const mockDashboard: DashboardWithAccessInfo = { - access: {}, - apiVersion: 'v2alpha1', - kind: 'DashboardWithAccessInfo', - metadata: { - name: 'fake-dash', - creationTimestamp: '', - resourceVersion: '1', - generation: 2, + setupDashboardAPI( + { + access: {}, + apiVersion: 'v2alpha1', + kind: 'DashboardWithAccessInfo', + metadata: { + name: 'fake-dash', + creationTimestamp: '', + resourceVersion: '1', + generation: 2, + }, + spec: { ...defaultDashboardV2Spec() }, }, - spec: { ...defaultDashboardV2Spec() }, - }; - - const fetchDashboardSpy = jest.spyOn(loader, 'fetchDashboard').mockResolvedValue(mockDashboard); + getDashSpy + ); // mimic navigating from db1 to db2 and then back to db1, which maintains the cache. but on // db1 load the initial version will be 1. Since the cache is set we also need to verify against the // current dashboard state whether we should reload or not loader.setSceneCache('fake-dash', loader.state.dashboard!.clone({ version: 2 })); - const options = { version: 2, scopes: [], timeRange: { from: 'now-1h', to: 'now' }, variables: {} }; - await loader.reloadDashboard(options); - expect(fetchDashboardSpy).toHaveBeenCalledTimes(1); + const params = { version: 2, scopes: [], from: 'now-1h', to: 'now' }; + await loader.reloadDashboard(params); + + expect(getDashSpy).toHaveBeenCalledTimes(2); expect(loader.state.dashboard?.state.version).toBe(2); - - fetchDashboardSpy.mockRestore(); }); it('should handle errors during reload', async () => { @@ -797,8 +780,8 @@ describe('DashboardScenePageStateManager v2', () => { loader['dashboardLoader'] = mockLoader as unknown as DashboardLoaderSrvV2; - const options = { version: 2, scopes: [], timeRange: { from: 'now-1h', to: 'now' }, variables: {} }; - await loader.reloadDashboard(options); + const params = { version: 2, scopes: [], from: 'now-1h', to: 'now' }; + await loader.reloadDashboard(params); expect(loader.state.loadError).toBeDefined(); expect(loader.state.loadError?.message).toBe('Failed to load dashboard'); @@ -831,9 +814,9 @@ describe('DashboardScenePageStateManager v2', () => { loader['dashboardLoader'] = mockLoader as unknown as DashboardLoaderSrvV2; - const options = { version: 2, scopes: [], timeRange: { from: 'now-1h', to: 'now' }, variables: {} }; + const params = { version: 2, scopes: [], from: 'now-1h', to: 'now' }; - await expect(loader.reloadDashboard(options)).rejects.toThrow(DashboardVersionError); + await expect(loader.reloadDashboard(params)).rejects.toThrow(DashboardVersionError); }); }); }); @@ -852,7 +835,7 @@ describe('UnifiedDashboardScenePageStateManager', () => { const manager = new UnifiedDashboardScenePageStateManager({}); await manager.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal }); - expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash', undefined); + expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash'); expect(manager['activeManager']).toBeInstanceOf(DashboardScenePageStateManager); }); @@ -892,7 +875,7 @@ describe('UnifiedDashboardScenePageStateManager', () => { expect(manager['activeManager']).toBeInstanceOf(DashboardScenePageStateManagerV2); // Reload should now work with v2 manager - const options = { version: 1, scopes: [], timeRange: { from: 'now-1h', to: 'now' }, variables: {} }; + const params = { version: 1, scopes: [], from: 'now-1h', to: 'now' }; // Mock the fetchDashboard method to return a dashboard const v2Manager = manager['activeManager'] as DashboardScenePageStateManagerV2; @@ -910,7 +893,7 @@ describe('UnifiedDashboardScenePageStateManager', () => { spec: { ...defaultDashboardV2Spec() }, }); - await manager.reloadDashboard(options); + await manager.reloadDashboard(params); // Restore the original method v2Manager.fetchDashboard = originalFetchDashboard; @@ -959,21 +942,16 @@ describe('UnifiedDashboardScenePageStateManager', () => { await manager.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal }); - expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash', undefined); + expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash'); expect(manager['activeManager']).toBeInstanceOf(DashboardScenePageStateManager); - loadDashboardMock.mockClear(); + const reloadMock = jest.fn(); + manager['activeManager'].reloadDashboard = reloadMock; - const options = { version: 2, scopes: [], timeRange: { from: 'now-1h', to: 'now' }, variables: {} }; - await manager.reloadDashboard(options); + const params = { version: 2, scopes: [], from: 'now-1h', to: 'now' }; + await manager.reloadDashboard(params); - expect(manager['activeManager']).toBeInstanceOf(DashboardScenePageStateManager); - expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash', { - from: 'now-1h', - to: 'now', - version: 2, - scopes: [], - }); + expect(reloadMock).toHaveBeenCalledTimes(1); }); it('should reload v2 dashboard with v2 manager', async () => { @@ -984,9 +962,10 @@ describe('UnifiedDashboardScenePageStateManager', () => { expect(manager['activeManager']).toBeInstanceOf(DashboardScenePageStateManagerV2); - const options = { version: 2, scopes: [], timeRange: { from: 'now-1h', to: 'now' }, variables: {} }; + const params = { version: 2, scopes: [], from: 'now-1h', to: 'now' }; + try { - await manager.reloadDashboard(options); + await manager.reloadDashboard(params); } catch (e) { expect(e).toBeInstanceOf(Error); expect((e as Error).message).toBe('Method not implemented.'); @@ -1004,11 +983,9 @@ describe('UnifiedDashboardScenePageStateManager', () => { const manager = new UnifiedDashboardScenePageStateManager({}); await manager.loadDashboard({ uid: 'fake-dash', route: DashboardRoutes.Normal }); - expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash', undefined); + expect(loadDashboardMock).toHaveBeenCalledWith('db', '', 'fake-dash'); expect(manager['activeManager']).toBeInstanceOf(DashboardScenePageStateManager); - loadDashboardMock.mockClear(); - const mockDashboard: DashboardDTO = { dashboard: { uid: 'fake-dash', @@ -1018,19 +995,18 @@ describe('UnifiedDashboardScenePageStateManager', () => { meta: {}, }; - const fetchDashboardSpy = jest.spyOn(manager['activeManager'], 'fetchDashboard').mockResolvedValue(mockDashboard); + loadDashboardMock.mockResolvedValue(mockDashboard); // mimic navigating from db1 to db2 and then back to db1, which maintains the cache. but on // db1 load the initial version will be 1. Since the cache is set we also need to verify against the // current dashboard state whether we should reload or not manager.setSceneCache('fake-dash', manager.state.dashboard!.clone({ version: 2 })); - const options = { version: 2, scopes: [], timeRange: { from: 'now-1h', to: 'now' }, variables: {} }; - await manager.reloadDashboard(options); - expect(fetchDashboardSpy).toHaveBeenCalledTimes(1); + const params = { version: 2, scopes: [], from: 'now-1h', to: 'now' }; + await manager.reloadDashboard(params); + + expect(loadDashboardMock).toHaveBeenCalledTimes(2); expect(manager.state.dashboard?.state.version).toBe(2); - - fetchDashboardSpy.mockRestore(); }); }); diff --git a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts index 196809da8e6..edf26b6d1bb 100644 --- a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts +++ b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts @@ -1,5 +1,3 @@ -import { isEqual } from 'lodash'; - import { locationUtil, UrlQueryMap } from '@grafana/data'; import { t } from '@grafana/i18n/internal'; import { config, getBackendSrv, isFetchError, locationService } from '@grafana/runtime'; @@ -48,7 +46,6 @@ export interface LoadError { export interface DashboardScenePageState { dashboard?: DashboardScene; - options?: LoadDashboardOptions; panelEditor?: PanelEditor; isLoading?: boolean; loadError?: LoadError; @@ -73,15 +70,6 @@ export interface LoadDashboardOptions { slug?: string; type?: string; urlFolderUid?: string; - params?: { - version: number; - scopes: string[]; - timeRange: { - from: string; - to: string; - }; - variables: UrlQueryMap; - }; } export type HomeDashboardDTO = DashboardDTO & { @@ -93,7 +81,7 @@ interface DashboardScenePageStateManagerLike { getDashboardFromCache(cacheKey: string): T | null; loadDashboard(options: LoadDashboardOptions): Promise; transformResponseToScene(rsp: T | null, options: LoadDashboardOptions): DashboardScene | null; - reloadDashboard(params: LoadDashboardOptions['params']): Promise; + reloadDashboard(queryParams: UrlQueryMap): Promise; loadSnapshot(slug: string): Promise; setDashboardCache(cacheKey: string, dashboard: T): void; clearSceneCache(): void; @@ -108,7 +96,7 @@ abstract class DashboardScenePageStateManagerBase implements DashboardScenePageStateManagerLike { abstract fetchDashboard(options: LoadDashboardOptions): Promise; - abstract reloadDashboard(params: LoadDashboardOptions['params']): Promise; + abstract reloadDashboard(queryParams: UrlQueryMap): Promise; abstract transformResponseToScene(rsp: T | null, options: LoadDashboardOptions): DashboardScene | null; abstract loadSnapshotScene(slug: string): Promise; @@ -274,7 +262,7 @@ abstract class DashboardScenePageStateManagerBase restoreDashboardStateFromLocalStorage(dashboard); } - this.setState({ dashboard: dashboard, isLoading: false, options }); + this.setState({ dashboard: dashboard, isLoading: false }); const measure = stopMeasure(LOAD_SCENE_MEASUREMENT); const queryController = sceneGraph.getQueryController(dashboard); @@ -418,16 +406,13 @@ export class DashboardScenePageStateManager extends DashboardScenePageStateManag uid, route, urlFolderUid, - params, }: LoadDashboardOptions): Promise { const cacheKey = route === DashboardRoutes.Home ? HOME_DASHBOARD_CACHE_KEY : uid; - if (!params) { - const cachedDashboard = this.getDashboardFromCache(cacheKey); + const cachedDashboard = this.getDashboardFromCache(cacheKey); - if (cachedDashboard) { - return cachedDashboard; - } + if (cachedDashboard) { + return cachedDashboard; } let rsp: DashboardDTO; @@ -454,17 +439,7 @@ export class DashboardScenePageStateManager extends DashboardScenePageStateManag return await dashboardLoaderSrv.loadDashboard('public', '', uid); } default: - const queryParams = params - ? { - version: params.version, - scopes: params.scopes, - from: params.timeRange.from, - to: params.timeRange.to, - ...params.variables, - } - : undefined; - - rsp = await dashboardLoaderSrv.loadDashboard(type || 'db', slug || '', uid, queryParams); + rsp = await dashboardLoaderSrv.loadDashboard(type || 'db', slug || '', uid); if (route === DashboardRoutes.Embedded) { rsp.meta.isEmbedded = true; @@ -504,30 +479,20 @@ export class DashboardScenePageStateManager extends DashboardScenePageStateManag return rsp; } - public async reloadDashboard(params: LoadDashboardOptions['params']) { - const stateOptions = this.state.options; + public async reloadDashboard(queryParams: UrlQueryMap): Promise { + const dashboard = this.state.dashboard; - if (!stateOptions) { + if (!dashboard || !dashboard.state.uid) { return; } - const options = { - ...stateOptions, - params, - }; - - if ( - isEqual(options.params?.variables, stateOptions.params?.variables) && - isEqual(options.params?.scopes, stateOptions.params?.scopes) - ) { - return; - } + const uid = dashboard.state.uid; try { this.setState({ isLoading: true }); - const rsp = await this.fetchDashboard(options); - const fromCache = this.getSceneFromCache(options.uid); + const rsp = await dashboardLoaderSrv.loadDashboard('db', dashboard.state.meta.slug, uid, queryParams); + const fromCache = this.getSceneFromCache(uid); // check if cached db version is same as both // response and current db state. There are scenarios where they can differ @@ -564,16 +529,16 @@ export class DashboardScenePageStateManager extends DashboardScenePageStateManag const scene = transformSaveModelToScene(rsp); // we need to call and restore dashboard state on every reload that pulls a new dashboard version - if (config.featureToggles.preserveDashboardStateWhenNavigating && Boolean(options.uid)) { + if (config.featureToggles.preserveDashboardStateWhenNavigating && Boolean(uid)) { restoreDashboardStateFromLocalStorage(scene); } - this.setSceneCache(options.uid, scene); - - this.setState({ dashboard: scene, isLoading: false, options }); + this.setSceneCache(uid, scene); + this.setState({ dashboard: scene, isLoading: false }); } catch (err) { const status = getStatusFromError(err); const message = getMessageFromError(err); + this.setState({ isLoading: false, loadError: { @@ -581,6 +546,7 @@ export class DashboardScenePageStateManager extends DashboardScenePageStateManag status, }, }); + if (err instanceof DashboardVersionError) { throw err; } @@ -634,15 +600,14 @@ export class DashboardScenePageStateManagerV2 extends DashboardScenePageStateMan uid, route, urlFolderUid, - params, }: LoadDashboardOptions): Promise | null> { const cacheKey = route === DashboardRoutes.Home ? HOME_DASHBOARD_CACHE_KEY : uid; - if (!params) { - const cachedDashboard = this.getDashboardFromCache(cacheKey); - if (cachedDashboard) { - return cachedDashboard; - } + + const cachedDashboard = this.getDashboardFromCache(cacheKey); + if (cachedDashboard) { + return cachedDashboard; } + let rsp: DashboardWithAccessInfo; try { switch (route) { @@ -656,16 +621,8 @@ export class DashboardScenePageStateManagerV2 extends DashboardScenePageStateMan return await this.dashboardLoader.loadDashboard('public', '', uid); } default: - const queryParams = params - ? { - version: params.version, - scopes: params.scopes, - from: params.timeRange.from, - to: params.timeRange.to, - ...params.variables, - } - : undefined; - rsp = await this.dashboardLoader.loadDashboard(type || 'db', slug || '', uid, queryParams); + rsp = await this.dashboardLoader.loadDashboard(type || 'db', slug || '', uid); + if (route === DashboardRoutes.Embedded) { throw new Error('Method not implemented.'); // rsp.meta.isEmbedded = true; @@ -699,30 +656,20 @@ export class DashboardScenePageStateManagerV2 extends DashboardScenePageStateMan return rsp; } - public async reloadDashboard(params: LoadDashboardOptions['params']): Promise { - const stateOptions = this.state.options; + public async reloadDashboard(queryParams: UrlQueryMap): Promise { + const dashboard = this.state.dashboard; - if (!stateOptions) { + if (!dashboard || !dashboard.state.uid) { return; } - const options = { - ...stateOptions, - params, - }; - - if ( - isEqual(options.params?.variables, stateOptions.params?.variables) && - isEqual(options.params?.scopes, stateOptions.params?.scopes) - ) { - return; - } + const uid = dashboard.state.uid; try { this.setState({ isLoading: true }); - const rsp = await this.fetchDashboard(options); - const fromCache = this.getSceneFromCache(options.uid); + const rsp = await this.dashboardLoader.loadDashboard('db', dashboard.state.meta.slug, uid, queryParams); + const fromCache = this.getSceneFromCache(uid); if ( fromCache && @@ -750,13 +697,13 @@ export class DashboardScenePageStateManagerV2 extends DashboardScenePageStateMan const scene = transformSaveModelSchemaV2ToScene(rsp); // we need to call and restore dashboard state on every reload that pulls a new dashboard version - if (config.featureToggles.preserveDashboardStateWhenNavigating && Boolean(options.uid)) { + if (config.featureToggles.preserveDashboardStateWhenNavigating && Boolean(uid)) { restoreDashboardStateFromLocalStorage(scene); } - this.setSceneCache(options.uid, scene); + this.setSceneCache(uid, scene); - this.setState({ dashboard: scene, isLoading: false, options }); + this.setState({ dashboard: scene, isLoading: false }); } catch (err) { const status = getStatusFromError(err); const message = getMessageFromError(err); @@ -815,8 +762,8 @@ export class UnifiedDashboardScenePageStateManager extends DashboardScenePageSta ); } - public async reloadDashboard(params: LoadDashboardOptions['params']) { - return this.withVersionHandling((manager) => manager.reloadDashboard.call(this, params)); + public async reloadDashboard(queryParams: UrlQueryMap) { + return this.withVersionHandling((manager) => manager.reloadDashboard.call(this, queryParams)); } public getDashboardFromCache(uid: string) { @@ -864,6 +811,14 @@ export class UnifiedDashboardScenePageStateManager extends DashboardScenePageSta this.cache = {}; } + public getSceneFromCache(key: string) { + return this.activeManager.getSceneFromCache(key); + } + + public setSceneCache(cacheKey: string, scene: DashboardScene): void { + this.activeManager.setSceneCache(cacheKey, scene); + } + public getCache() { return this.activeManager.getCache(); } diff --git a/public/app/features/dashboard-scene/scene/DashboardReloadBehavior.ts b/public/app/features/dashboard-scene/scene/DashboardReloadBehavior.ts index bd75781f091..55b8cd1c855 100644 --- a/public/app/features/dashboard-scene/scene/DashboardReloadBehavior.ts +++ b/public/app/features/dashboard-scene/scene/DashboardReloadBehavior.ts @@ -5,9 +5,11 @@ import { sceneGraph, SceneObjectBase, SceneObjectState, - SceneTimeRangeLike, + sceneUtils, + SceneVariable, VariableDependencyConfig, } from '@grafana/scenes'; +import { createLogger } from '@grafana/ui'; import { getDashboardScenePageStateManager } from '../pages/DashboardScenePageStateManager'; @@ -16,12 +18,12 @@ import { DashboardScene } from './DashboardScene'; export interface DashboardReloadBehaviorState extends SceneObjectState { reloadOnParamsChange?: boolean; uid?: string; - version?: number; } export class DashboardReloadBehavior extends SceneObjectBase { - private _timeRange: SceneTimeRangeLike | undefined; private _dashboardScene: DashboardScene | undefined; + private _prevState?: UrlQueryMap; + private _log = createLogger('DashboardReloadBehavior'); constructor(state: DashboardReloadBehaviorState) { super(state); @@ -37,61 +39,70 @@ export class DashboardReloadBehavior extends SceneObjectBase { + this._log.logger('onAnyVariableChanged', variable.state.name, JSON.stringify(variable.getValue())); + this.reloadDashboard(); + }, dependsOnScopes: true, }); - - this._subs.add( - this._timeRange.subscribeToState((newState, prevState) => { - if (!isEqual(newState.value, prevState.value)) { - this.reloadDashboard(); - } - }) - ); - - this.reloadDashboard(); }); } + private getCurrentState(): UrlQueryMap { + const scopes = sceneGraph.getScopes(this) ?? []; + const timeRange = sceneGraph.getTimeRange(this).state.value; + + return { + scopes: scopes.map((scope) => scope.metadata.name), + from: timeRange.from.toISOString(), + to: timeRange.to.toISOString(), + ...sceneUtils.getUrlState(this._dashboardScene?.state.$variables!), + version: this._dashboardScene?.state.version, + }; + } + private isEditing() { return !!this._dashboardScene?.state.isEditing; } private isWaitingForVariables() { const varSet = sceneGraph.getVariables(this.parent!); - return varSet.state.variables.some((variable) => varSet.isVariableLoadingOrWaitingToUpdate(variable)); } private reloadDashboard() { if (this.isEditing() || this.isWaitingForVariables()) { + this._log.logger('DashboardReloadBehavior reloadDashboard isEditing or waiting for variables, skipping reload'); return; } + const newState = this.getCurrentState(); + const prevState = this._prevState ?? {}; + + // Ignore time range changes for now + prevState.from = newState.from; + prevState.to = newState.to; + + const stateChanged = !isEqual(newState, this._prevState); + + this._log.logger( + `DashboardReloadBehavior reloadDashboard stateChanged ${stateChanged ? 'true' : 'false'}`, + this._prevState, + newState + ); + + if (!stateChanged) { + return; + } + + this._prevState = newState; + // This is wrapped in setTimeout in order to allow variables and scopes to be set in the URL before actually reloading the dashboard setTimeout(() => { - const scopes = sceneGraph.getScopes(this) ?? []; - - getDashboardScenePageStateManager().reloadDashboard({ - version: this.state.version!, - 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(), - to: this._timeRange!.state.value.to.toISOString(), - }, - variables: sceneGraph.getVariables(this).state.variables.reduce( - (acc, variable) => ({ - ...acc, - ...variable.urlSync?.getUrlState(), - }), - {} - ), - }); + getDashboardScenePageStateManager().reloadDashboard(newState); }); } } diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts index dcc683771f0..fe2ef3ec1f9 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts @@ -199,7 +199,6 @@ export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo scope.metadata.name); if (scopes.length) { // We only update scopes but never delete them. This is to keep the scopes in memory if user navigates to // page that does not use scopes (like from dashboard to dashboard list back to dashboard). If user @@ -93,12 +94,7 @@ export class ScopesService implements ScopesContextValue { const oldScopeNames = prev.selectedScopes.map((scope) => scope.scope.metadata.name); const newScopeNames = state.selectedScopes.map((scope) => scope.scope.metadata.name); if (!isEqual(oldScopeNames, newScopeNames)) { - this.locationService.partial( - { - scopes: newScopeNames, - }, - true - ); + this.locationService.partial({ scopes: newScopeNames }, true); } }) ); diff --git a/public/app/features/scopes/selector/ScopesSelectorService.ts b/public/app/features/scopes/selector/ScopesSelectorService.ts index edccb3cc5e1..aae713b5f02 100644 --- a/public/app/features/scopes/selector/ScopesSelectorService.ts +++ b/public/app/features/scopes/selector/ScopesSelectorService.ts @@ -212,7 +212,10 @@ export class ScopesSelectorService extends ScopesServiceBase { - if (isEqual(treeScopes, getTreeScopesFromSelectedScopes(this.state.selectedScopes))) { + const newNames = treeScopes.map(({ scopeName }) => scopeName); + const currentNames = this.state.selectedScopes.map((scope) => scope.scope.metadata.name); + + if (isEqual(newNames, currentNames)) { return; } diff --git a/public/app/features/scopes/tests/dashboardReload.test.ts b/public/app/features/scopes/tests/dashboardReload.test.ts index 97b46ee3235..612ad6d4209 100644 --- a/public/app/features/scopes/tests/dashboardReload.test.ts +++ b/public/app/features/scopes/tests/dashboardReload.test.ts @@ -17,7 +17,9 @@ jest.mock('@grafana/runtime', () => ({ describe('Dashboard reload', () => { let dashboardReloadSpy: jest.SpyInstance; - + beforeEach(() => { + jest.spyOn(console, 'log').mockImplementation(); + }); beforeAll(() => { config.featureToggles.scopeFilters = true; config.featureToggles.groupByVariable = true; diff --git a/yarn.lock b/yarn.lock index 32e9d56f28c..3c5a29cc495 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3443,11 +3443,11 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes-react@npm:^6.12.0": - version: 6.16.0 - resolution: "@grafana/scenes-react@npm:6.16.0" +"@grafana/scenes-react@npm:^6.18.0": + version: 6.18.0 + resolution: "@grafana/scenes-react@npm:6.18.0" dependencies: - "@grafana/scenes": "npm:6.16.0" + "@grafana/scenes": "npm:6.18.0" lru-cache: "npm:^10.2.2" react-use: "npm:^17.4.0" peerDependencies: @@ -3459,13 +3459,13 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/a1a7824b1c8f05fd5149d93a5aa0037014d5ff3e777669d5212c2426812973510dbeea225cb4f5d5e5a9038910a590d6d170b5ebe681c5be6e2fd951a3fabc07 + checksum: 10/7173458c5a4c128f3026fc3ad5b7cb135cd4a99340939c2c6f3081b2f8d7028347f27ba79259c0f8aa2315f59abc5cccdd2110b3a343b2f4c2ab576236dc60e4 languageName: node linkType: hard -"@grafana/scenes@npm:6.16.0, @grafana/scenes@npm:^6.12.0": - version: 6.16.0 - resolution: "@grafana/scenes@npm:6.16.0" +"@grafana/scenes@npm:6.18.0, @grafana/scenes@npm:^6.18.0": + version: 6.18.0 + resolution: "@grafana/scenes@npm:6.18.0" dependencies: "@floating-ui/react": "npm:^0.26.16" "@leeoniya/ufuzzy": "npm:^1.0.16" @@ -3483,7 +3483,7 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/e379ba53fc835b6b3a6eb19d7527a6e775c10684d4eed650f85e5ad8c3fdb682fe20428c2ea467e66fc9050b19b19eb46ebed2ef5253dd1c3f6a58fd04d42205 + checksum: 10/9363488c0a5f57745497a5887ec50a3fdee7c93a3b40c83e000470bd60b437d9614d1cf2d62209f1415e6c58432f2d2dc1287c1c762381582fc21b986722656b languageName: node linkType: hard @@ -17777,8 +17777,8 @@ __metadata: "@grafana/plugin-ui": "npm:0.10.6" "@grafana/prometheus": "workspace:*" "@grafana/runtime": "workspace:*" - "@grafana/scenes": "npm:^6.12.0" - "@grafana/scenes-react": "npm:^6.12.0" + "@grafana/scenes": "npm:^6.18.0" + "@grafana/scenes-react": "npm:^6.18.0" "@grafana/schema": "workspace:*" "@grafana/sql": "workspace:*" "@grafana/test-utils": "workspace:*"