diff --git a/.betterer.results b/.betterer.results
index 9b47eddaeb4..b09b00781de 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -3227,12 +3227,11 @@ exports[`better eslint`] = {
],
"public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
- [0, 0, 0, "Do not use any type assertions.", "1"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "2"],
- [0, 0, 0, "Do not use any type assertions.", "3"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "4"],
- [0, 0, 0, "Do not use any type assertions.", "5"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "6"]
+ [0, 0, 0, "Unexpected any. Specify a different type.", "1"],
+ [0, 0, 0, "Do not use any type assertions.", "2"],
+ [0, 0, 0, "Unexpected any. Specify a different type.", "3"],
+ [0, 0, 0, "Do not use any type assertions.", "4"],
+ [0, 0, 0, "Unexpected any. Specify a different type.", "5"]
],
"public/app/features/dashboard/components/DashboardRow/DashboardRow.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
@@ -3266,8 +3265,7 @@ exports[`better eslint`] = {
"public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
- [0, 0, 0, "Do not use any type assertions.", "2"],
- [0, 0, 0, "Do not use any type assertions.", "3"]
+ [0, 0, 0, "Do not use any type assertions.", "2"]
],
"public/app/features/dashboard/components/PanelEditor/getFieldOverrideElements.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -4622,8 +4620,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/search/page/components/MoveToFolderModal.tsx:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
- [0, 0, 0, "Do not use any type assertions.", "1"]
+ [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/search/page/components/SearchResultsCards.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
diff --git a/public/app/core/components/Select/FolderPicker.test.tsx b/public/app/core/components/Select/FolderPicker.test.tsx
index 6fa19a5a935..3c6b18fc135 100644
--- a/public/app/core/components/Select/FolderPicker.test.tsx
+++ b/public/app/core/components/Select/FolderPicker.test.tsx
@@ -16,8 +16,8 @@ describe('FolderPicker', () => {
jest
.spyOn(api, 'searchFolders')
.mockResolvedValue([
- { title: 'Dash 1', id: 1 } as DashboardSearchHit,
- { title: 'Dash 2', id: 2 } as DashboardSearchHit,
+ { title: 'Dash 1', uid: 'xMsQdBfWz' } as DashboardSearchHit,
+ { title: 'Dash 2', uid: 'wfTJJL5Wz' } as DashboardSearchHit,
]);
render();
@@ -28,12 +28,12 @@ describe('FolderPicker', () => {
jest
.spyOn(api, 'searchFolders')
.mockResolvedValue([
- { title: 'Dash 1', id: 1 } as DashboardSearchHit,
- { title: 'Dash 2', id: 2 } as DashboardSearchHit,
- { title: 'Dash 3', id: 3 } as DashboardSearchHit,
+ { title: 'Dash 1', uid: 'xMsQdBfWz' } as DashboardSearchHit,
+ { title: 'Dash 2', uid: 'wfTJJL5Wz' } as DashboardSearchHit,
+ { title: 'Dash 3', uid: '7MeksYbmk' } as DashboardSearchHit,
]);
- render( hits.filter((h) => h.id !== 2)} />);
+ render( hits.filter((h) => h.uid !== 'wfTJJL5Wz')} />);
const pickerContainer = screen.getByLabelText(selectors.components.FolderPicker.input);
selectEvent.openMenu(pickerContainer);
@@ -46,13 +46,13 @@ describe('FolderPicker', () => {
});
it('should allow creating a new option', async () => {
- const newFolder = { title: 'New Folder', id: 3 } as DashboardSearchHit;
+ const newFolder = { title: 'New Folder', uid: '7MeksYbmk' } as DashboardSearchHit;
jest
.spyOn(api, 'searchFolders')
.mockResolvedValue([
- { title: 'Dash 1', id: 1 } as DashboardSearchHit,
- { title: 'Dash 2', id: 2 } as DashboardSearchHit,
+ { title: 'Dash 1', uid: 'xMsQdBfWz' } as DashboardSearchHit,
+ { title: 'Dash 2', uid: 'wfTJJL5Wz' } as DashboardSearchHit,
]);
const onChangeFn = jest.fn();
@@ -70,7 +70,7 @@ describe('FolderPicker', () => {
expect(create).toHaveBeenCalledWith({ title: newFolder.title });
});
- expect(onChangeFn).toHaveBeenCalledWith({ title: newFolder.title, id: newFolder.id });
+ expect(onChangeFn).toHaveBeenCalledWith({ title: newFolder.title, uid: newFolder.uid });
await waitFor(() => {
expect(screen.getByText(newFolder.title)).toBeInTheDocument();
});
@@ -80,8 +80,8 @@ describe('FolderPicker', () => {
jest
.spyOn(api, 'searchFolders')
.mockResolvedValue([
- { title: 'Dash 1', id: 1 } as DashboardSearchHit,
- { title: 'Dash 2', id: 2 } as DashboardSearchHit,
+ { title: 'Dash 1', uid: 'xMsQdBfWz' } as DashboardSearchHit,
+ { title: 'Dash 2', uid: 'wfTJJL5Wz' } as DashboardSearchHit,
]);
jest.spyOn(contextSrv, 'hasAccess').mockReturnValue(true);
@@ -101,8 +101,8 @@ describe('FolderPicker', () => {
jest
.spyOn(api, 'searchFolders')
.mockResolvedValue([
- { title: 'Dash 1', id: 1 } as DashboardSearchHit,
- { title: 'Dash 2', id: 2 } as DashboardSearchHit,
+ { title: 'Dash 1', uid: 'xMsQdBfWz' } as DashboardSearchHit,
+ { title: 'Dash 2', uid: 'wfTJJL5Wz' } as DashboardSearchHit,
]);
jest.spyOn(contextSrv, 'hasAccess').mockReturnValue(true);
@@ -122,8 +122,8 @@ describe('FolderPicker', () => {
jest
.spyOn(api, 'searchFolders')
.mockResolvedValue([
- { title: 'Dash 1', id: 1 } as DashboardSearchHit,
- { title: 'Dash 2', id: 2 } as DashboardSearchHit,
+ { title: 'Dash 1', uid: 'xMsQdBfWz' } as DashboardSearchHit,
+ { title: 'Dash 2', uid: 'wfTJJL5Wz' } as DashboardSearchHit,
]);
jest.spyOn(contextSrv, 'hasAccess').mockReturnValue(false);
@@ -141,28 +141,28 @@ describe('FolderPicker', () => {
});
describe('getInitialValues', () => {
- describe('when called with folderId and title', () => {
- it('then it should return folderId and title', async () => {
+ describe('when called with folderUid and title', () => {
+ it('then it should return folderUid and title', async () => {
const getFolder = jest.fn().mockResolvedValue({});
- const folder = await getInitialValues({ folderId: 0, folderName: 'Some title', getFolder });
+ const folder = await getInitialValues({ folderUid: '', folderName: 'Some title', getFolder });
- expect(folder).toEqual({ label: 'Some title', value: 0 });
+ expect(folder).toEqual({ label: 'Some title', value: '' });
expect(getFolder).not.toHaveBeenCalled();
});
});
- describe('when called with just a folderId', () => {
+ describe('when called with just a folderUid', () => {
it('then it should call api to retrieve title', async () => {
- const getFolder = jest.fn().mockResolvedValue({ id: 0, title: 'Title from api' });
- const folder = await getInitialValues({ folderId: 0, getFolder });
+ const getFolder = jest.fn().mockResolvedValue({ uid: '', title: 'Title from api' });
+ const folder = await getInitialValues({ folderUid: '', getFolder });
- expect(folder).toEqual({ label: 'Title from api', value: 0 });
+ expect(folder).toEqual({ label: 'Title from api', value: '' });
expect(getFolder).toHaveBeenCalledTimes(1);
- expect(getFolder).toHaveBeenCalledWith(0);
+ expect(getFolder).toHaveBeenCalledWith('');
});
});
- describe('when called without folderId', () => {
+ describe('when called without folderUid', () => {
it('then it should throw an error', async () => {
const getFolder = jest.fn().mockResolvedValue({});
await expect(getInitialValues({ getFolder })).rejects.toThrow();
diff --git a/public/app/core/components/Select/FolderPicker.tsx b/public/app/core/components/Select/FolderPicker.tsx
index 9e7a8976314..8f2d2764b62 100644
--- a/public/app/core/components/Select/FolderPicker.tsx
+++ b/public/app/core/components/Select/FolderPicker.tsx
@@ -9,7 +9,7 @@ import { useStyles2, ActionMeta, AsyncSelect, Input, InputActionMeta } from '@gr
import appEvents from 'app/core/app_events';
import { t } from 'app/core/internationalization';
import { contextSrv } from 'app/core/services/context_srv';
-import { createFolder, getFolderById, searchFolders } from 'app/features/manage-dashboards/state/actions';
+import { createFolder, getFolderByUid, searchFolders } from 'app/features/manage-dashboards/state/actions';
import { DashboardSearchHit } from 'app/features/search/types';
import { AccessControlAction, PermissionLevelString } from 'app/types';
@@ -28,13 +28,13 @@ export interface CustomAdd {
}
export interface Props {
- onChange: ($folder: { title: string; id: number }) => void;
+ onChange: ($folder: { title: string; uid: string }) => void;
enableCreateNew?: boolean;
rootName?: string;
enableReset?: boolean;
dashboardId?: number | string;
initialTitle?: string;
- initialFolderId?: number;
+ initialFolderUid?: string;
permissionLevel?: Exclude;
filter?: FolderPickerFilter;
allowEmpty?: boolean;
@@ -47,15 +47,15 @@ export interface Props {
/**
* Skips loading all folders in order to find the folder matching
* the folder where the dashboard is stored.
- * Instead initialFolderId and initialTitle will be used to display the correct folder.
- * initialFolderId needs to have an value > -1 or an error will be thrown.
+ * Instead initialFolderUid and initialTitle will be used to display the correct folder.
+ * initialFolderUid needs to be a string or an error will be thrown.
*/
skipInitialLoad?: boolean;
/** The id of the search input. Use this to set a matching label with htmlFor */
inputId?: string;
}
-export type SelectedFolder = SelectableValue;
-const VALUE_FOR_ADD = -10;
+export type SelectedFolder = SelectableValue;
+const VALUE_FOR_ADD = '-10';
export function FolderPicker(props: Props) {
const {
@@ -67,7 +67,7 @@ export function FolderPicker(props: Props) {
inputId,
onClear,
enableReset,
- initialFolderId,
+ initialFolderUid,
initialTitle = '',
permissionLevel = PermissionLevelString.Edit,
rootName = 'General',
@@ -90,14 +90,14 @@ export function FolderPicker(props: Props) {
const getOptions = useCallback(
async (query: string) => {
const searchHits = await searchFolders(query, permissionLevel, accessControlMetadata);
- const options: Array> = mapSearchHitsToOptions(searchHits, filter);
+ const options: Array> = mapSearchHitsToOptions(searchHits, filter);
const hasAccess =
contextSrv.hasAccess(AccessControlAction.DashboardsWrite, contextSrv.isEditor) ||
contextSrv.hasAccess(AccessControlAction.DashboardsCreate, contextSrv.isEditor);
if (hasAccess && rootName?.toLowerCase().startsWith(query.toLowerCase()) && showRoot) {
- options.unshift({ label: rootName, value: 0 });
+ options.unshift({ label: rootName, value: '' });
}
if (
@@ -106,7 +106,7 @@ export function FolderPicker(props: Props) {
initialTitle !== '' &&
!options.find((option) => option.label === initialTitle)
) {
- options.unshift({ label: initialTitle, value: initialFolderId });
+ options.unshift({ label: initialTitle, value: initialFolderUid });
}
if (enableCreateNew && Boolean(customAdd)) {
return [...options, { value: VALUE_FOR_ADD, label: ADD_NEW_FOLER_OPTION, title: query }];
@@ -116,7 +116,7 @@ export function FolderPicker(props: Props) {
},
[
enableReset,
- initialFolderId,
+ initialFolderUid,
initialTitle,
permissionLevel,
rootName,
@@ -133,19 +133,19 @@ export function FolderPicker(props: Props) {
}, [getOptions]);
const loadInitialValue = async () => {
- const resetFolder: SelectableValue = { label: initialTitle, value: undefined };
- const rootFolder: SelectableValue = { label: rootName, value: 0 };
+ const resetFolder: SelectableValue = { label: initialTitle, value: undefined };
+ const rootFolder: SelectableValue = { label: rootName, value: '' };
const options = await getOptions('');
- let folder: SelectableValue | null = null;
+ let folder: SelectableValue | null = null;
- if (initialFolderId !== undefined && initialFolderId !== null && initialFolderId > -1) {
- folder = options.find((option) => option.value === initialFolderId) || null;
+ if (initialFolderUid !== undefined && initialFolderUid !== null) {
+ folder = options.find((option) => option.value === initialFolderUid) || null;
} else if (enableReset && initialTitle) {
folder = resetFolder;
- } else if (initialFolderId) {
- folder = options.find((option) => option.id === initialFolderId) || null;
+ } else if (initialFolderUid) {
+ folder = options.find((option) => option.id === initialFolderUid) || null;
}
if (!folder && !allowEmpty) {
@@ -166,25 +166,25 @@ export function FolderPicker(props: Props) {
useEffect(() => {
// if this is not the same as our initial value notify parent
- if (folder && folder.value !== initialFolderId) {
- !isCreatingNew && folder.value && folder.label && onChange({ id: folder.value, title: folder.label });
+ if (folder && folder.value !== initialFolderUid) {
+ !isCreatingNew && folder.value && folder.label && onChange({ uid: folder.value, title: folder.label });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [folder, initialFolderId]);
+ }, [folder, initialFolderUid]);
// initial values for dropdown
useAsync(async () => {
if (skipInitialLoad) {
const folder = await getInitialValues({
- getFolder: getFolderById,
- folderId: initialFolderId,
+ getFolder: getFolderByUid,
+ folderUid: initialFolderUid,
folderName: initialTitle,
});
setFolder(folder);
}
await loadInitialValue();
- }, [skipInitialLoad, initialFolderId, initialTitle]);
+ }, [skipInitialLoad, initialFolderUid, initialTitle]);
useEffect(() => {
if (folder && folder.id === VALUE_FOR_ADD) {
@@ -193,7 +193,7 @@ export function FolderPicker(props: Props) {
}, [folder]);
const onFolderChange = useCallback(
- (newFolder: SelectableValue | null | undefined, actionMeta: ActionMeta) => {
+ (newFolder: SelectableValue | null | undefined, actionMeta: ActionMeta) => {
if (newFolder?.value === VALUE_FOR_ADD) {
setFolder({
id: VALUE_FOR_ADD,
@@ -202,7 +202,7 @@ export function FolderPicker(props: Props) {
setNewFolderValue(inputValue);
} else {
if (!newFolder) {
- newFolder = { value: 0, label: rootName };
+ newFolder = { value: '', label: rootName };
}
if (actionMeta.action === 'clear' && onClear) {
@@ -211,7 +211,7 @@ export function FolderPicker(props: Props) {
}
setFolder(newFolder);
- onChange({ id: newFolder.value!, title: newFolder.label! });
+ onChange({ uid: newFolder.value!, title: newFolder.label! });
}
},
[onChange, onClear, rootName, inputValue]
@@ -223,11 +223,11 @@ export function FolderPicker(props: Props) {
return false;
}
const newFolder = await createFolder({ title: folderName });
- let folder: SelectableValue = { value: -1, label: 'Not created' };
+ let folder: SelectableValue = { value: '', label: 'Not created' };
- if (newFolder.id > -1) {
+ if (newFolder.uid) {
appEvents.emit(AppEvents.alertSuccess, ['Folder Created', 'OK']);
- folder = { value: newFolder.id, label: newFolder.title };
+ folder = { value: newFolder.uid, label: newFolder.title };
setFolder(newFolder);
onFolderChange(folder, { action: 'create-option', option: folder });
@@ -255,7 +255,7 @@ export function FolderPicker(props: Props) {
break;
}
case 'Escape': {
- setFolder({ value: 0, label: rootName });
+ setFolder({ value: '', label: rootName });
setIsCreatingNew(false);
}
}
@@ -266,11 +266,11 @@ export function FolderPicker(props: Props) {
const onNewFolderChange = (e: FormEvent) => {
const value = e.currentTarget.value;
setNewFolderValue(value);
- setFolder({ id: -1, title: value });
+ setFolder({ id: undefined, title: value });
};
const onBlur = () => {
- setFolder({ value: 0, label: rootName });
+ setFolder({ value: '', label: rootName });
setIsCreatingNew(false);
};
@@ -344,25 +344,25 @@ export function FolderPicker(props: Props) {
function mapSearchHitsToOptions(hits: DashboardSearchHit[], filter?: FolderPickerFilter) {
const filteredHits = filter ? filter(hits) : hits;
- return filteredHits.map((hit) => ({ label: hit.title, value: hit.id }));
+ return filteredHits.map((hit) => ({ label: hit.title, value: hit.uid }));
}
interface Args {
- getFolder: typeof getFolderById;
- folderId?: number;
+ getFolder: typeof getFolderByUid;
+ folderUid?: string;
folderName?: string;
}
-export async function getInitialValues({ folderName, folderId, getFolder }: Args): Promise> {
- if (folderId === null || folderId === undefined || folderId < 0) {
- throw new Error('folderId should to be greater or equal to zero.');
+export async function getInitialValues({ folderName, folderUid, getFolder }: Args): Promise> {
+ if (folderUid === null || folderUid === undefined) {
+ throw new Error('folderUid is not found.');
}
if (folderName) {
- return { label: folderName, value: folderId };
+ return { label: folderName, value: folderUid };
}
- const folderDto = await getFolder(folderId);
- return { label: folderDto.title, value: folderId };
+ const folderDto = await getFolder(folderUid);
+ return { label: folderDto.title, value: folderUid };
}
const getStyles = (theme: GrafanaTheme2) => ({
diff --git a/public/app/features/alerting/unified/components/rule-editor/RuleFolderPicker.tsx b/public/app/features/alerting/unified/components/rule-editor/RuleFolderPicker.tsx
index db6f02bd74c..f1e973a5d81 100644
--- a/public/app/features/alerting/unified/components/rule-editor/RuleFolderPicker.tsx
+++ b/public/app/features/alerting/unified/components/rule-editor/RuleFolderPicker.tsx
@@ -11,7 +11,7 @@ import { FolderWarning, CustomAdd } from '../../../../../core/components/Select/
export interface Folder {
title: string;
- id: number;
+ uid: string;
}
export interface RuleFolderPickerProps extends Omit {
@@ -53,7 +53,7 @@ export function RuleFolderPicker(props: RuleFolderPickerProps) {
showRoot={false}
allowEmpty={true}
initialTitle={value?.title}
- initialFolderId={value?.id}
+ initialFolderUid={value?.uid}
accessControlMetadata
{...props}
permissionLevel={PermissionLevelString.View}
diff --git a/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx b/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx
index ab14b47bc27..0ae88841c4e 100644
--- a/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx
+++ b/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx
@@ -76,7 +76,7 @@ export const DashboardPrompt = React.memo(({ dashboard }: Props) => {
showModal(SaveLibraryPanelModal, {
isUnsavedPrompt: true,
panel: dashboard.panelInEdit as PanelModelWithLibraryPanel,
- folderId: dashboard.meta.folderId as number,
+ folderUid: dashboard.meta.folderUid ?? '',
onConfirm: () => {
hideModal();
moveToBlockedLocationAfterReactStateUpdate(location);
diff --git a/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.tsx b/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.tsx
index 789b60b2ffc..c84d5c8a6f4 100644
--- a/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.tsx
+++ b/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.tsx
@@ -30,8 +30,8 @@ export function GeneralSettingsUnconnected({
}: Props): JSX.Element {
const [renderCounter, setRenderCounter] = useState(0);
- const onFolderChange = (folder: { id: number; title: string }) => {
- dashboard.meta.folderId = folder.id;
+ const onFolderChange = (folder: { uid: string; title: string }) => {
+ dashboard.meta.folderUid = folder.uid;
dashboard.meta.folderTitle = folder.title;
dashboard.meta.hasUnsavedFolderChange = true;
};
@@ -109,7 +109,7 @@ export function GeneralSettingsUnconnected({
{
{this.state.showSaveLibraryPanelModal && (
= ({
const defaultValues: SaveDashboardAsFormDTO = {
title: isNew ? dashboard.title : `${dashboard.title} Copy`,
$folder: {
- id: dashboard.meta.folderId,
+ uid: dashboard.meta.folderUid,
title: dashboard.meta.folderTitle,
},
copyTags: false,
@@ -60,7 +60,7 @@ export const SaveDashboardAsForm: React.FC = ({
return 'Dashboard name cannot be the same as folder name';
}
try {
- await validationSrv.validateNewDashboardName(getFormValues().$folder.id, dashboardName);
+ await validationSrv.validateNewDashboardName(getFormValues().$folder.uid, dashboardName);
return true;
} catch (e) {
return e instanceof Error ? e.message : 'Dashboard name is invalid';
@@ -84,7 +84,7 @@ export const SaveDashboardAsForm: React.FC = ({
const result = await onSubmit(
clone,
{
- folderId: data.$folder.id,
+ folderUid: data.$folder.uid,
},
dashboard
);
@@ -111,7 +111,7 @@ export const SaveDashboardAsForm: React.FC = ({
diff --git a/public/app/features/dashboard/components/SaveDashboard/types.ts b/public/app/features/dashboard/components/SaveDashboard/types.ts
index 0a5646aa095..084ff68b517 100644
--- a/public/app/features/dashboard/components/SaveDashboard/types.ts
+++ b/public/app/features/dashboard/components/SaveDashboard/types.ts
@@ -11,7 +11,7 @@ export interface SaveDashboardData {
}
export interface SaveDashboardOptions extends CloneOptions {
- folderId?: number;
+ folderUid?: string;
overwrite?: boolean;
message?: string;
makeEditable?: boolean;
@@ -20,7 +20,7 @@ export interface SaveDashboardOptions extends CloneOptions {
export interface SaveDashboardCommand {
dashboard: DashboardDataDTO;
message?: string;
- folderId?: number;
+ folderUid?: string;
overwrite?: boolean;
}
diff --git a/public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx b/public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx
index 22de9223b8e..42306bcd8c8 100644
--- a/public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx
+++ b/public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx
@@ -15,12 +15,12 @@ import { DashboardSavedEvent } from 'app/types/events';
import { SaveDashboardOptions } from './types';
const saveDashboard = async (saveModel: any, options: SaveDashboardOptions, dashboard: DashboardModel) => {
- let folderId = options.folderId;
- if (folderId === undefined) {
- folderId = dashboard.meta.folderId ?? saveModel.folderId;
+ let folderUid = options.folderUid;
+ if (folderUid === undefined) {
+ folderUid = dashboard.meta.folderUid ?? saveModel.folderUid;
}
- const result = await saveDashboardApiCall({ ...options, folderId, dashboard: saveModel });
+ const result = await saveDashboardApiCall({ ...options, folderUid, dashboard: saveModel });
// fetch updated access control permissions
await contextSrv.fetchUserPermissions();
return result;
diff --git a/public/app/features/dashboard/components/ShareModal/ShareLibraryPanel.tsx b/public/app/features/dashboard/components/ShareModal/ShareLibraryPanel.tsx
index 28f98cec05a..e3b65aab155 100644
--- a/public/app/features/dashboard/components/ShareModal/ShareLibraryPanel.tsx
+++ b/public/app/features/dashboard/components/ShareModal/ShareLibraryPanel.tsx
@@ -7,10 +7,10 @@ import { AddLibraryPanelContents } from 'app/features/library-panels/components/
import { ShareModalTabProps } from './types';
interface Props extends ShareModalTabProps {
- initialFolderId?: number;
+ initialFolderUid?: string;
}
-export const ShareLibraryPanel = ({ panel, initialFolderId, onDismiss }: Props) => {
+export const ShareLibraryPanel = ({ panel, initialFolderUid, onDismiss }: Props) => {
useEffect(() => {
reportInteraction('grafana_dashboards_library_panel_share_viewed');
}, []);
@@ -24,7 +24,7 @@ export const ShareLibraryPanel = ({ panel, initialFolderId, onDismiss }: Props)
Create library panel.
-
+
>
);
};
diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx
index 40c6b6100d8..1783d7d34bd 100644
--- a/public/app/features/dashboard/containers/DashboardPage.tsx
+++ b/public/app/features/dashboard/containers/DashboardPage.tsx
@@ -45,7 +45,7 @@ export interface DashboardPageRouteParams {
export type DashboardPageRouteSearchParams = {
tab?: string;
- folderId?: string;
+ folderUid?: string;
editPanel?: string;
viewPanel?: string;
editview?: string;
@@ -139,7 +139,7 @@ export class UnthemedDashboardPage extends PureComponent {
urlSlug: match.params.slug,
urlUid: match.params.uid,
urlType: match.params.type,
- urlFolderId: queryParams.folderId,
+ urlFolderUid: queryParams.folderUid,
panelType: queryParams.panelType,
routeName: this.props.route.routeName,
fixUrl: !isPublic,
diff --git a/public/app/features/dashboard/services/DashboardSrv.ts b/public/app/features/dashboard/services/DashboardSrv.ts
index 0b4eb8d0f07..11e32a26eeb 100644
--- a/public/app/features/dashboard/services/DashboardSrv.ts
+++ b/public/app/features/dashboard/services/DashboardSrv.ts
@@ -69,7 +69,7 @@ export class DashboardSrv {
const parsedJson = JSON.parse(json);
return saveDashboard({
dashboard: parsedJson,
- folderId: this.dashboard?.meta.folderId || parsedJson.folderId,
+ folderUid: this.dashboard?.meta.folderUid || parsedJson.folderUid,
});
}
diff --git a/public/app/features/dashboard/state/initDashboard.ts b/public/app/features/dashboard/state/initDashboard.ts
index dc6d007d023..705dd42141c 100644
--- a/public/app/features/dashboard/state/initDashboard.ts
+++ b/public/app/features/dashboard/state/initDashboard.ts
@@ -12,7 +12,15 @@ import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
import { toStateKey } from 'app/features/variables/utils';
-import { DashboardDTO, DashboardInitPhase, DashboardRoutes, StoreState, ThunkDispatch, ThunkResult } from 'app/types';
+import {
+ DashboardDTO,
+ DashboardInitPhase,
+ DashboardMeta,
+ DashboardRoutes,
+ StoreState,
+ ThunkDispatch,
+ ThunkResult,
+} from 'app/types';
import { createDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner';
import { initVariablesTransaction } from '../../variables/state/actions';
@@ -27,7 +35,7 @@ export interface InitDashboardArgs {
urlUid?: string;
urlSlug?: string;
urlType?: string;
- urlFolderId?: string;
+ urlFolderUid?: string;
panelType?: string;
accessToken?: string;
routeName?: string;
@@ -89,7 +97,7 @@ async function fetchDashboard(
return dashDTO;
}
case DashboardRoutes.New: {
- return getNewDashboardModelData(args.urlFolderId, args.panelType);
+ return getNewDashboardModelData(args.urlFolderUid, args.panelType);
}
case DashboardRoutes.Path: {
const path = args.urlSlug ?? '';
@@ -255,14 +263,17 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult {
};
}
-export function getNewDashboardModelData(urlFolderId?: string, panelType?: string): any {
+export function getNewDashboardModelData(
+ urlFolderUid?: string,
+ panelType?: string
+): { dashboard: any; meta: DashboardMeta } {
const data = {
meta: {
canStar: false,
canShare: false,
canDelete: false,
isNew: true,
- folderId: 0,
+ folderUid: '',
},
dashboard: {
title: 'New dashboard',
@@ -276,8 +287,8 @@ export function getNewDashboardModelData(urlFolderId?: string, panelType?: strin
},
};
- if (urlFolderId) {
- data.meta.folderId = parseInt(urlFolderId, 10);
+ if (urlFolderUid) {
+ data.meta.folderUid = urlFolderUid;
}
return data;
diff --git a/public/app/features/dashboard/utils/panel.ts b/public/app/features/dashboard/utils/panel.ts
index 6950dadc9d4..1a870ee6b03 100644
--- a/public/app/features/dashboard/utils/panel.ts
+++ b/public/app/features/dashboard/utils/panel.ts
@@ -75,7 +75,7 @@ export const addLibraryPanel = (dashboard: DashboardModel, panel: PanelModel) =>
component: AddLibraryPanelModal,
props: {
panel,
- initialFolderId: dashboard.meta.folderId,
+ initialFolderUid: dashboard.meta.folderUid,
isOpen: true,
},
})
diff --git a/public/app/features/library-panels/components/AddLibraryPanelModal/AddLibraryPanelModal.tsx b/public/app/features/library-panels/components/AddLibraryPanelModal/AddLibraryPanelModal.tsx
index 36d64f498da..9f2f5253f2b 100644
--- a/public/app/features/library-panels/components/AddLibraryPanelModal/AddLibraryPanelModal.tsx
+++ b/public/app/features/library-panels/components/AddLibraryPanelModal/AddLibraryPanelModal.tsx
@@ -13,11 +13,11 @@ import { usePanelSave } from '../../utils/usePanelSave';
interface AddLibraryPanelContentsProps {
onDismiss: () => void;
panel: PanelModel;
- initialFolderId?: number;
+ initialFolderUid?: string;
}
-export const AddLibraryPanelContents = ({ panel, initialFolderId, onDismiss }: AddLibraryPanelContentsProps) => {
- const [folderId, setFolderId] = useState(initialFolderId);
+export const AddLibraryPanelContents = ({ panel, initialFolderUid, onDismiss }: AddLibraryPanelContentsProps) => {
+ const [folderUid, setFolderUid] = useState(initialFolderUid);
const [panelName, setPanelName] = useState(panel.title);
const [debouncedPanelName, setDebouncedPanelName] = useState(panel.title);
const [waiting, setWaiting] = useState(false);
@@ -28,15 +28,15 @@ export const AddLibraryPanelContents = ({ panel, initialFolderId, onDismiss }: A
const { saveLibraryPanel } = usePanelSave();
const onCreate = useCallback(() => {
panel.libraryPanel = { uid: '', name: panelName };
- saveLibraryPanel(panel, folderId!).then((res) => {
+ saveLibraryPanel(panel, folderUid!).then((res) => {
if (!(res instanceof Error)) {
onDismiss();
}
});
- }, [panel, panelName, folderId, onDismiss, saveLibraryPanel]);
+ }, [panel, panelName, folderUid, onDismiss, saveLibraryPanel]);
const isValidName = useAsync(async () => {
try {
- return !(await getLibraryPanelByName(panelName)).some((lp) => lp.folderId === folderId);
+ return !(await getLibraryPanelByName(panelName)).some((lp) => lp.folderUid === folderUid);
} catch (err) {
if (isFetchError(err)) {
err.isHandled = true;
@@ -45,7 +45,7 @@ export const AddLibraryPanelContents = ({ panel, initialFolderId, onDismiss }: A
} finally {
setWaiting(false);
}
- }, [debouncedPanelName, folderId]);
+ }, [debouncedPanelName, folderUid]);
const invalidInput =
!isValidName?.value && isValidName.value !== undefined && panelName === debouncedPanelName && !waiting;
@@ -72,8 +72,8 @@ export const AddLibraryPanelContents = ({ panel, initialFolderId, onDismiss }: A
)}
>
setFolderId(id)}
- initialFolderId={initialFolderId}
+ onChange={({ uid }) => setFolderUid(uid)}
+ initialFolderUid={initialFolderUid}
inputId="share-panel-library-panel-folder-picker"
/>
@@ -94,10 +94,10 @@ interface Props extends AddLibraryPanelContentsProps {
isOpen?: boolean;
}
-export const AddLibraryPanelModal = ({ isOpen = false, panel, initialFolderId, ...props }: Props) => {
+export const AddLibraryPanelModal = ({ isOpen = false, panel, initialFolderUid, ...props }: Props) => {
return (
-
+
);
};
diff --git a/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx b/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx
index e3a84f0a57e..726957acb8c 100644
--- a/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx
+++ b/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx
@@ -28,9 +28,9 @@ jest.mock('debounce-promise', () => {
const debounce = (fn: any) => {
const debounced = () =>
Promise.resolve([
- { label: 'General', value: { id: 0, title: 'General' } },
- { label: 'Folder1', value: { id: 1, title: 'Folder1' } },
- { label: 'Folder2', value: { id: 2, title: 'Folder2' } },
+ { label: 'General', value: { uid: '', title: 'General' } },
+ { label: 'Folder1', value: { id: 'xMsQdBfWz', title: 'Folder1' } },
+ { label: 'Folder2', value: { id: 'wfTJJL5Wz', title: 'Folder2' } },
]);
return debounced;
};
@@ -187,7 +187,7 @@ describe('LibraryPanelsSearch', () => {
kind: LibraryElementKind.Panel,
uid: 'uid',
description: 'Library Panel Description',
- folderId: 0,
+ folderUid: '',
model: { type: 'timeseries', title: 'A title' },
type: 'timeseries',
orgId: 1,
@@ -242,7 +242,7 @@ describe('LibraryPanelsSearch', () => {
kind: LibraryElementKind.Panel,
uid: 'uid',
description: 'Library Panel Description',
- folderId: 0,
+ folderUid: '',
model: { type: 'timeseries', title: 'A title' },
type: 'timeseries',
orgId: 1,
@@ -286,7 +286,7 @@ describe('LibraryPanelsSearch', () => {
kind: LibraryElementKind.Panel,
uid: 'uid',
description: 'Library Panel Description',
- folderId: 0,
+ folderUid: '',
model: { type: 'timeseries', title: 'A title' },
type: 'timeseries',
orgId: 1,
diff --git a/public/app/features/library-panels/components/LibraryPanelsView/reducer.test.ts b/public/app/features/library-panels/components/LibraryPanelsView/reducer.test.ts
index be4cbad2922..527d345534b 100644
--- a/public/app/features/library-panels/components/LibraryPanelsView/reducer.test.ts
+++ b/public/app/features/library-panels/components/LibraryPanelsView/reducer.test.ts
@@ -106,7 +106,7 @@ function mockLibraryPanel({
uid = '1',
id = 1,
orgId = 1,
- folderId = 0,
+ folderUid = '',
name = 'Test Panel',
model = { type: 'text', title: 'Test Panel' },
meta = {
@@ -126,7 +126,7 @@ function mockLibraryPanel({
uid,
id,
orgId,
- folderId,
+ folderUid,
name,
kind: LibraryElementKind.Panel,
model,
diff --git a/public/app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup.tsx b/public/app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup.tsx
index 3fb0c327952..94d86bab9a7 100644
--- a/public/app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup.tsx
+++ b/public/app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup.tsx
@@ -70,7 +70,7 @@ export const PanelLibraryOptionsGroup: FC = ({ panel, searchQuery }) => {
setShowingAddPanelModal(false)}
- initialFolderId={dashboard?.meta.folderId}
+ initialFolderUid={dashboard?.meta.folderUid}
isOpen={showingAddPanelModal}
/>
)}
diff --git a/public/app/features/library-panels/components/SaveLibraryPanelModal/SaveLibraryPanelModal.tsx b/public/app/features/library-panels/components/SaveLibraryPanelModal/SaveLibraryPanelModal.tsx
index 442cce84952..e3b551d302a 100644
--- a/public/app/features/library-panels/components/SaveLibraryPanelModal/SaveLibraryPanelModal.tsx
+++ b/public/app/features/library-panels/components/SaveLibraryPanelModal/SaveLibraryPanelModal.tsx
@@ -10,14 +10,21 @@ import { usePanelSave } from '../../utils/usePanelSave';
interface Props {
panel: PanelModelWithLibraryPanel;
- folderId: number;
+ folderUid: string;
isUnsavedPrompt?: boolean;
onConfirm: () => void;
onDismiss: () => void;
onDiscard: () => void;
}
-export const SaveLibraryPanelModal = ({ panel, folderId, isUnsavedPrompt, onDismiss, onConfirm, onDiscard }: Props) => {
+export const SaveLibraryPanelModal = ({
+ panel,
+ folderUid,
+ isUnsavedPrompt,
+ onDismiss,
+ onConfirm,
+ onDiscard,
+}: Props) => {
const [searchString, setSearchString] = useState('');
const dashState = useAsync(async () => {
const searchHits = await getConnectedDashboards(panel.libraryPanel.uid);
@@ -98,7 +105,7 @@ export const SaveLibraryPanelModal = ({ panel, folderId, isUnsavedPrompt, onDism
)}
- setFolder(f as FolderInfo)} />
+ setFolder(f)} />
diff --git a/public/app/features/search/page/components/SearchView.tsx b/public/app/features/search/page/components/SearchView.tsx
index 116f5536525..5a03908f1df 100644
--- a/public/app/features/search/page/components/SearchView.tsx
+++ b/public/app/features/search/page/components/SearchView.tsx
@@ -165,7 +165,7 @@ export const SearchView = ({ showManage, folderDTO, hidePseudoFolders, keyboardE
title="This folder doesn't have any dashboards yet"
buttonIcon="plus"
buttonTitle="Create Dashboard"
- buttonLink={`dashboard/new?folderId=${folderDTO.id}`}
+ buttonLink={`dashboard/new?folderUid=${folderDTO.uid}`}
proTip="Add/move dashboards to your folder at ->"
proTipLink="dashboards"
proTipLinkTitle="Manage dashboards"
diff --git a/public/app/plugins/panel/alertlist/module.tsx b/public/app/plugins/panel/alertlist/module.tsx
index f24992a25a7..e0a40db6c7b 100644
--- a/public/app/plugins/panel/alertlist/module.tsx
+++ b/public/app/plugins/panel/alertlist/module.tsx
@@ -256,7 +256,7 @@ const unifiedAlertList = new PanelPlugin(UnifiedAlertLi
showRoot={false}
allowEmpty={true}
initialTitle={props.value?.title}
- initialFolderId={props.value?.id}
+ initialFolderUid={props.value?.uid}
permissionLevel={PermissionLevelString.View}
onClear={() => props.onChange('')}
{...props}
diff --git a/public/app/types/folders.ts b/public/app/types/folders.ts
index bd0616499bf..9f5bc9628a0 100644
--- a/public/app/types/folders.ts
+++ b/public/app/types/folders.ts
@@ -31,7 +31,7 @@ export interface FolderInfo {
/**
* @deprecated use uid instead.
*/
- id?: number;
+ id?: number; // can't be totally removed as search and alerts api aren't supporting folderUids yet. It will break DashList and AlertList panel
uid?: string;
title?: string;
url?: string;