Change folder breadcrumb on folder change in general settings (#86342)
* Change folder breadcrumb on folder change in general settings * tests and refactor * refactor to fix broken tests * fix test
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
import { locationUtil } from '@grafana/data';
|
import { locationUtil } from '@grafana/data';
|
||||||
import { config, getBackendSrv, isFetchError, locationService } from '@grafana/runtime';
|
import { config, getBackendSrv, isFetchError, locationService } from '@grafana/runtime';
|
||||||
import { updateNavIndex } from 'app/core/actions';
|
|
||||||
import { StateManagerBase } from 'app/core/services/StateManagerBase';
|
import { StateManagerBase } from 'app/core/services/StateManagerBase';
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
|
||||||
import { default as localStorageStore } from 'app/core/store';
|
import { default as localStorageStore } from 'app/core/store';
|
||||||
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 { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||||
@@ -10,8 +8,6 @@ import {
|
|||||||
DASHBOARD_FROM_LS_KEY,
|
DASHBOARD_FROM_LS_KEY,
|
||||||
removeDashboardToFetchFromLocalStorage,
|
removeDashboardToFetchFromLocalStorage,
|
||||||
} from 'app/features/dashboard/state/initDashboard';
|
} from 'app/features/dashboard/state/initDashboard';
|
||||||
import { buildNavModel } from 'app/features/folders/state/navModel';
|
|
||||||
import { store } from 'app/store/store';
|
|
||||||
import { DashboardDTO, DashboardRoutes } from 'app/types';
|
import { DashboardDTO, DashboardRoutes } from 'app/types';
|
||||||
|
|
||||||
import { PanelEditor } from '../panel-edit/PanelEditor';
|
import { PanelEditor } from '../panel-edit/PanelEditor';
|
||||||
@@ -19,6 +15,8 @@ import { DashboardScene } from '../scene/DashboardScene';
|
|||||||
import { buildNewDashboardSaveModel } from '../serialization/buildNewDashboardSaveModel';
|
import { buildNewDashboardSaveModel } from '../serialization/buildNewDashboardSaveModel';
|
||||||
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
|
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
|
||||||
|
|
||||||
|
import { updateNavModel } from './utils';
|
||||||
|
|
||||||
export interface DashboardScenePageState {
|
export interface DashboardScenePageState {
|
||||||
dashboard?: DashboardScene;
|
dashboard?: DashboardScene;
|
||||||
panelEditor?: PanelEditor;
|
panelEditor?: PanelEditor;
|
||||||
@@ -127,7 +125,9 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate nav model in global store according to the folder
|
// Populate nav model in global store according to the folder
|
||||||
await this.initNavModel(rsp);
|
if (rsp.meta.folderUid) {
|
||||||
|
await updateNavModel(rsp.meta.folderUid);
|
||||||
|
}
|
||||||
|
|
||||||
// Do not cache new dashboards
|
// Do not cache new dashboards
|
||||||
this.dashboardCache = { dashboard: rsp, ts: Date.now(), cacheKey };
|
this.dashboardCache = { dashboard: rsp, ts: Date.now(), cacheKey };
|
||||||
@@ -235,20 +235,6 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async initNavModel(dashboard: DashboardDTO) {
|
|
||||||
// only the folder API has information about ancestors
|
|
||||||
// get parent folder (if it exists) and put it in the store
|
|
||||||
// this will be used to populate the full breadcrumb trail
|
|
||||||
if (dashboard.meta.folderUid) {
|
|
||||||
try {
|
|
||||||
const folder = await backendSrv.getFolderByUid(dashboard.meta.folderUid);
|
|
||||||
store.dispatch(updateNavIndex(buildNavModel(folder)));
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('Error fetching parent folder', dashboard.meta.folderUid, 'for dashboard', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public clearState() {
|
public clearState() {
|
||||||
getDashboardSrv().setCurrent(undefined);
|
getDashboardSrv().setCurrent(undefined);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
|
import { configureStore } from 'app/store/configureStore';
|
||||||
|
|
||||||
|
import { updateNavModel } from './utils';
|
||||||
|
|
||||||
|
describe('utils', () => {
|
||||||
|
it('Should update nav model', async () => {
|
||||||
|
const reduxStore = configureStore();
|
||||||
|
|
||||||
|
jest.spyOn(backendSrv, 'getFolderByUid').mockResolvedValue({
|
||||||
|
id: 1,
|
||||||
|
uid: 'new-folder',
|
||||||
|
title: 'NewFolder',
|
||||||
|
url: '',
|
||||||
|
canAdmin: true,
|
||||||
|
canDelete: true,
|
||||||
|
canEdit: true,
|
||||||
|
canSave: true,
|
||||||
|
created: '',
|
||||||
|
createdBy: '',
|
||||||
|
hasAcl: false,
|
||||||
|
updated: '',
|
||||||
|
updatedBy: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(reduxStore.getState().navIndex[`folder-dashboards-new-folder`]).toBeUndefined();
|
||||||
|
|
||||||
|
await updateNavModel('new-folder');
|
||||||
|
|
||||||
|
expect(reduxStore.getState().navIndex[`folder-dashboards-new-folder`]).not.toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { updateNavIndex } from 'app/core/actions';
|
||||||
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
|
import { buildNavModel } from 'app/features/folders/state/navModel';
|
||||||
|
import { store } from 'app/store/store';
|
||||||
|
|
||||||
|
export async function updateNavModel(folderUid: string) {
|
||||||
|
try {
|
||||||
|
const folder = await backendSrv.getFolderByUid(folderUid);
|
||||||
|
store.dispatch(updateNavIndex(buildNavModel(folder)));
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('Error fetching parent folder', folderUid, 'for dashboard', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { behaviors, SceneGridLayout, SceneTimeRange } from '@grafana/scenes';
|
import { behaviors, SceneGridLayout, SceneTimeRange } from '@grafana/scenes';
|
||||||
import { DashboardCursorSync } from '@grafana/schema';
|
import { DashboardCursorSync } from '@grafana/schema';
|
||||||
|
|
||||||
|
import * as utils from '../pages/utils';
|
||||||
import { DashboardControls } from '../scene/DashboardControls';
|
import { DashboardControls } from '../scene/DashboardControls';
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
import { activateFullSceneTree } from '../utils/test-utils';
|
import { activateFullSceneTree } from '../utils/test-utils';
|
||||||
@@ -89,11 +90,14 @@ describe('GeneralSettingsEditView', () => {
|
|||||||
expect(settings.getRefreshPicker()?.state?.intervals).toEqual(['5s']);
|
expect(settings.getRefreshPicker()?.state?.intervals).toEqual(['5s']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('A change to folder updates the dashboard state', () => {
|
it('A change to folder updates the dashboard state', async () => {
|
||||||
settings.onFolderChange('folder-2', 'folder 2');
|
const updateNavModel = jest.spyOn(utils, 'updateNavModel').mockImplementation(jest.fn());
|
||||||
|
|
||||||
|
await settings.onFolderChange('folder-2', 'folder 2');
|
||||||
|
|
||||||
expect(dashboard.state.meta.folderUid).toBe('folder-2');
|
expect(dashboard.state.meta.folderUid).toBe('folder-2');
|
||||||
expect(dashboard.state.meta.folderTitle).toBe('folder 2');
|
expect(dashboard.state.meta.folderTitle).toBe('folder 2');
|
||||||
|
expect(updateNavModel).toHaveBeenCalledWith('folder-2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('A change to tooltip settings updates the dashboard state', () => {
|
it('A change to tooltip settings updates the dashboard state', () => {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { DeleteDashboardButton } from 'app/features/dashboard/components/DeleteD
|
|||||||
import { GenAIDashDescriptionButton } from 'app/features/dashboard/components/GenAI/GenAIDashDescriptionButton';
|
import { GenAIDashDescriptionButton } from 'app/features/dashboard/components/GenAI/GenAIDashDescriptionButton';
|
||||||
import { GenAIDashTitleButton } from 'app/features/dashboard/components/GenAI/GenAIDashTitleButton';
|
import { GenAIDashTitleButton } from 'app/features/dashboard/components/GenAI/GenAIDashTitleButton';
|
||||||
|
|
||||||
|
import { updateNavModel } from '../pages/utils';
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
import { NavToolbarActions } from '../scene/NavToolbarActions';
|
import { NavToolbarActions } from '../scene/NavToolbarActions';
|
||||||
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
||||||
@@ -96,13 +97,17 @@ export class GeneralSettingsEditView
|
|||||||
this._dashboard.setState({ tags: value });
|
this._dashboard.setState({ tags: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
public onFolderChange = (newUID: string | undefined, newTitle: string | undefined) => {
|
public onFolderChange = async (newUID: string | undefined, newTitle: string | undefined) => {
|
||||||
const newMeta = {
|
const newMeta = {
|
||||||
...this._dashboard.state.meta,
|
...this._dashboard.state.meta,
|
||||||
folderUid: newUID || this._dashboard.state.meta.folderUid,
|
folderUid: newUID || this._dashboard.state.meta.folderUid,
|
||||||
folderTitle: newTitle || this._dashboard.state.meta.folderTitle,
|
folderTitle: newTitle || this._dashboard.state.meta.folderTitle,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (newMeta.folderUid) {
|
||||||
|
await updateNavModel(newMeta.folderUid);
|
||||||
|
}
|
||||||
|
|
||||||
this._dashboard.setState({ meta: newMeta });
|
this._dashboard.setState({ meta: newMeta });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user