mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardList/AlertList: Fix for missing All folder value (#39772)
* DashboardList/AlertList: Fix for missing All folder value * Refactor: Fixes case where folder does not exist in results
This commit is contained in:
parent
9de633d3a3
commit
7c7b21b39e
@ -200,6 +200,9 @@ export const Components = {
|
|||||||
FolderPicker: {
|
FolderPicker: {
|
||||||
container: 'Folder picker select container',
|
container: 'Folder picker select container',
|
||||||
},
|
},
|
||||||
|
ReadonlyFolderPicker: {
|
||||||
|
container: 'data-testid Readonly folder picker select container',
|
||||||
|
},
|
||||||
DataSourcePicker: {
|
DataSourcePicker: {
|
||||||
container: 'Data source picker select container',
|
container: 'Data source picker select container',
|
||||||
input: () => 'input[id="data-source-picker"]',
|
input: () => 'input[id="data-source-picker"]',
|
||||||
|
@ -0,0 +1,137 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
import { render, waitFor, within } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { byTestId } from 'testing-library-selector';
|
||||||
|
|
||||||
|
import * as api from './api';
|
||||||
|
import { FolderInfo, PermissionLevelString } from '../../../../types';
|
||||||
|
import { ALL_FOLDER, GENERAL_FOLDER, ReadonlyFolderPicker, ReadonlyFolderPickerProps } from './ReadonlyFolderPicker';
|
||||||
|
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
|
const FOLDERS = [
|
||||||
|
{ value: GENERAL_FOLDER, label: GENERAL_FOLDER.title },
|
||||||
|
{ value: { id: 1, title: 'Test' }, label: 'Test' },
|
||||||
|
];
|
||||||
|
|
||||||
|
async function getTestContext(
|
||||||
|
propOverrides: Partial<ReadonlyFolderPickerProps> = {},
|
||||||
|
folders: Array<SelectableValue<FolderInfo>> = [],
|
||||||
|
folder: SelectableValue<FolderInfo> | undefined = undefined
|
||||||
|
) {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
const selectors = {
|
||||||
|
container: byTestId(e2eSelectors.components.ReadonlyFolderPicker.container),
|
||||||
|
};
|
||||||
|
const getFoldersAsOptionsSpy = jest.spyOn(api, 'getFoldersAsOptions').mockResolvedValue(folders);
|
||||||
|
const getFolderAsOptionSpy = jest.spyOn(api, 'getFolderAsOption').mockResolvedValue(folder);
|
||||||
|
const props: ReadonlyFolderPickerProps = {
|
||||||
|
onChange: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(props, propOverrides);
|
||||||
|
|
||||||
|
render(<ReadonlyFolderPicker {...props} />);
|
||||||
|
await waitFor(() => expect(getFoldersAsOptionsSpy).toHaveBeenCalledTimes(1));
|
||||||
|
|
||||||
|
return { getFoldersAsOptionsSpy, getFolderAsOptionSpy, selectors };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ReadonlyFolderPicker', () => {
|
||||||
|
describe('when there are no folders', () => {
|
||||||
|
it('then the no folder should be selected and Choose should appear', async () => {
|
||||||
|
const { selectors } = await getTestContext();
|
||||||
|
|
||||||
|
expect(within(selectors.container.get()).getByText('Choose')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when permissionLevel is set', () => {
|
||||||
|
it('then permissionLevel is passed correctly to getFoldersAsOptions', async () => {
|
||||||
|
const { getFoldersAsOptionsSpy } = await getTestContext({ permissionLevel: PermissionLevelString.Edit });
|
||||||
|
|
||||||
|
expect(getFoldersAsOptionsSpy).toHaveBeenCalledWith({
|
||||||
|
query: '',
|
||||||
|
permissionLevel: 'Edit',
|
||||||
|
extraFolders: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when extraFolders is set', () => {
|
||||||
|
it('then extraFolders is passed correctly to getFoldersAsOptions', async () => {
|
||||||
|
const { getFoldersAsOptionsSpy } = await getTestContext({ extraFolders: [ALL_FOLDER] });
|
||||||
|
|
||||||
|
expect(getFoldersAsOptionsSpy).toHaveBeenCalledWith({
|
||||||
|
query: '',
|
||||||
|
permissionLevel: 'View',
|
||||||
|
extraFolders: [{ id: undefined, title: 'All' }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when entering a query in the input', () => {
|
||||||
|
it('then query is passed correctly to getFoldersAsOptions', async () => {
|
||||||
|
const { getFoldersAsOptionsSpy, selectors } = await getTestContext();
|
||||||
|
|
||||||
|
expect(within(selectors.container.get()).getByRole('textbox')).toBeInTheDocument();
|
||||||
|
getFoldersAsOptionsSpy.mockClear();
|
||||||
|
await userEvent.type(within(selectors.container.get()).getByRole('textbox'), 'A');
|
||||||
|
await waitFor(() => expect(getFoldersAsOptionsSpy).toHaveBeenCalledTimes(1));
|
||||||
|
|
||||||
|
expect(getFoldersAsOptionsSpy).toHaveBeenCalledWith({
|
||||||
|
query: 'A',
|
||||||
|
permissionLevel: 'View',
|
||||||
|
extraFolders: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when there are folders', () => {
|
||||||
|
it('then the first folder in all folders should be selected', async () => {
|
||||||
|
const { selectors } = await getTestContext({}, FOLDERS);
|
||||||
|
|
||||||
|
expect(within(selectors.container.get()).getByText('General')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and initialFolderId is passed in props and it matches an existing folder', () => {
|
||||||
|
it('then the folder with an id equal to initialFolderId should be selected', async () => {
|
||||||
|
const { selectors } = await getTestContext({ initialFolderId: 1 }, FOLDERS);
|
||||||
|
|
||||||
|
expect(within(selectors.container.get()).getByText('Test')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and initialFolderId is passed in props and it does not match an existing folder from search api', () => {
|
||||||
|
it('then getFolderAsOption should be called and correct folder should be selected', async () => {
|
||||||
|
const folderById = {
|
||||||
|
value: { id: 50000, title: 'Outside api search' },
|
||||||
|
label: 'Outside api search',
|
||||||
|
};
|
||||||
|
const { selectors, getFolderAsOptionSpy } = await getTestContext(
|
||||||
|
{ initialFolderId: 50000 },
|
||||||
|
FOLDERS,
|
||||||
|
folderById
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(within(selectors.container.get()).getByText('Outside api search')).toBeInTheDocument();
|
||||||
|
expect(getFolderAsOptionSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(getFolderAsOptionSpy).toHaveBeenCalledWith(50000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and initialFolderId is passed in props and folder does not exist', () => {
|
||||||
|
it('then getFolderAsOption should be called and the first folder should be selected instead', async () => {
|
||||||
|
const { selectors, getFolderAsOptionSpy } = await getTestContext(
|
||||||
|
{ initialFolderId: 50000 },
|
||||||
|
FOLDERS,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(within(selectors.container.get()).getByText('General')).toBeInTheDocument();
|
||||||
|
expect(getFolderAsOptionSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(getFolderAsOptionSpy).toHaveBeenCalledWith(50000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,85 @@
|
|||||||
|
import React, { ReactElement, useCallback, useState } from 'react';
|
||||||
|
import debouncePromise from 'debounce-promise';
|
||||||
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
import { AsyncSelect } from '@grafana/ui';
|
||||||
|
|
||||||
|
import { FolderInfo, PermissionLevelString } from '../../../../types';
|
||||||
|
import { findOptionWithId, getFolderAsOption, getFoldersAsOptions } from './api';
|
||||||
|
import { PermissionLevel } from './types';
|
||||||
|
import { GENERAL_FOLDER_ID, GENERAL_FOLDER_TITLE } from '../../../../features/search/constants';
|
||||||
|
|
||||||
|
export const ALL_FOLDER: FolderInfo = { id: undefined, title: 'All' };
|
||||||
|
export const GENERAL_FOLDER: FolderInfo = { id: GENERAL_FOLDER_ID, title: GENERAL_FOLDER_TITLE };
|
||||||
|
|
||||||
|
export interface ReadonlyFolderPickerProps {
|
||||||
|
onChange: (folder?: FolderInfo) => void;
|
||||||
|
initialFolderId?: number;
|
||||||
|
/**
|
||||||
|
* By default the folders API doesn't include the General folder because it doesn't exist
|
||||||
|
* Add any extra folders you need to appear in the folder picker with the extraFolders property
|
||||||
|
*/
|
||||||
|
extraFolders?: FolderInfo[];
|
||||||
|
permissionLevel?: PermissionLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReadonlyFolderPicker({
|
||||||
|
onChange: propsOnChange,
|
||||||
|
extraFolders = [],
|
||||||
|
initialFolderId,
|
||||||
|
permissionLevel = PermissionLevelString.View,
|
||||||
|
}: ReadonlyFolderPickerProps): ReactElement {
|
||||||
|
const [initialized, setInitialized] = useState(false);
|
||||||
|
const [option, setOption] = useState<SelectableValue<FolderInfo> | undefined>(undefined);
|
||||||
|
const [options, setOptions] = useState<Array<SelectableValue<FolderInfo>> | undefined>(undefined);
|
||||||
|
const initialize = useCallback(
|
||||||
|
async (options: Array<SelectableValue<FolderInfo>>) => {
|
||||||
|
let option = findOptionWithId(options, initialFolderId);
|
||||||
|
if (!option) {
|
||||||
|
// we didn't find the option with the initialFolderId
|
||||||
|
// might be because the folder doesn't exist any longer
|
||||||
|
// might be because the folder is outside of the search limit of the api
|
||||||
|
option = (await getFolderAsOption(initialFolderId)) ?? options[0]; // get folder by id or select the first item in the options and call propsOnChange
|
||||||
|
propsOnChange(option.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
setInitialized(true);
|
||||||
|
setOptions(options);
|
||||||
|
setOption(option);
|
||||||
|
},
|
||||||
|
[initialFolderId, propsOnChange]
|
||||||
|
);
|
||||||
|
const loadOptions = useCallback(
|
||||||
|
async (query: string) => {
|
||||||
|
const options = await getFoldersAsOptions({ query, permissionLevel, extraFolders });
|
||||||
|
if (!initialized) {
|
||||||
|
await initialize(options);
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
[permissionLevel, extraFolders, initialized, initialize]
|
||||||
|
);
|
||||||
|
const debouncedLoadOptions = debouncePromise(loadOptions, 300, { leading: true });
|
||||||
|
const onChange = useCallback(
|
||||||
|
({ value }: SelectableValue<FolderInfo>) => {
|
||||||
|
const option = findOptionWithId(options, value?.id);
|
||||||
|
setOption(option);
|
||||||
|
propsOnChange(value);
|
||||||
|
},
|
||||||
|
[options, propsOnChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-testid={selectors.components.ReadonlyFolderPicker.container}>
|
||||||
|
<AsyncSelect
|
||||||
|
menuShouldPortal
|
||||||
|
loadingMessage="Loading folders..."
|
||||||
|
defaultOptions
|
||||||
|
defaultValue={option}
|
||||||
|
value={option}
|
||||||
|
loadOptions={debouncedLoadOptions}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
import * as api from '../../../../features/manage-dashboards/state/actions';
|
||||||
|
import { getFolderAsOption, getFoldersAsOptions } from './api';
|
||||||
|
import { DashboardSearchHit } from '../../../../features/search/types';
|
||||||
|
import { PermissionLevelString } from '../../../../types';
|
||||||
|
import { ALL_FOLDER, GENERAL_FOLDER } from './ReadonlyFolderPicker';
|
||||||
|
import { silenceConsoleOutput } from '../../../../../test/core/utils/silenceConsoleOutput';
|
||||||
|
|
||||||
|
function getTestContext(
|
||||||
|
searchHits: DashboardSearchHit[] = [],
|
||||||
|
folderById: { id: number; title: string } = { id: 1, title: 'Folder 1' }
|
||||||
|
) {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
const searchFoldersSpy = jest.spyOn(api, 'searchFolders').mockResolvedValue(searchHits);
|
||||||
|
const getFolderByIdSpy = jest.spyOn(api, 'getFolderById').mockResolvedValue(folderById);
|
||||||
|
|
||||||
|
return { searchFoldersSpy, getFolderByIdSpy };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('getFoldersAsOptions', () => {
|
||||||
|
describe('when called without permissionLevel and query', () => {
|
||||||
|
it('then the correct defaults are passed to the api', async () => {
|
||||||
|
const { searchFoldersSpy } = getTestContext();
|
||||||
|
|
||||||
|
await getFoldersAsOptions({ query: '' });
|
||||||
|
|
||||||
|
expect(searchFoldersSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(searchFoldersSpy).toHaveBeenCalledWith('', 'View');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and extra folders are passed', () => {
|
||||||
|
it('then extra folders should all appear first in the result', async () => {
|
||||||
|
const args = { query: '', extraFolders: [ALL_FOLDER, GENERAL_FOLDER] };
|
||||||
|
const searchHits: any[] = [{ id: 1, title: 'Folder 1' }];
|
||||||
|
getTestContext(searchHits);
|
||||||
|
|
||||||
|
const result = await getFoldersAsOptions(args);
|
||||||
|
expect(result).toEqual([
|
||||||
|
{ value: { id: undefined, title: 'All' }, label: 'All' },
|
||||||
|
{ value: { id: 0, title: 'General' }, label: 'General' },
|
||||||
|
{ value: { id: 1, title: 'Folder 1' }, label: 'Folder 1' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when called with permissionLevel and query', () => {
|
||||||
|
it('then the correct values are passed to the api', async () => {
|
||||||
|
const { searchFoldersSpy } = getTestContext();
|
||||||
|
|
||||||
|
await getFoldersAsOptions({ query: 'Folder1', permissionLevel: PermissionLevelString.Edit });
|
||||||
|
|
||||||
|
expect(searchFoldersSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(searchFoldersSpy).toHaveBeenCalledWith('Folder1', 'Edit');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and extra folders are passed and extra folders contain query', () => {
|
||||||
|
it('then correct extra folders should all appear first in the result', async () => {
|
||||||
|
const args = { query: 'er', extraFolders: [ALL_FOLDER, GENERAL_FOLDER] };
|
||||||
|
const searchHits: any[] = [{ id: 1, title: 'Folder 1' }];
|
||||||
|
getTestContext(searchHits);
|
||||||
|
|
||||||
|
const result = await getFoldersAsOptions(args);
|
||||||
|
expect(result).toEqual([
|
||||||
|
{ value: { id: 0, title: 'General' }, label: 'General' },
|
||||||
|
{ value: { id: 1, title: 'Folder 1' }, label: 'Folder 1' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and extra folders are passed and extra folders do not contain query', () => {
|
||||||
|
it('then no extra folders should appear first in the result', async () => {
|
||||||
|
const args = { query: '1', extraFolders: [ALL_FOLDER, GENERAL_FOLDER] };
|
||||||
|
const searchHits: any[] = [{ id: 1, title: 'Folder 1' }];
|
||||||
|
getTestContext(searchHits);
|
||||||
|
|
||||||
|
const result = await getFoldersAsOptions(args);
|
||||||
|
expect(result).toEqual([{ value: { id: 1, title: 'Folder 1' }, label: 'Folder 1' }]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getFolderAsOption', () => {
|
||||||
|
describe('when called with undefined', () => {
|
||||||
|
it('then it should return undefined', async () => {
|
||||||
|
const { getFolderByIdSpy } = getTestContext();
|
||||||
|
|
||||||
|
const result = await getFolderAsOption(undefined);
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
expect(getFolderByIdSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when called with a folder id that does not exist', () => {
|
||||||
|
silenceConsoleOutput();
|
||||||
|
it('then it should return undefined', async () => {
|
||||||
|
const { getFolderByIdSpy } = getTestContext();
|
||||||
|
getFolderByIdSpy.mockRejectedValue('Not found');
|
||||||
|
|
||||||
|
const result = await getFolderAsOption(-1);
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
expect(getFolderByIdSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when called with a folder id that exist', () => {
|
||||||
|
it('then it should return a SelectableValue of FolderInfo', async () => {
|
||||||
|
const { getFolderByIdSpy } = getTestContext();
|
||||||
|
|
||||||
|
const result = await getFolderAsOption(1);
|
||||||
|
expect(result).toEqual({ value: { id: 1, title: 'Folder 1' }, label: 'Folder 1' });
|
||||||
|
expect(getFolderByIdSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,74 @@
|
|||||||
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
|
||||||
|
import { FolderInfo, PermissionLevelString } from '../../../../types';
|
||||||
|
import { getFolderById, searchFolders } from '../../../../features/manage-dashboards/state/actions';
|
||||||
|
import { PermissionLevel } from './types';
|
||||||
|
|
||||||
|
interface GetFoldersArgs {
|
||||||
|
query: string;
|
||||||
|
permissionLevel?: PermissionLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFolders({ query, permissionLevel }: GetFoldersArgs): Promise<FolderInfo[]> {
|
||||||
|
const searchHits = await searchFolders(query, permissionLevel);
|
||||||
|
const folders: FolderInfo[] = searchHits.map((searchHit) => ({
|
||||||
|
id: searchHit.id,
|
||||||
|
title: searchHit.title,
|
||||||
|
url: searchHit.url,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetFoldersWithEntriesArgs extends GetFoldersArgs {
|
||||||
|
extraFolders?: FolderInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFoldersWithEntries({
|
||||||
|
query,
|
||||||
|
permissionLevel,
|
||||||
|
extraFolders,
|
||||||
|
}: GetFoldersWithEntriesArgs): Promise<FolderInfo[]> {
|
||||||
|
const folders = await getFolders({ query, permissionLevel });
|
||||||
|
const extra: FolderInfo[] = extraFolders ?? [];
|
||||||
|
const filteredExtra = query ? extra.filter((f) => f.title?.toLowerCase().includes(query.toLowerCase())) : extra;
|
||||||
|
if (folders) {
|
||||||
|
return filteredExtra.concat(folders);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFoldersAsOptions({
|
||||||
|
query,
|
||||||
|
permissionLevel = PermissionLevelString.View,
|
||||||
|
extraFolders = [],
|
||||||
|
}: GetFoldersWithEntriesArgs) {
|
||||||
|
const folders = await getFoldersWithEntries({ query, permissionLevel, extraFolders });
|
||||||
|
return folders.map((value) => {
|
||||||
|
const option: SelectableValue<FolderInfo> = { value, label: value.title };
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findOptionWithId(
|
||||||
|
options?: Array<SelectableValue<FolderInfo>>,
|
||||||
|
id?: number
|
||||||
|
): SelectableValue<FolderInfo> | undefined {
|
||||||
|
return options?.find((o) => o.value?.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFolderAsOption(folderId?: number): Promise<SelectableValue<FolderInfo> | undefined> {
|
||||||
|
if (folderId === undefined || folderId === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { id, title } = await getFolderById(folderId);
|
||||||
|
return { value: { id, title }, label: title };
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Could not find folder with id:${folderId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
import { PermissionLevelString } from '../../../../types';
|
||||||
|
|
||||||
|
export type PermissionLevel = Exclude<PermissionLevelString, PermissionLevelString.Admin>;
|
@ -5,3 +5,4 @@ export const SEARCH_ITEM_MARGIN = 8;
|
|||||||
export const DEFAULT_SORT = { label: 'A\u2013Z', value: 'alpha-asc' };
|
export const DEFAULT_SORT = { label: 'A\u2013Z', value: 'alpha-asc' };
|
||||||
export const SECTION_STORAGE_KEY = 'search.sections';
|
export const SECTION_STORAGE_KEY = 'search.sections';
|
||||||
export const GENERAL_FOLDER_ID = 0;
|
export const GENERAL_FOLDER_ID = 0;
|
||||||
|
export const GENERAL_FOLDER_TITLE = 'General';
|
||||||
|
@ -3,11 +3,15 @@ import { PanelPlugin } from '@grafana/data';
|
|||||||
import { TagsInput } from '@grafana/ui';
|
import { TagsInput } from '@grafana/ui';
|
||||||
import { AlertList } from './AlertList';
|
import { AlertList } from './AlertList';
|
||||||
import { UnifiedAlertList } from './UnifiedAlertList';
|
import { UnifiedAlertList } from './UnifiedAlertList';
|
||||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
import { AlertListOptions, ShowOption, SortOrder, UnifiedAlertListOptions } from './types';
|
||||||
import { AlertListOptions, UnifiedAlertListOptions, ShowOption, SortOrder } from './types';
|
|
||||||
import { alertListPanelMigrationHandler } from './AlertListMigrationHandler';
|
import { alertListPanelMigrationHandler } from './AlertListMigrationHandler';
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
import { RuleFolderPicker } from 'app/features/alerting/unified/components/rule-editor/RuleFolderPicker';
|
import { RuleFolderPicker } from 'app/features/alerting/unified/components/rule-editor/RuleFolderPicker';
|
||||||
|
import {
|
||||||
|
ALL_FOLDER,
|
||||||
|
GENERAL_FOLDER,
|
||||||
|
ReadonlyFolderPicker,
|
||||||
|
} from '../../../core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker';
|
||||||
|
|
||||||
function showIfCurrentState(options: AlertListOptions) {
|
function showIfCurrentState(options: AlertListOptions) {
|
||||||
return options.showOptions === ShowOption.Current;
|
return options.showOptions === ShowOption.Current;
|
||||||
@ -74,13 +78,12 @@ const alertList = new PanelPlugin<AlertListOptions>(AlertList)
|
|||||||
name: 'Folder',
|
name: 'Folder',
|
||||||
id: 'folderId',
|
id: 'folderId',
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
editor: function RenderFolderPicker(props) {
|
editor: function RenderFolderPicker({ value, onChange }) {
|
||||||
return (
|
return (
|
||||||
<FolderPicker
|
<ReadonlyFolderPicker
|
||||||
initialFolderId={props.value}
|
initialFolderId={value}
|
||||||
initialTitle="All"
|
onChange={(folder) => onChange(folder?.id)}
|
||||||
enableReset={true}
|
extraFolders={[ALL_FOLDER, GENERAL_FOLDER]}
|
||||||
onChange={({ id }) => props.onChange(id)}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { PanelModel, PanelPlugin } from '@grafana/data';
|
import { PanelModel, PanelPlugin } from '@grafana/data';
|
||||||
import { DashList } from './DashList';
|
import { DashList } from './DashList';
|
||||||
import { DashListOptions } from './types';
|
import { DashListOptions } from './types';
|
||||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { TagsInput } from '@grafana/ui';
|
import { TagsInput } from '@grafana/ui';
|
||||||
import { PermissionLevelString } from '../../../types';
|
import {
|
||||||
|
ALL_FOLDER,
|
||||||
|
GENERAL_FOLDER,
|
||||||
|
ReadonlyFolderPicker,
|
||||||
|
} from '../../../core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<DashListOptions>(DashList)
|
export const plugin = new PanelPlugin<DashListOptions>(DashList)
|
||||||
.setPanelOptions((builder) => {
|
.setPanelOptions((builder) => {
|
||||||
@ -43,15 +46,13 @@ export const plugin = new PanelPlugin<DashListOptions>(DashList)
|
|||||||
path: 'folderId',
|
path: 'folderId',
|
||||||
name: 'Folder',
|
name: 'Folder',
|
||||||
id: 'folderId',
|
id: 'folderId',
|
||||||
defaultValue: null,
|
defaultValue: undefined,
|
||||||
editor: function RenderFolderPicker(props) {
|
editor: function RenderFolderPicker({ value, onChange }) {
|
||||||
return (
|
return (
|
||||||
<FolderPicker
|
<ReadonlyFolderPicker
|
||||||
initialFolderId={props.value}
|
initialFolderId={value}
|
||||||
initialTitle="All"
|
onChange={(folder) => onChange(folder?.id)}
|
||||||
enableReset={true}
|
extraFolders={[ALL_FOLDER, GENERAL_FOLDER]}
|
||||||
permissionLevel={PermissionLevelString.View}
|
|
||||||
onChange={({ id }) => props.onChange(id)}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user