mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Options: expose a dashboard picker element (#38955)
This commit is contained in:
parent
45e67630e8
commit
98cca6317d
@ -246,4 +246,14 @@ export class PanelOptionsEditorBuilder<TOptions> extends OptionsUIRegistryBuilde
|
||||
editor: standardEditorsRegistry.get('field-name').editor as any,
|
||||
});
|
||||
}
|
||||
|
||||
addDashboardPicker<TSettings = any>(
|
||||
config: PanelOptionsEditorConfig<TOptions, TSettings & FieldNamePickerConfigSettings, string>
|
||||
): this {
|
||||
return this.addCustomEditor({
|
||||
...config,
|
||||
id: config.path,
|
||||
editor: standardEditorsRegistry.get('dashboard-uid').editor as any, // added at runtime
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -229,6 +229,8 @@ export const getStandardFieldConfigs = () => {
|
||||
|
||||
/**
|
||||
* Returns collection of standard option editors definitions
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const getStandardOptionEditors = () => {
|
||||
const number: StandardEditorsRegistryItem<number> = {
|
||||
|
@ -31,7 +31,7 @@ import { reportPerformance } from './core/services/echo/EchoSrv';
|
||||
import { PerformanceBackend } from './core/services/echo/backends/PerformanceBackend';
|
||||
import 'app/routes/GrafanaCtrl';
|
||||
import 'app/features/all';
|
||||
import { getScrollbarWidth, getStandardFieldConfigs, getStandardOptionEditors } from '@grafana/ui';
|
||||
import { getScrollbarWidth, getStandardFieldConfigs } from '@grafana/ui';
|
||||
import { getDefaultVariableAdapters, variableAdapters } from './features/variables/adapters';
|
||||
import { initDevFeatures } from './dev';
|
||||
import { getStandardTransformers } from 'app/core/utils/standardTransformers';
|
||||
@ -49,6 +49,7 @@ import getDefaultMonacoLanguages from '../lib/monaco-languages';
|
||||
import { contextSrv } from './core/services/context_srv';
|
||||
import { GAEchoBackend } from './core/services/echo/backends/analytics/GABackend';
|
||||
import { RudderstackBackend } from './core/services/echo/backends/analytics/RudderstackBackend';
|
||||
import { getAllOptionEditors } from './core/components/editors/registry';
|
||||
|
||||
// add move to lodash for backward compatabilty with plugins
|
||||
// @ts-ignore
|
||||
@ -81,7 +82,7 @@ export class GrafanaApp {
|
||||
initExtensions();
|
||||
configureStore();
|
||||
|
||||
standardEditorsRegistry.setInit(getStandardOptionEditors);
|
||||
standardEditorsRegistry.setInit(getAllOptionEditors);
|
||||
standardFieldConfigEditorRegistry.setInit(getStandardFieldConfigs);
|
||||
standardTransformersRegistry.setInit(getStandardTransformers);
|
||||
variableAdapters.setInit(getDefaultVariableAdapters);
|
||||
|
63
public/app/core/components/editors/DashboardPicker.tsx
Normal file
63
public/app/core/components/editors/DashboardPicker.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import React, { FC, useCallback, useState } from 'react';
|
||||
import debounce from 'debounce-promise';
|
||||
import { SelectableValue, StandardEditorProps } from '@grafana/data';
|
||||
import { DashboardSearchHit } from 'app/features/search/types';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
import { AsyncSelect } from '@grafana/ui';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
export interface DashboardPickerOptions {
|
||||
placeholder?: string;
|
||||
isClearable?: boolean;
|
||||
}
|
||||
|
||||
const getDashboards = (query = '') => {
|
||||
return backendSrv.search({ type: 'dash-db', query, limit: 100 }).then((result: DashboardSearchHit[]) => {
|
||||
return result.map((item: DashboardSearchHit) => ({
|
||||
value: item.uid,
|
||||
label: `${item?.folderTitle ?? 'General'}/${item.title}`,
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
/** This will return the item UID */
|
||||
export const DashboardPicker: FC<StandardEditorProps<string, any, any>> = ({ value, onChange, item }) => {
|
||||
const [current, setCurrent] = useState<SelectableValue<string>>();
|
||||
|
||||
// This is required because the async select does not match the raw uid value
|
||||
// We can not use a simple Select because the dashboard search should not return *everything*
|
||||
useAsync(async () => {
|
||||
if (!value) {
|
||||
setCurrent(undefined);
|
||||
return;
|
||||
}
|
||||
const res = await backendSrv.getDashboardByUid(value);
|
||||
setCurrent({
|
||||
value: res.dashboard.uid,
|
||||
label: `${res.meta?.folderTitle ?? 'General'}/${res.dashboard.title}`,
|
||||
});
|
||||
return undefined;
|
||||
}, [value]);
|
||||
|
||||
const onPicked = useCallback(
|
||||
(sel: SelectableValue<string>) => {
|
||||
onChange(sel?.value);
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
const debouncedSearch = debounce(getDashboards, 300);
|
||||
const { placeholder, isClearable } = item?.settings ?? {};
|
||||
|
||||
return (
|
||||
<AsyncSelect
|
||||
menuShouldPortal
|
||||
isClearable={isClearable}
|
||||
defaultOptions={true}
|
||||
loadOptions={debouncedSearch}
|
||||
onChange={onPicked}
|
||||
placeholder={placeholder ?? 'Select dashboard'}
|
||||
noOptionsMessage="No dashboards found"
|
||||
value={current}
|
||||
/>
|
||||
);
|
||||
};
|
@ -5,14 +5,19 @@ import { AsyncSelect } from '@grafana/ui';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
import { DashboardSearchHit } from 'app/features/search/types';
|
||||
|
||||
export interface DashboardPickerItem extends Pick<DashboardSearchHit, 'uid' | 'id'> {
|
||||
/**
|
||||
* @deprecated prefer using dashboard uid rather than id
|
||||
*/
|
||||
export interface DashboardPickerItem extends SelectableValue<number> {
|
||||
id: number;
|
||||
uid: string;
|
||||
value: number;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
interface Props {
|
||||
onChange: (dashboard: DashboardPickerItem) => void;
|
||||
value?: SelectableValue;
|
||||
value?: DashboardPickerItem;
|
||||
width?: number;
|
||||
isClearable?: boolean;
|
||||
invalid?: boolean;
|
||||
@ -20,7 +25,7 @@ export interface Props {
|
||||
}
|
||||
|
||||
const getDashboards = (query = '') => {
|
||||
return backendSrv.search({ type: 'dash-db', query }).then((result: DashboardSearchHit[]) => {
|
||||
return backendSrv.search({ type: 'dash-db', query, limit: 100 }).then((result: DashboardSearchHit[]) => {
|
||||
return result.map((item: DashboardSearchHit) => ({
|
||||
id: item.id,
|
||||
uid: item.uid,
|
||||
@ -30,7 +35,10 @@ const getDashboards = (query = '') => {
|
||||
});
|
||||
};
|
||||
|
||||
export const DashboardPicker: FC<Props> = ({ onChange, value, width, isClearable = false, invalid, disabled }) => {
|
||||
/**
|
||||
* @deprecated prefer using dashboard uid rather than id
|
||||
*/
|
||||
export const DashboardPickerByID: FC<Props> = ({ onChange, value, width, isClearable = false, invalid, disabled }) => {
|
||||
const debouncedSearch = debounce(getDashboards, 300);
|
||||
|
||||
return (
|
16
public/app/core/components/editors/registry.tsx
Normal file
16
public/app/core/components/editors/registry.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { DashboardPicker, DashboardPickerOptions } from './DashboardPicker';
|
||||
import { getStandardOptionEditors } from '@grafana/ui';
|
||||
import { StandardEditorsRegistryItem } from '@grafana/data';
|
||||
|
||||
/**
|
||||
* Returns collection of standard option editors definitions
|
||||
*/
|
||||
export const getAllOptionEditors = () => {
|
||||
const dashboardPicker: StandardEditorsRegistryItem<string, DashboardPickerOptions> = {
|
||||
id: 'dashboard-uid',
|
||||
name: 'Dashboard',
|
||||
description: 'Select dashboard',
|
||||
editor: DashboardPicker as any,
|
||||
};
|
||||
return [...getStandardOptionEditors(), dashboardPicker];
|
||||
};
|
@ -4,11 +4,11 @@ import { Button, Field, Form, HorizontalGroup, Input, LinkButton } from '@grafan
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { Playlist } from './types';
|
||||
import { DashboardPicker } from '../../core/components/Select/DashboardPicker';
|
||||
import { TagFilter } from '../../core/components/TagFilter/TagFilter';
|
||||
import { SearchSrv } from '../../core/services/search_srv';
|
||||
import { usePlaylistItems } from './usePlaylistItems';
|
||||
import { PlaylistTable } from './PlaylistTable';
|
||||
import { DashboardPickerByID } from 'app/core/components/editors/DashboardPickerByID';
|
||||
|
||||
interface PlaylistFormProps {
|
||||
onSubmit: (playlist: Playlist) => void;
|
||||
@ -52,7 +52,7 @@ export const PlaylistForm: FC<PlaylistFormProps> = ({ onSubmit, playlist }) => {
|
||||
<h3 className="page-headering">Add dashboards</h3>
|
||||
|
||||
<Field label="Add by title">
|
||||
<DashboardPicker onChange={addById} isClearable />
|
||||
<DashboardPickerByID onChange={addById} isClearable />
|
||||
</Field>
|
||||
|
||||
<Field label="Add by tag">
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { DashboardPickerItem } from 'app/core/components/editors/DashboardPickerByID';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { PlaylistItem } from './types';
|
||||
import { DashboardPickerItem } from '../../core/components/Select/DashboardPicker';
|
||||
|
||||
export function usePlaylistItems(playlistItems?: PlaylistItem[]) {
|
||||
const [items, setItems] = useState<PlaylistItem[]>(playlistItems ?? []);
|
||||
|
||||
const addById = useCallback(
|
||||
(dashboard: DashboardPickerItem) => {
|
||||
if (items.find((item) => item.id === dashboard.id)) {
|
||||
if (!dashboard || items.find((item) => item.id === dashboard.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -33,5 +33,13 @@ export const plugin = new PanelPlugin<DebugPanelOptions>(DebugPanel).useFieldCon
|
||||
name: 'Schema Changed Count',
|
||||
defaultValue: true,
|
||||
showIf: ({ mode }) => mode === DebugMode.Render,
|
||||
})
|
||||
.addDashboardPicker({
|
||||
path: 'dashboardUID',
|
||||
name: 'Dashboard',
|
||||
settings: {
|
||||
placeholder: 'Select dashboard',
|
||||
isClearable: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user