mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard: Prevents folder change when navigating to general settings (#38103)
* Dashboard: Prevents folder change when navigating to general settings * Tests: fixes broken tests * Chore: changes from PR feedback
This commit is contained in:
parent
5ff3b7bf3f
commit
a0773b290b
@ -4,7 +4,7 @@ import debounce from 'debounce-promise';
|
|||||||
import { AsyncMultiSelect, Icon, resetSelectStyles, useStyles2 } from '@grafana/ui';
|
import { AsyncMultiSelect, Icon, resetSelectStyles, useStyles2 } from '@grafana/ui';
|
||||||
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||||
|
|
||||||
import { FolderInfo } from 'app/types';
|
import { FolderInfo, PermissionLevelString } from 'app/types';
|
||||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||||
|
|
||||||
export interface FolderFilterProps {
|
export interface FolderFilterProps {
|
||||||
@ -67,7 +67,7 @@ async function getFoldersAsOptions(searchString: string, setLoading: (loading: b
|
|||||||
const params = {
|
const params = {
|
||||||
query: searchString,
|
query: searchString,
|
||||||
type: 'dash-folder',
|
type: 'dash-folder',
|
||||||
permission: 'View',
|
permission: PermissionLevelString.View,
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchHits = await getBackendSrv().search(params);
|
const searchHits = await getBackendSrv().search(params);
|
||||||
|
@ -1,30 +1,49 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import { FolderPicker } from './FolderPicker';
|
|
||||||
|
|
||||||
jest.mock('@grafana/runtime', () => ({
|
import { FolderPicker, getInitialValues } from './FolderPicker';
|
||||||
...((jest.requireActual('@grafana/runtime') as unknown) as object),
|
import * as api from 'app/features/manage-dashboards/state/actions';
|
||||||
getBackendSrv: () => ({
|
import { DashboardSearchHit } from '../../../features/search/types';
|
||||||
search: jest.fn(() => [
|
|
||||||
{ title: 'Dash 1', id: 'A' },
|
|
||||||
{ title: 'Dash 2', id: 'B' },
|
|
||||||
]),
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../../config', () => ({
|
|
||||||
getConfig: () => ({}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('app/core/services/context_srv', () => ({
|
|
||||||
contextSrv: {
|
|
||||||
user: { orgId: 1 },
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('FolderPicker', () => {
|
describe('FolderPicker', () => {
|
||||||
it('should render', () => {
|
it('should render', () => {
|
||||||
|
jest
|
||||||
|
.spyOn(api, 'searchFolders')
|
||||||
|
.mockResolvedValue([
|
||||||
|
{ title: 'Dash 1', id: 1 } as DashboardSearchHit,
|
||||||
|
{ title: 'Dash 2', id: 2 } as DashboardSearchHit,
|
||||||
|
]);
|
||||||
const wrapper = shallow(<FolderPicker onChange={jest.fn()} />);
|
const wrapper = shallow(<FolderPicker onChange={jest.fn()} />);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getInitialValues', () => {
|
||||||
|
describe('when called with folderId and title', () => {
|
||||||
|
it('then it should return folderId and title', async () => {
|
||||||
|
const getFolder = jest.fn().mockResolvedValue({});
|
||||||
|
const folder = await getInitialValues({ folderId: 0, folderName: 'Some title', getFolder });
|
||||||
|
|
||||||
|
expect(folder).toEqual({ label: 'Some title', value: 0 });
|
||||||
|
expect(getFolder).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when called with just a folderId', () => {
|
||||||
|
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 });
|
||||||
|
|
||||||
|
expect(folder).toEqual({ label: 'Title from api', value: 0 });
|
||||||
|
expect(getFolder).toHaveBeenCalledTimes(1);
|
||||||
|
expect(getFolder).toHaveBeenCalledWith(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when called without folderId', () => {
|
||||||
|
it('then it should throw an error', async () => {
|
||||||
|
const getFolder = jest.fn().mockResolvedValue({});
|
||||||
|
await expect(getInitialValues({ getFolder })).rejects.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -2,13 +2,12 @@ import React, { PureComponent } from 'react';
|
|||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { AsyncSelect } from '@grafana/ui';
|
import { AsyncSelect } from '@grafana/ui';
|
||||||
import { AppEvents, SelectableValue } from '@grafana/data';
|
import { AppEvents, SelectableValue } from '@grafana/data';
|
||||||
import { getBackendSrv } from '@grafana/runtime';
|
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
import appEvents from '../../app_events';
|
import appEvents from '../../app_events';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
import { DashboardSearchHit } from 'app/features/search/types';
|
import { createFolder, getFolderById, searchFolders } from 'app/features/manage-dashboards/state/actions';
|
||||||
import { createFolder } from 'app/features/manage-dashboards/state/actions';
|
import { PermissionLevelString } from '../../../types';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
onChange: ($folder: { title: string; id: number }) => void;
|
onChange: ($folder: { title: string; id: number }) => void;
|
||||||
@ -18,9 +17,16 @@ export interface Props {
|
|||||||
dashboardId?: any;
|
dashboardId?: any;
|
||||||
initialTitle?: string;
|
initialTitle?: string;
|
||||||
initialFolderId?: number;
|
initialFolderId?: number;
|
||||||
permissionLevel?: 'View' | 'Edit';
|
permissionLevel?: Exclude<PermissionLevelString, PermissionLevelString.Admin>;
|
||||||
allowEmpty?: boolean;
|
allowEmpty?: boolean;
|
||||||
showRoot?: boolean;
|
showRoot?: boolean;
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
skipInitialLoad?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@ -48,26 +54,29 @@ export class FolderPicker extends PureComponent<Props, State> {
|
|||||||
enableReset: false,
|
enableReset: false,
|
||||||
initialTitle: '',
|
initialTitle: '',
|
||||||
enableCreateNew: false,
|
enableCreateNew: false,
|
||||||
permissionLevel: 'Edit',
|
permissionLevel: PermissionLevelString.Edit,
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
showRoot: true,
|
showRoot: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount = async () => {
|
componentDidMount = async () => {
|
||||||
|
if (this.props.skipInitialLoad) {
|
||||||
|
const folder = await getInitialValues({
|
||||||
|
getFolder: getFolderById,
|
||||||
|
folderId: this.props.initialFolderId,
|
||||||
|
folderName: this.props.initialTitle,
|
||||||
|
});
|
||||||
|
this.setState({ folder });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await this.loadInitialValue();
|
await this.loadInitialValue();
|
||||||
};
|
};
|
||||||
|
|
||||||
getOptions = async (query: string) => {
|
getOptions = async (query: string) => {
|
||||||
const { rootName, enableReset, initialTitle, permissionLevel, initialFolderId, showRoot } = this.props;
|
const { rootName, enableReset, initialTitle, permissionLevel, initialFolderId, showRoot } = this.props;
|
||||||
const params = {
|
|
||||||
query,
|
|
||||||
type: 'dash-folder',
|
|
||||||
permission: permissionLevel,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: move search to BackendSrv interface
|
const searchHits = await searchFolders(query, permissionLevel);
|
||||||
// @ts-ignore
|
|
||||||
const searchHits = (await getBackendSrv().search(params)) as DashboardSearchHit[];
|
|
||||||
|
|
||||||
const options: Array<SelectableValue<number>> = searchHits.map((hit) => ({ label: hit.title, value: hit.id }));
|
const options: Array<SelectableValue<number>> = searchHits.map((hit) => ({ label: hit.title, value: hit.id }));
|
||||||
if (contextSrv.isEditor && rootName?.toLowerCase().startsWith(query.toLowerCase()) && showRoot) {
|
if (contextSrv.isEditor && rootName?.toLowerCase().startsWith(query.toLowerCase()) && showRoot) {
|
||||||
@ -179,3 +188,22 @@ export class FolderPicker extends PureComponent<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Args {
|
||||||
|
getFolder: typeof getFolderById;
|
||||||
|
folderId?: number;
|
||||||
|
folderName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getInitialValues({ folderName, folderId, getFolder }: Args): Promise<SelectableValue<number>> {
|
||||||
|
if (folderId === null || folderId === undefined || folderId < 0) {
|
||||||
|
throw new Error('folderId should to be greater or equal to zero.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folderName) {
|
||||||
|
return { label: folderName, value: folderId };
|
||||||
|
}
|
||||||
|
|
||||||
|
const folderDto = await getFolder(folderId);
|
||||||
|
return { label: folderDto.title, value: folderId };
|
||||||
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Matcher, render, waitFor } from '@testing-library/react';
|
import { Matcher, render, waitFor } from '@testing-library/react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { locationService, setDataSourceSrv, setBackendSrv, BackendSrv } from '@grafana/runtime';
|
import { locationService, setDataSourceSrv } from '@grafana/runtime';
|
||||||
import { configureStore } from 'app/store/configureStore';
|
import { configureStore } from 'app/store/configureStore';
|
||||||
import RuleEditor from './RuleEditor';
|
import RuleEditor from './RuleEditor';
|
||||||
import { Router, Route } from 'react-router-dom';
|
import { Route, Router } from 'react-router-dom';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { byLabelText, byRole, byTestId, byText } from 'testing-library-selector';
|
import { byLabelText, byRole, byTestId, byText } from 'testing-library-selector';
|
||||||
import { selectOptionInTest } from '@grafana/ui';
|
import { selectOptionInTest } from '@grafana/ui';
|
||||||
@ -18,6 +18,7 @@ import { DataSourceType, GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
|
|||||||
import { DashboardSearchHit } from 'app/features/search/types';
|
import { DashboardSearchHit } from 'app/features/search/types';
|
||||||
import { getDefaultQueries } from './utils/rule-form';
|
import { getDefaultQueries } from './utils/rule-form';
|
||||||
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
||||||
|
import * as api from 'app/features/manage-dashboards/state/actions';
|
||||||
|
|
||||||
jest.mock('./components/rule-editor/ExpressionEditor', () => ({
|
jest.mock('./components/rule-editor/ExpressionEditor', () => ({
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
@ -163,7 +164,7 @@ describe('RuleEditor', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('can create new grafana managed alert', async () => {
|
it('can create new grafana managed alert', async () => {
|
||||||
const searchFolderMock = jest.fn().mockResolvedValue([
|
const searchFolderMock = jest.spyOn(api, 'searchFolders').mockResolvedValue([
|
||||||
{
|
{
|
||||||
title: 'Folder A',
|
title: 'Folder A',
|
||||||
id: 1,
|
id: 1,
|
||||||
@ -182,10 +183,6 @@ describe('RuleEditor', () => {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const backendSrv = ({
|
|
||||||
search: searchFolderMock,
|
|
||||||
} as any) as BackendSrv;
|
|
||||||
setBackendSrv(backendSrv);
|
|
||||||
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
||||||
mocks.api.setRulerRuleGroup.mockResolvedValue();
|
mocks.api.setRulerRuleGroup.mockResolvedValue();
|
||||||
mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]);
|
mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]);
|
||||||
|
@ -4,25 +4,9 @@ import userEvent from '@testing-library/user-event';
|
|||||||
import { selectOptionInTest } from '@grafana/ui';
|
import { selectOptionInTest } from '@grafana/ui';
|
||||||
|
|
||||||
import { byRole } from 'testing-library-selector';
|
import { byRole } from 'testing-library-selector';
|
||||||
import { Props, GeneralSettingsUnconnected as GeneralSettings } from './GeneralSettings';
|
import { GeneralSettingsUnconnected as GeneralSettings, Props } from './GeneralSettings';
|
||||||
import { DashboardModel } from '../../state';
|
import { DashboardModel } from '../../state';
|
||||||
|
|
||||||
jest.mock('@grafana/runtime', () => ({
|
|
||||||
...((jest.requireActual('@grafana/runtime') as unknown) as object),
|
|
||||||
getBackendSrv: () => ({
|
|
||||||
search: jest.fn(() => [
|
|
||||||
{ title: 'A', id: 'A' },
|
|
||||||
{ title: 'B', id: 'B' },
|
|
||||||
]),
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('app/core/services/context_srv', () => ({
|
|
||||||
contextSrv: {
|
|
||||||
user: { orgId: 1 },
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const setupTestContext = (options: Partial<Props>) => {
|
const setupTestContext = (options: Partial<Props>) => {
|
||||||
const defaults: Props = {
|
const defaults: Props = {
|
||||||
dashboard: ({
|
dashboard: ({
|
||||||
@ -33,6 +17,7 @@ const setupTestContext = (options: Partial<Props>) => {
|
|||||||
time_options: ['5m', '15m', '1h', '6h', '12h', '24h', '2d', '7d', '30d'],
|
time_options: ['5m', '15m', '1h', '6h', '12h', '24h', '2d', '7d', '30d'],
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
|
folderId: 1,
|
||||||
folderTitle: 'test',
|
folderTitle: 'test',
|
||||||
},
|
},
|
||||||
timezone: 'utc',
|
timezone: 'utc',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { connect, ConnectedProps } from 'react-redux';
|
import { connect, ConnectedProps } from 'react-redux';
|
||||||
import { TimeZone } from '@grafana/data';
|
import { TimeZone } from '@grafana/data';
|
||||||
import { TagsInput, Input, Field, CollapsableSection, RadioButtonGroup } from '@grafana/ui';
|
import { CollapsableSection, Field, Input, RadioButtonGroup, TagsInput } from '@grafana/ui';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
||||||
import { DashboardModel } from '../../state/DashboardModel';
|
import { DashboardModel } from '../../state/DashboardModel';
|
||||||
@ -96,6 +96,7 @@ export function GeneralSettingsUnconnected({ dashboard, updateTimeZone }: Props)
|
|||||||
onChange={onFolderChange}
|
onChange={onFolderChange}
|
||||||
enableCreateNew={true}
|
enableCreateNew={true}
|
||||||
dashboardId={dashboard.id}
|
dashboardId={dashboard.id}
|
||||||
|
skipInitialLoad={true}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
@ -3,17 +3,7 @@ import { mount } from 'enzyme';
|
|||||||
import { SaveDashboardAsForm } from './SaveDashboardAsForm';
|
import { SaveDashboardAsForm } from './SaveDashboardAsForm';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import * as api from 'app/features/manage-dashboards/state/actions';
|
||||||
jest.mock('@grafana/runtime', () => ({
|
|
||||||
...((jest.requireActual('@grafana/runtime') as unknown) as object),
|
|
||||||
getBackendSrv: () => ({ get: jest.fn().mockResolvedValue([]), search: jest.fn().mockResolvedValue([]) }),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('app/core/services/context_srv', () => ({
|
|
||||||
contextSrv: {
|
|
||||||
user: { orgId: 1 },
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('app/features/plugins/datasource_srv', () => ({}));
|
jest.mock('app/features/plugins/datasource_srv', () => ({}));
|
||||||
jest.mock('app/features/expressions/ExpressionDatasource', () => ({}));
|
jest.mock('app/features/expressions/ExpressionDatasource', () => ({}));
|
||||||
@ -21,6 +11,8 @@ jest.mock('app/features/manage-dashboards/services/ValidationSrv', () => ({
|
|||||||
validateNewDashboardName: () => true,
|
validateNewDashboardName: () => true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.spyOn(api, 'searchFolders').mockResolvedValue([]);
|
||||||
|
|
||||||
const prepareDashboardMock = (panel: any) => {
|
const prepareDashboardMock = (panel: any) => {
|
||||||
const json = {
|
const json = {
|
||||||
title: 'name',
|
title: 'name',
|
||||||
@ -56,6 +48,7 @@ const renderAndSubmitForm = async (dashboard: any, submitSpy: any) => {
|
|||||||
describe('SaveDashboardAsForm', () => {
|
describe('SaveDashboardAsForm', () => {
|
||||||
describe('default values', () => {
|
describe('default values', () => {
|
||||||
it('applies default dashboard properties', async () => {
|
it('applies default dashboard properties', async () => {
|
||||||
|
jest.spyOn(api, 'searchFolders').mockResolvedValue([]);
|
||||||
const spy = jest.fn();
|
const spy = jest.fn();
|
||||||
|
|
||||||
await renderAndSubmitForm(prepareDashboardMock({}), spy);
|
await renderAndSubmitForm(prepareDashboardMock({}), spy);
|
||||||
|
@ -2,16 +2,17 @@ import { AppEvents, DataSourceInstanceSettings, locationUtil } from '@grafana/da
|
|||||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||||
import {
|
import {
|
||||||
clearDashboard,
|
clearDashboard,
|
||||||
setInputs,
|
|
||||||
setGcomDashboard,
|
|
||||||
setJsonDashboard,
|
|
||||||
InputType,
|
|
||||||
ImportDashboardDTO,
|
ImportDashboardDTO,
|
||||||
|
InputType,
|
||||||
|
setGcomDashboard,
|
||||||
|
setInputs,
|
||||||
|
setJsonDashboard,
|
||||||
} from './reducers';
|
} from './reducers';
|
||||||
import { ThunkResult, FolderInfo, DashboardDTO, DashboardDataDTO } from 'app/types';
|
import { DashboardDataDTO, DashboardDTO, FolderInfo, PermissionLevelString, ThunkResult } from 'app/types';
|
||||||
import { appEvents } from '../../../core/core';
|
import { appEvents } from '../../../core/core';
|
||||||
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
||||||
import { getDataSourceSrv, locationService } from '@grafana/runtime';
|
import { getDataSourceSrv, locationService } from '@grafana/runtime';
|
||||||
|
import { DashboardSearchHit } from '../../search/types';
|
||||||
|
|
||||||
export function fetchGcomDashboard(id: string): ThunkResult<void> {
|
export function fetchGcomDashboard(id: string): ThunkResult<void> {
|
||||||
return async (dispatch) => {
|
return async (dispatch) => {
|
||||||
@ -222,6 +223,14 @@ export function createFolder(payload: any) {
|
|||||||
return getBackendSrv().post('/api/folders', payload);
|
return getBackendSrv().post('/api/folders', payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function searchFolders(query: any, permission?: PermissionLevelString): Promise<DashboardSearchHit[]> {
|
||||||
|
return getBackendSrv().search({ query, type: 'dash-folder', permission });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFolderById(id: number): Promise<{ id: number; title: string }> {
|
||||||
|
return getBackendSrv().get(`/api/folders/id/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
export function deleteDashboard(uid: string, showSuccessAlert: boolean) {
|
export function deleteDashboard(uid: string, showSuccessAlert: boolean) {
|
||||||
return getBackendSrv().request({
|
return getBackendSrv().request({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
@ -4,6 +4,7 @@ import { DashListOptions } from './types';
|
|||||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
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';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<DashListOptions>(DashList)
|
export const plugin = new PanelPlugin<DashListOptions>(DashList)
|
||||||
.setPanelOptions((builder) => {
|
.setPanelOptions((builder) => {
|
||||||
@ -49,7 +50,7 @@ export const plugin = new PanelPlugin<DashListOptions>(DashList)
|
|||||||
initialFolderId={props.value}
|
initialFolderId={props.value}
|
||||||
initialTitle="All"
|
initialTitle="All"
|
||||||
enableReset={true}
|
enableReset={true}
|
||||||
permissionLevel="View"
|
permissionLevel={PermissionLevelString.View}
|
||||||
onChange={({ id }) => props.onChange(id)}
|
onChange={({ id }) => props.onChange(id)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -63,6 +63,12 @@ export enum PermissionLevel {
|
|||||||
Admin = 4,
|
Admin = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum PermissionLevelString {
|
||||||
|
View = 'View',
|
||||||
|
Edit = 'Edit',
|
||||||
|
Admin = 'Admin',
|
||||||
|
}
|
||||||
|
|
||||||
export enum DataSourcePermissionLevel {
|
export enum DataSourcePermissionLevel {
|
||||||
Query = 1,
|
Query = 1,
|
||||||
Admin = 2,
|
Admin = 2,
|
||||||
@ -92,8 +98,12 @@ export const dashboardAclTargets: AclTargetInfo[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const dashboardPermissionLevels: DashboardPermissionInfo[] = [
|
export const dashboardPermissionLevels: DashboardPermissionInfo[] = [
|
||||||
{ value: PermissionLevel.View, label: 'View', description: 'Can view dashboards.' },
|
{ value: PermissionLevel.View, label: PermissionLevelString.View, description: 'Can view dashboards.' },
|
||||||
{ value: PermissionLevel.Edit, label: 'Edit', description: 'Can add, edit and delete dashboards.' },
|
{
|
||||||
|
value: PermissionLevel.Edit,
|
||||||
|
label: PermissionLevelString.Edit,
|
||||||
|
description: 'Can add, edit and delete dashboards.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: PermissionLevel.Admin,
|
value: PermissionLevel.Admin,
|
||||||
label: 'Admin',
|
label: 'Admin',
|
||||||
|
Loading…
Reference in New Issue
Block a user