From b10392733d61f12dc5806a170a1bc6bb9e23fbf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 6 Apr 2020 16:24:41 +0200 Subject: [PATCH] FieldConfig: Unify the custom and standard registry (#23307) * FieldConfig: Unifying standard and custom registry * Adding path to option items to make id be prefixed for custom options * Code updates progress * Add docs back * Fix TS * ld overrides tests from ui to data * Refactor - rename * Gauge and table cleanup * F-I-X e2e Co-authored-by: Dominik Prokop --- .../src/field/FieldConfigOptionsRegistry.tsx | 4 + .../src/field/fieldOverrides.test.ts | 146 ++++++++++-- .../grafana-data/src/field/fieldOverrides.ts | 44 ++-- packages/grafana-data/src/field/index.ts | 1 + .../standardFieldConfigEditorRegistry.ts | 4 +- .../src/panel/PanelPlugin.test.tsx | 158 ++++++++----- .../grafana-data/src/panel/PanelPlugin.ts | 223 ++++++++---------- .../src/types/OptionsUIRegistryBuilder.ts | 2 +- .../grafana-data/src/types/fieldOverrides.ts | 16 +- packages/grafana-data/src/types/panel.ts | 2 +- .../src/utils/OptionsUIBuilders.ts | 14 ++ packages/grafana-data/src/utils/Registry.ts | 2 +- .../src/utils/tests/mockStandardProperties.ts | 170 +++++++++++++ .../FieldConfigs/fieldOverrides.test.ts | 126 ---------- .../src/components/Table/Table.story.tsx | 20 +- .../grafana-ui/src/utils/standardEditors.tsx | 28 ++- .../components/Inspector/PanelInspector.tsx | 2 +- .../PanelEditor/DynamicConfigValueEditor.tsx | 8 +- .../PanelEditor/FieldConfigEditor.tsx | 69 +----- .../PanelEditor/OptionsPaneContent.tsx | 1 - .../components/PanelEditor/OverrideEditor.tsx | 40 ++-- .../PanelEditor/PanelOptionsEditor.tsx | 2 +- .../dashboard/state/PanelModel.test.ts | 55 ++++- .../features/dashboard/state/PanelModel.ts | 4 +- .../dashboard/state/PanelQueryRunner.test.ts | 2 +- .../fieldDisplayValuesProxy.test.ts | 2 +- .../panel/panellinks/linkSuppliers.test.ts | 2 +- public/app/plugins/panel/bargauge/module.tsx | 8 +- public/app/plugins/panel/gauge/module.tsx | 9 +- public/app/plugins/panel/piechart/module.tsx | 5 +- public/app/plugins/panel/stat/module.tsx | 10 +- public/app/plugins/panel/stat/types.ts | 28 +-- public/app/plugins/panel/table2/module.tsx | 54 +++-- 33 files changed, 707 insertions(+), 554 deletions(-) create mode 100644 packages/grafana-data/src/field/FieldConfigOptionsRegistry.tsx create mode 100644 packages/grafana-data/src/utils/tests/mockStandardProperties.ts delete mode 100644 packages/grafana-ui/src/components/FieldConfigs/fieldOverrides.test.ts diff --git a/packages/grafana-data/src/field/FieldConfigOptionsRegistry.tsx b/packages/grafana-data/src/field/FieldConfigOptionsRegistry.tsx new file mode 100644 index 00000000000..30af765b20b --- /dev/null +++ b/packages/grafana-data/src/field/FieldConfigOptionsRegistry.tsx @@ -0,0 +1,4 @@ +import { Registry } from '../utils'; +import { FieldPropertyEditorItem } from '../types'; + +export class FieldConfigOptionsRegistry extends Registry {} diff --git a/packages/grafana-data/src/field/fieldOverrides.test.ts b/packages/grafana-data/src/field/fieldOverrides.test.ts index 8cacef5316b..2bb2818b58c 100644 --- a/packages/grafana-data/src/field/fieldOverrides.test.ts +++ b/packages/grafana-data/src/field/fieldOverrides.test.ts @@ -4,44 +4,37 @@ import { setFieldConfigDefaults, applyFieldOverrides, } from './fieldOverrides'; -import { MutableDataFrame } from '../dataframe'; +import { MutableDataFrame, toDataFrame } from '../dataframe'; import { FieldConfig, - FieldConfigEditorRegistry, - FieldOverrideContext, FieldPropertyEditorItem, GrafanaTheme, FieldType, + DataFrame, + FieldConfigSource, + InterpolateFunction, } from '../types'; import { Registry } from '../utils'; -import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry'; +import { mockStandardProperties } from '../utils/tests/mockStandardProperties'; +import { FieldMatcherID } from '../transformations'; +import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; const property1 = { - id: 'property1', // Match field properties + id: 'custom.property1', // Match field properties + path: 'property1', // Match field properties process: (value: any) => value, shouldApply: () => true, } as any; const property2 = { - id: 'property2', // Match field properties + id: 'custom.property2', // Match field properties + path: 'property2', // Match field properties process: (value: any) => value, shouldApply: () => true, } as any; -const unit = { - id: 'unit', // Match field properties - process: (value: any) => value, - shouldApply: () => true, -} as any; - -export const customFieldRegistry: FieldConfigEditorRegistry = new Registry(() => { - return [property1, property2]; -}); - -// For the need of this test we need to mock the standard registry -// as we cannot imporrt from grafana/ui -standardFieldConfigEditorRegistry.setInit(() => { - return [unit]; +export const customFieldRegistry: FieldConfigOptionsRegistry = new Registry(() => { + return [property1, property2, ...mockStandardProperties()]; }); describe('Global MinMax', () => { @@ -59,6 +52,32 @@ describe('Global MinMax', () => { }); describe('applyFieldOverrides', () => { + const f0 = new MutableDataFrame(); + f0.add({ title: 'AAA', value: 100, value2: 1234 }, true); + f0.add({ title: 'BBB', value: -20 }, true); + f0.add({ title: 'CCC', value: 200, value2: 1000 }, true); + expect(f0.length).toEqual(3); + + // Hardcode the max value + f0.fields[1].config.max = 0; + f0.fields[1].config.decimals = 6; + + const src: FieldConfigSource = { + defaults: { + unit: 'xyz', + decimals: 2, + }, + overrides: [ + { + matcher: { id: FieldMatcherID.numeric }, + properties: [ + { id: 'decimals', value: 1 }, // Numeric + { id: 'title', value: 'Kittens' }, // Text + ], + }, + ], + }; + describe('given multiple data frames', () => { const f0 = new MutableDataFrame({ name: 'A', @@ -72,12 +91,13 @@ describe('applyFieldOverrides', () => { it('should add scopedVars to fields', () => { const withOverrides = applyFieldOverrides({ data: [f0, f1], - fieldOptions: { + fieldConfig: { defaults: {}, overrides: [], }, replaceVariables: (value: any) => value, theme: {} as GrafanaTheme, + fieldConfigRegistry: new FieldConfigOptionsRegistry(), }); expect(withOverrides[0].fields[0].config.scopedVars).toMatchInlineSnapshot(` @@ -115,6 +135,83 @@ describe('applyFieldOverrides', () => { `); }); }); + + it('will merge FieldConfig with default values', () => { + const field: FieldConfig = { + min: 0, + max: 100, + }; + const f1 = { + unit: 'ms', + dateFormat: '', // should be ignored + max: parseFloat('NOPE'), // should be ignored + min: null, // should alo be ignored! + }; + + const f: DataFrame = toDataFrame({ + fields: [{ type: FieldType.number, name: 'x', config: field, values: [] }], + }); + const processed = applyFieldOverrides({ + data: [f], + fieldConfig: { + defaults: f1 as FieldConfig, + overrides: [], + }, + fieldConfigRegistry: customFieldRegistry, + replaceVariables: v => v, + theme: {} as GrafanaTheme, + })[0]; + const out = processed.fields[0].config; + + expect(out.min).toEqual(0); + expect(out.max).toEqual(100); + expect(out.unit).toEqual('ms'); + }); + + it('will apply field overrides', () => { + const data = applyFieldOverrides({ + data: [f0], // the frame + fieldConfig: src as FieldConfigSource, // defaults + overrides + replaceVariables: (undefined as any) as InterpolateFunction, + theme: (undefined as any) as GrafanaTheme, + fieldConfigRegistry: customFieldRegistry, + })[0]; + const valueColumn = data.fields[1]; + const config = valueColumn.config; + + // Keep max from the original setting + expect(config.max).toEqual(0); + + // Don't Automatically pick the min value + expect(config.min).toEqual(undefined); + + // The default value applied + expect(config.unit).toEqual('xyz'); + + // The default value applied + expect(config.title).toEqual('Kittens'); + + // The override applied + expect(config.decimals).toEqual(1); + }); + + it('will apply set min/max when asked', () => { + const data = applyFieldOverrides({ + data: [f0], // the frame + fieldConfig: src as FieldConfigSource, // defaults + overrides + replaceVariables: (undefined as any) as InterpolateFunction, + theme: (undefined as any) as GrafanaTheme, + autoMinMax: true, + })[0]; + const valueColumn = data.fields[1]; + const config = valueColumn.config; + + // Keep max from the original setting + expect(config.max).toEqual(0); + + // Don't Automatically pick the min value + expect(config.min).toEqual(-20); + }); }); describe('setFieldConfigDefaults', () => { @@ -132,10 +229,11 @@ describe('setFieldConfigDefaults', () => { unit: 'km', }; - const context: FieldOverrideContext = { + const context: FieldOverrideEnv = { data: [] as any, field: { type: FieldType.number } as any, dataFrameIndex: 0, + fieldConfigRegistry: customFieldRegistry, }; // we mutate dsFieldConfig @@ -169,7 +267,7 @@ describe('setFieldConfigDefaults', () => { data: [] as any, field: { type: FieldType.number } as any, dataFrameIndex: 0, - custom: customFieldRegistry, + fieldConfigRegistry: customFieldRegistry, }; // we mutate dsFieldConfig @@ -178,7 +276,7 @@ describe('setFieldConfigDefaults', () => { expect(dsFieldConfig).toMatchInlineSnapshot(` Object { "custom": Object { - "property1": 10, + "property1": 20, "property2": 10, }, } diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index ff18d42ce4c..126693a2d3d 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -7,7 +7,6 @@ import { ThresholdsMode, FieldColorMode, ColorScheme, - FieldConfigEditorRegistry, FieldOverrideContext, ScopedVars, ApplyFieldOverrideOptions, @@ -18,6 +17,7 @@ import isNumber from 'lodash/isNumber'; import { getDisplayProcessor } from './displayProcessor'; import { guessFieldTypeForField } from '../dataframe'; import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry'; +import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; interface OverrideProps { match: FieldMatcher; @@ -59,11 +59,13 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra return []; } - const source = options.fieldOptions; + const source = options.fieldConfig; if (!source) { return options.data; } + const fieldConfigRegistry = options.fieldConfigRegistry ?? standardFieldConfigEditorRegistry; + let range: GlobalMinMax | undefined = undefined; // Prepare the Matchers @@ -105,7 +107,7 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra data: options.data!, dataFrameIndex: index, replaceVariables: options.replaceVariables, - custom: options.custom, + fieldConfigRegistry: fieldConfigRegistry, }; // Anything in the field config that's not set by the datasource @@ -188,13 +190,13 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra } export interface FieldOverrideEnv extends FieldOverrideContext { - custom?: FieldConfigEditorRegistry; + fieldConfigRegistry: FieldConfigOptionsRegistry; } function setDynamicConfigValue(config: FieldConfig, value: DynamicConfigValue, context: FieldOverrideEnv) { - const reg = value.custom ? context.custom : standardFieldConfigEditorRegistry; + const reg = context.fieldConfigRegistry; - const item = reg?.getIfExists(value.prop); + const item = reg.getIfExists(value.id); if (!item || !item.shouldApply(context.field!)) { return; } @@ -204,19 +206,19 @@ function setDynamicConfigValue(config: FieldConfig, value: DynamicConfigValue, c const remove = val === undefined || val === null; if (remove) { - if (value.custom && config.custom) { - delete config.custom[value.prop]; + if (value.isCustom && config.custom) { + delete config.custom[item.path]; } else { - delete (config as any)[value.prop]; + delete (config as any)[item.path]; } } else { - if (value.custom) { + if (value.isCustom) { if (!config.custom) { config.custom = {}; } - config.custom[value.prop] = val; + config.custom[item.path] = val; } else { - (config as any)[value.prop] = val; + (config as any)[item.path] = val; } } } @@ -228,23 +230,24 @@ export function setFieldConfigDefaults(config: FieldConfig, defaults: FieldConfi const keys = Object.keys(defaults); for (const key of keys) { if (key === 'custom') { - if (!context.custom) { + if (!context.fieldConfigRegistry) { continue; } if (!config.custom) { config.custom = {}; } - const customKeys = Object.keys(defaults.custom!); + const customKeys = Object.keys(defaults.custom!); for (const customKey of customKeys) { - processFieldConfigValue(config.custom!, defaults.custom!, customKey, context.custom, context); + processFieldConfigValue(config.custom!, defaults.custom!, `custom.${customKey}`, context); } } else { // when config from ds exists for a given field -> use it - processFieldConfigValue(config, defaults, key, standardFieldConfigEditorRegistry, context); + processFieldConfigValue(config, defaults, key, context); } } } + validateFieldConfig(config); } @@ -252,20 +255,19 @@ const processFieldConfigValue = ( destination: Record, // it's mutable source: Record, key: string, - registry: FieldConfigEditorRegistry, - context: FieldOverrideContext + context: FieldOverrideEnv ) => { const currentConfig = destination[key]; if (currentConfig === null || currentConfig === undefined) { - const item = registry.getIfExists(key); + const item = context.fieldConfigRegistry.getIfExists(key); if (!item) { return; } if (item && item.shouldApply(context.field!)) { - const val = item.process(source[key], context, item.settings); + const val = item.process(source[item.path], context, item.settings); if (val !== undefined && val !== null) { - destination[key] = val; + destination[item.path] = val; } } } diff --git a/packages/grafana-data/src/field/index.ts b/packages/grafana-data/src/field/index.ts index 2ba7edd4c50..548a9ab98e7 100644 --- a/packages/grafana-data/src/field/index.ts +++ b/packages/grafana-data/src/field/index.ts @@ -3,5 +3,6 @@ export * from './displayProcessor'; export * from './scale'; export * from './standardFieldConfigEditorRegistry'; export * from './overrides/processors'; +export { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; export { applyFieldOverrides, validateFieldConfig } from './fieldOverrides'; diff --git a/packages/grafana-data/src/field/standardFieldConfigEditorRegistry.ts b/packages/grafana-data/src/field/standardFieldConfigEditorRegistry.ts index 41f108d3496..854d87eeb84 100644 --- a/packages/grafana-data/src/field/standardFieldConfigEditorRegistry.ts +++ b/packages/grafana-data/src/field/standardFieldConfigEditorRegistry.ts @@ -1,6 +1,6 @@ -import { FieldConfigEditorRegistry, FieldPropertyEditorItem } from '../types/fieldOverrides'; import { Registry, RegistryItem } from '../utils/Registry'; import { ComponentType } from 'react'; +import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; export interface StandardEditorProps { value: TValue; @@ -11,6 +11,6 @@ export interface StandardEditorsRegistryItem exte editor: ComponentType>; settings?: TSettings; } -export const standardFieldConfigEditorRegistry: FieldConfigEditorRegistry = new Registry(); +export const standardFieldConfigEditorRegistry = new FieldConfigOptionsRegistry(); export const standardEditorsRegistry = new Registry>(); diff --git a/packages/grafana-data/src/panel/PanelPlugin.test.tsx b/packages/grafana-data/src/panel/PanelPlugin.test.tsx index 66ea67c2526..9cc3c6e3bfc 100644 --- a/packages/grafana-data/src/panel/PanelPlugin.test.tsx +++ b/packages/grafana-data/src/panel/PanelPlugin.test.tsx @@ -1,11 +1,23 @@ import React from 'react'; -import { identityOverrideProcessor, standardEditorsRegistry } from '../field'; -import { PanelPlugin, standardFieldConfigProperties } from './PanelPlugin'; +import { identityOverrideProcessor, standardEditorsRegistry, standardFieldConfigEditorRegistry } from '../field'; +import { PanelPlugin } from './PanelPlugin'; import { FieldConfigProperty } from '../types'; describe('PanelPlugin', () => { describe('declarative options', () => { beforeAll(() => { + standardFieldConfigEditorRegistry.setInit(() => { + return [ + { + id: 'min', + path: 'min', + }, + { + id: 'max', + path: 'max', + }, + ] as any; + }); standardEditorsRegistry.setInit(() => { return [ { @@ -14,26 +26,29 @@ describe('PanelPlugin', () => { ] as any; }); }); + test('field config UI API', () => { const panel = new PanelPlugin(() => { return
Panel
; }); - panel.setCustomFieldOptions(builder => { - builder.addCustomEditor({ - id: 'custom', - name: 'Custom', - description: 'Custom field config property description', - editor: () =>
Editor
, - override: () =>
Editor
, - process: identityOverrideProcessor, - settings: {}, - shouldApply: () => true, - }); + panel.useFieldConfig({ + useCustomConfig: builder => { + builder.addCustomEditor({ + id: 'custom', + path: 'custom', + name: 'Custom', + description: 'Custom field config property description', + editor: () =>
Editor
, + override: () =>
Editor
, + process: identityOverrideProcessor, + settings: {}, + shouldApply: () => true, + }); + }, }); - expect(panel.customFieldConfigs).toBeDefined(); - expect(panel.customFieldConfigs!.list()).toHaveLength(1); + expect(panel.fieldConfigRegistry.list()).toHaveLength(3); }); test('options UI API', () => { @@ -44,6 +59,7 @@ describe('PanelPlugin', () => { panel.setPanelOptions(builder => { builder.addCustomEditor({ id: 'option', + path: 'option', name: 'Option editor', description: 'Option editor description', editor: () =>
Editor
, @@ -66,18 +82,19 @@ describe('PanelPlugin', () => { panel.setPanelOptions(builder => { builder .addNumberInput({ - id: 'numericOption', + path: 'numericOption', name: 'Option editor', description: 'Option editor description', defaultValue: 10, }) .addNumberInput({ - id: 'numericOptionNoDefault', + path: 'numericOptionNoDefault', name: 'Option editor', description: 'Option editor description', }) .addCustomEditor({ id: 'customOption', + path: 'customOption', name: 'Option editor', description: 'Option editor description', editor: () =>
Editor
, @@ -101,7 +118,7 @@ describe('PanelPlugin', () => { panel.setPanelOptions(builder => { builder.addNumberInput({ - id: 'numericOption.nested', + path: 'numericOption.nested', name: 'Option editor', description: 'Option editor description', defaultValue: 10, @@ -122,30 +139,33 @@ describe('PanelPlugin', () => { return
Panel
; }); - panel.setCustomFieldOptions(builder => { - builder - .addNumberInput({ - id: 'numericOption', - name: 'Option editor', - description: 'Option editor description', - defaultValue: 10, - }) - .addNumberInput({ - id: 'numericOptionNoDefault', - name: 'Option editor', - description: 'Option editor description', - }) - .addCustomEditor({ - id: 'customOption', - name: 'Option editor', - description: 'Option editor description', - editor: () =>
Editor
, - override: () =>
Override editor
, - process: identityOverrideProcessor, - shouldApply: () => true, - settings: {}, - defaultValue: { value: 'Custom default value' }, - }); + panel.useFieldConfig({ + useCustomConfig: builder => { + builder + .addNumberInput({ + path: 'numericOption', + name: 'Option editor', + description: 'Option editor description', + defaultValue: 10, + }) + .addNumberInput({ + path: 'numericOptionNoDefault', + name: 'Option editor', + description: 'Option editor description', + }) + .addCustomEditor({ + id: 'customOption', + path: 'customOption', + name: 'Option editor', + description: 'Option editor description', + editor: () =>
Editor
, + override: () =>
Override editor
, + process: identityOverrideProcessor, + shouldApply: () => true, + settings: {}, + defaultValue: { value: 'Custom default value' }, + }); + }, }); const expectedDefaults = { @@ -161,13 +181,15 @@ describe('PanelPlugin', () => { return
Panel
; }); - panel.setCustomFieldOptions(builder => { - builder.addNumberInput({ - id: 'numericOption.nested', - name: 'Option editor', - description: 'Option editor description', - defaultValue: 10, - }); + panel.useFieldConfig({ + useCustomConfig: builder => { + builder.addNumberInput({ + path: 'numericOption.nested', + name: 'Option editor', + description: 'Option editor description', + defaultValue: 10, + }); + }, }); const expectedDefaults = { @@ -184,8 +206,8 @@ describe('PanelPlugin', () => { return
Panel
; }); - panel.useStandardFieldConfig(); - expect(panel.standardFieldConfigProperties).toEqual(Array.from(standardFieldConfigProperties.keys())); + panel.useFieldConfig(); + expect(panel.fieldConfigRegistry.list()).toHaveLength(2); }); test('selected standard config', () => { @@ -193,8 +215,10 @@ describe('PanelPlugin', () => { return
Panel
; }); - panel.useStandardFieldConfig([FieldConfigProperty.Min, FieldConfigProperty.Thresholds]); - expect(panel.standardFieldConfigProperties).toEqual(['min', 'thresholds']); + panel.useFieldConfig({ + standardOptions: [FieldConfigProperty.Min, FieldConfigProperty.Max], + }); + expect(panel.fieldConfigRegistry.list()).toHaveLength(2); }); describe('default values', () => { @@ -203,17 +227,21 @@ describe('PanelPlugin', () => { return
Panel
; }); - panel.useStandardFieldConfig([FieldConfigProperty.Color, FieldConfigProperty.Min], { - [FieldConfigProperty.Color]: '#ff00ff', - [FieldConfigProperty.Min]: 10, + panel.useFieldConfig({ + standardOptions: [FieldConfigProperty.Max, FieldConfigProperty.Min], + standardOptionsDefaults: { + [FieldConfigProperty.Max]: 20, + [FieldConfigProperty.Min]: 10, + }, }); - expect(panel.standardFieldConfigProperties).toEqual(['color', 'min']); + expect(panel.fieldConfigRegistry.list()).toHaveLength(2); expect(panel.fieldConfigDefaults).toEqual({ defaults: { min: 10, - color: '#ff00ff', + max: 20, + custom: {}, }, overrides: [], }); @@ -224,16 +252,20 @@ describe('PanelPlugin', () => { return
Panel
; }); - panel.useStandardFieldConfig([FieldConfigProperty.Color], { - [FieldConfigProperty.Color]: '#ff00ff', - [FieldConfigProperty.Min]: 10, + panel.useFieldConfig({ + standardOptions: [FieldConfigProperty.Max], + standardOptionsDefaults: { + [FieldConfigProperty.Max]: 20, + [FieldConfigProperty.Min]: 10, + }, }); - expect(panel.standardFieldConfigProperties).toEqual(['color']); + expect(panel.fieldConfigRegistry.list()).toHaveLength(1); expect(panel.fieldConfigDefaults).toEqual({ defaults: { - color: '#ff00ff', + max: 20, + custom: {}, }, overrides: [], }); diff --git a/packages/grafana-data/src/panel/PanelPlugin.ts b/packages/grafana-data/src/panel/PanelPlugin.ts index c382b932c04..7c517fc4c12 100644 --- a/packages/grafana-data/src/panel/PanelPlugin.ts +++ b/packages/grafana-data/src/panel/PanelPlugin.ts @@ -1,5 +1,4 @@ import { - FieldConfigEditorRegistry, FieldConfigSource, GrafanaPlugin, PanelEditorProps, @@ -9,55 +8,34 @@ import { PanelProps, PanelTypeChangedHandler, FieldConfigProperty, - ThresholdsMode, } from '../types'; import { FieldConfigEditorBuilder, PanelOptionsEditorBuilder } from '../utils/OptionsUIBuilders'; import { ComponentClass, ComponentType } from 'react'; import set from 'lodash/set'; import { deprecationWarning } from '../utils'; +import { FieldConfigOptionsRegistry, standardFieldConfigEditorRegistry } from '../field'; -export const allStandardFieldConfigProperties: FieldConfigProperty[] = [ - FieldConfigProperty.Min, - FieldConfigProperty.Max, - FieldConfigProperty.Title, - FieldConfigProperty.Unit, - FieldConfigProperty.Decimals, - FieldConfigProperty.NoValue, - FieldConfigProperty.Color, - FieldConfigProperty.Thresholds, - FieldConfigProperty.Mappings, - FieldConfigProperty.Links, -]; - -export const standardFieldConfigDefaults: Partial> = { - [FieldConfigProperty.Thresholds]: { - mode: ThresholdsMode.Absolute, - steps: [ - { value: -Infinity, color: 'green' }, - { value: 80, color: 'red' }, - ], - }, - [FieldConfigProperty.Mappings]: [], -}; - -export const standardFieldConfigProperties = new Map(allStandardFieldConfigProperties.map(p => [p, undefined])); +export interface SetFieldConfigOptionsArgs { + standardOptions?: FieldConfigProperty[]; + standardOptionsDefaults?: Partial>; + useCustomConfig?: (builder: FieldConfigEditorBuilder) => void; +} export class PanelPlugin extends GrafanaPlugin< PanelPluginMeta > { private _defaults?: TOptions; - private _standardFieldConfigProperties?: Map; - private _fieldConfigDefaults: FieldConfigSource = { defaults: {}, overrides: [], }; - private _customFieldConfigs?: FieldConfigEditorRegistry; - private customFieldConfigsUIBuilder = new FieldConfigEditorBuilder(); - private registerCustomFieldConfigs?: (builder: FieldConfigEditorBuilder) => void; + + private _fieldConfigRegistry?: FieldConfigOptionsRegistry; + private _initConfigRegistry = () => { + return new FieldConfigOptionsRegistry(); + }; private _optionEditors?: PanelOptionEditorsRegistry; - private optionsUIBuilder = new PanelOptionsEditorBuilder(); private registerOptionEditors?: (builder: PanelOptionsEditorBuilder) => void; panel: ComponentType>; @@ -94,39 +72,21 @@ export class PanelPlugin { - let customPropertiesDefaults = this._fieldConfigDefaults.defaults.custom; - - if (!customPropertiesDefaults) { - customPropertiesDefaults = {} as TFieldConfigOptions; - } - const editors = this.customFieldConfigs; + const configDefaults = this._fieldConfigDefaults.defaults; + configDefaults.custom = {} as TFieldConfigOptions; - if (editors && editors.list().length !== 0) { - for (const editor of editors.list()) { - set(customPropertiesDefaults, editor.id, editor.defaultValue); - } + for (const option of this.fieldConfigRegistry.list()) { + set(configDefaults, option.id, option.defaultValue); } return { defaults: { - ...(this._standardFieldConfigProperties ? Object.fromEntries(this._standardFieldConfigProperties) : {}), - custom: - Object.keys(customPropertiesDefaults).length > 0 - ? { - ...customPropertiesDefaults, - } - : undefined, - ...this._fieldConfigDefaults.defaults, + ...configDefaults, }, - // TODO: not sure yet what about overrides, if anything overrides: this._fieldConfigDefaults.overrides, }; } - get standardFieldConfigProperties() { - return this._standardFieldConfigProperties ? Array.from(this._standardFieldConfigProperties.keys()) : []; - } - /** * @deprecated setDefaults is deprecated in favor of setPanelOptions */ @@ -136,19 +96,19 @@ export class PanelPlugin(); + this.registerOptionEditors(builder); + this._optionEditors = builder.getRegistry(); } return this._optionEditors; @@ -188,47 +148,6 @@ export class PanelPlugin(ShapePanel) - * .setCustomFieldOptions(builder => { - * builder - * .addNumberInput({ - * id: 'shapeBorderWidth', - * name: 'Border width', - * description: 'Border width of the shape', - * settings: { - * min: 1, - * max: 5, - * }, - * }) - * .addSelect({ - * id: 'displayMode', - * name: 'Display mode', - * description: 'How the shape shout be rendered' - * settings: { - * options: [{value: 'fill', label: 'Fill' }, {value: 'transparent', label: 'Transparent }] - * }, - * }) - * }) - * ``` - * - * @public - **/ - setCustomFieldOptions(builder: (builder: FieldConfigEditorBuilder) => void) { - // builder is applied lazily when custom field configs are accessed - this.registerCustomFieldConfigs = builder; - return this; - } - /** * Enables panel options editor creation * @@ -277,44 +196,94 @@ export class PanelPlugin(ShapePanel) - * .useStandardFieldConfig(); + * .useFieldConfig(); * * // when plugin should only display specific standard options * // note, that options will be displayed in the order they are provided * export const plugin = new PanelPlugin(ShapePanel) - * .useStandardFieldConfig([FieldConfigProperty.Min, FieldConfigProperty.Max, FieldConfigProperty.Links]); + * .useFieldConfig({ + * standardOptions: [FieldConfigProperty.Min, FieldConfigProperty.Max] + * }); * * // when standard option's default value needs to be provided * export const plugin = new PanelPlugin(ShapePanel) - * .useStandardFieldConfig([FieldConfigProperty.Min, FieldConfigProperty.Max], { - * [FieldConfigProperty.Min]: 20, - * [FieldConfigProperty.Max]: 100 + * .useFieldConfig({ + * standardOptions: [FieldConfigProperty.Min, FieldConfigProperty.Max], + * standardOptionsDefaults: { + * [FieldConfigProperty.Min]: 20, + * [FieldConfigProperty.Max]: 100 + * } + * }); + * + * // when custom field config options needs to be provided + * export const plugin = new PanelPlugin(ShapePanel) + * .useFieldConfig({ + * useCustomConfig: builder => { + builder + * .addNumberInput({ + * id: 'shapeBorderWidth', + * name: 'Border width', + * description: 'Border width of the shape', + * settings: { + * min: 1, + * max: 5, + * }, + * }) + * .addSelect({ + * id: 'displayMode', + * name: 'Display mode', + * description: 'How the shape shout be rendered' + * settings: { + * options: [{value: 'fill', label: 'Fill' }, {value: 'transparent', label: 'Transparent }] + * }, + * }) + * }, * }); * * ``` * * @public */ - useStandardFieldConfig( - properties?: FieldConfigProperty[] | null, - customDefaults?: Partial> - ) { - if (!properties) { - this._standardFieldConfigProperties = standardFieldConfigProperties; - return this; - } else { - this._standardFieldConfigProperties = new Map(properties.map(p => [p, standardFieldConfigProperties.get(p)])); - } - - const defaults = customDefaults ?? standardFieldConfigDefaults; + useFieldConfig(config?: SetFieldConfigOptionsArgs) { + // builder is applied lazily when custom field configs are accessed + this._initConfigRegistry = () => { + const registry = new FieldConfigOptionsRegistry(); + + // Add custom options + if (config && config.useCustomConfig) { + const builder = new FieldConfigEditorBuilder(); + config.useCustomConfig(builder); + + for (const customProp of builder.getRegistry().list()) { + customProp.isCustom = true; + // need to do something to make the custom items not conflict with standard ones + // problem is id (registry index) is used as property path + // so sort of need a property path on the FieldPropertyEditorItem + customProp.id = 'custom.' + customProp.id; + registry.register(customProp); + } + } - if (defaults) { - Object.keys(defaults).map(k => { - if (properties.indexOf(k as FieldConfigProperty) > -1) { - this._standardFieldConfigProperties!.set(k as FieldConfigProperty, defaults[k as FieldConfigProperty]); + if (config && config.standardOptions) { + for (const standardOption of config.standardOptions) { + const standardEditor = standardFieldConfigEditorRegistry.get(standardOption); + registry.register({ + ...standardEditor, + defaultValue: + (config.standardOptionsDefaults && config.standardOptionsDefaults[standardOption]) || + standardEditor.defaultValue, + }); } - }); - } + } else { + for (const fieldConfigProp of standardFieldConfigEditorRegistry.list()) { + console.log(fieldConfigProp); + registry.register(fieldConfigProp); + } + } + + return registry; + }; + return this; } } diff --git a/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts b/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts index 87990e7ba73..55be700bcd2 100644 --- a/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts +++ b/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts @@ -6,7 +6,7 @@ import { NumberFieldConfigSettings, SelectFieldConfigSettings, StringFieldConfig * Option editor registry item */ export interface OptionsEditorItem extends RegistryItem { - id: (keyof TOptions & string) | string; + path: (keyof TOptions & string) | string; editor: ComponentType; settings?: TSettings; defaultValue?: TValue; diff --git a/packages/grafana-data/src/types/fieldOverrides.ts b/packages/grafana-data/src/types/fieldOverrides.ts index 55584003a0f..de2c9b7c1be 100644 --- a/packages/grafana-data/src/types/fieldOverrides.ts +++ b/packages/grafana-data/src/types/fieldOverrides.ts @@ -15,9 +15,9 @@ import { StandardEditorProps } from '../field'; import { OptionsEditorItem } from './OptionsUIRegistryBuilder'; export interface DynamicConfigValue { - prop: string; + id: string; value?: any; - custom?: boolean; + isCustom?: boolean; } export interface ConfigOverrideRule { @@ -55,7 +55,7 @@ export interface FieldOverrideEditorProps extends Omit { - id: (keyof TOptions & string) | string; + path: (keyof TOptions & string) | string; name: string; description: string; settings?: TSettings; @@ -68,6 +68,9 @@ export interface FieldPropertyEditorItem>; + /** true for plugin field config properties */ + isCustom?: boolean; + // Convert the override value to a well typed value process: (value: any, context: FieldOverrideContext, settings?: TSettings) => TValue | undefined | null; @@ -75,17 +78,14 @@ export interface FieldPropertyEditorItem boolean; } -export type FieldConfigEditorRegistry = Registry; - export interface ApplyFieldOverrideOptions { data?: DataFrame[]; - fieldOptions: FieldConfigSource; + fieldConfig: FieldConfigSource; replaceVariables: InterpolateFunction; theme: GrafanaTheme; timeZone?: TimeZone; autoMinMax?: boolean; - standard?: FieldConfigEditorRegistry; - custom?: FieldConfigEditorRegistry; + fieldConfigRegistry?: Registry; } export enum FieldConfigProperty { diff --git a/packages/grafana-data/src/types/panel.ts b/packages/grafana-data/src/types/panel.ts index c2d1c488c6f..0051e4c8a2b 100644 --- a/packages/grafana-data/src/types/panel.ts +++ b/packages/grafana-data/src/types/panel.ts @@ -119,7 +119,7 @@ export interface PanelOptionsEditorItem, TValue> {} export interface PanelOptionsEditorConfig { - id: (keyof TOptions & string) | string; + path: (keyof TOptions & string) | string; name: string; description: string; settings?: TSettings; diff --git a/packages/grafana-data/src/utils/OptionsUIBuilders.ts b/packages/grafana-data/src/utils/OptionsUIBuilders.ts index 726d90bae6e..ed4e25e8ff2 100644 --- a/packages/grafana-data/src/utils/OptionsUIBuilders.ts +++ b/packages/grafana-data/src/utils/OptionsUIBuilders.ts @@ -34,6 +34,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder addNumberInput(config: FieldConfigEditorConfig) { return this.addCustomEditor({ ...config, + id: config.path, override: standardEditorsRegistry.get('number').editor as any, editor: standardEditorsRegistry.get('number').editor as any, process: numberOverrideProcessor, @@ -45,6 +46,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder addTextInput(config: FieldConfigEditorConfig) { return this.addCustomEditor({ ...config, + id: config.path, override: standardEditorsRegistry.get('text').editor as any, editor: standardEditorsRegistry.get('text').editor as any, process: stringOverrideProcessor, @@ -58,6 +60,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder ) { return this.addCustomEditor({ ...config, + id: config.path, override: standardEditorsRegistry.get('select').editor as any, editor: standardEditorsRegistry.get('select').editor as any, process: selectOverrideProcessor, @@ -70,6 +73,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder addRadio(config: FieldConfigEditorConfig) { return this.addCustomEditor({ ...config, + id: config.path, override: standardEditorsRegistry.get('radio').editor as any, editor: standardEditorsRegistry.get('radio').editor as any, process: selectOverrideProcessor, @@ -82,6 +86,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder addBooleanSwitch(config: FieldConfigEditorConfig) { return this.addCustomEditor({ ...config, + id: config.path, editor: standardEditorsRegistry.get('boolean').editor as any, override: standardEditorsRegistry.get('boolean').editor as any, process: booleanOverrideProcessor, @@ -95,6 +100,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder ) { return this.addCustomEditor({ ...config, + id: config.path, editor: standardEditorsRegistry.get('color').editor as any, override: standardEditorsRegistry.get('color').editor as any, process: identityOverrideProcessor, @@ -108,6 +114,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder ) { return this.addCustomEditor({ ...config, + id: config.path, editor: standardEditorsRegistry.get('unit').editor as any, override: standardEditorsRegistry.get('unit').editor as any, process: unitOverrideProcessor, @@ -128,6 +135,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde addNumberInput(config: PanelOptionsEditorConfig) { return this.addCustomEditor({ ...config, + id: config.path, editor: standardEditorsRegistry.get('number').editor as any, }); } @@ -135,6 +143,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde addTextInput(config: PanelOptionsEditorConfig) { return this.addCustomEditor({ ...config, + id: config.path, editor: standardEditorsRegistry.get('text').editor as any, }); } @@ -144,6 +153,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde ) { return this.addCustomEditor({ ...config, + id: config.path, editor: standardEditorsRegistry.get('select').editor as any, }); } @@ -153,6 +163,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde ) { return this.addCustomEditor({ ...config, + id: config.path, editor: standardEditorsRegistry.get('radio').editor as any, }); } @@ -160,6 +171,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde addBooleanSwitch(config: PanelOptionsEditorConfig) { return this.addCustomEditor({ ...config, + id: config.path, editor: standardEditorsRegistry.get('boolean').editor as any, }); } @@ -169,6 +181,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde ): this { return this.addCustomEditor({ ...config, + id: config.path, editor: standardEditorsRegistry.get('color').editor as any, settings: config.settings || {}, }); @@ -179,6 +192,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde ): this { return this.addCustomEditor({ ...config, + id: config.path, editor: standardEditorsRegistry.get('unit').editor as any, }); } diff --git a/packages/grafana-data/src/utils/Registry.ts b/packages/grafana-data/src/utils/Registry.ts index 3152de6991f..16def86a15d 100644 --- a/packages/grafana-data/src/utils/Registry.ts +++ b/packages/grafana-data/src/utils/Registry.ts @@ -124,7 +124,7 @@ export class Registry { if (!this.initialized) { this.getIfExists('xxx'); // will trigger init } - return [...this.ordered]; // copy of everythign just in case + return this.ordered; // copy of everythign just in case } register(ext: T) { diff --git a/packages/grafana-data/src/utils/tests/mockStandardProperties.ts b/packages/grafana-data/src/utils/tests/mockStandardProperties.ts new file mode 100644 index 00000000000..50e2f8f9c54 --- /dev/null +++ b/packages/grafana-data/src/utils/tests/mockStandardProperties.ts @@ -0,0 +1,170 @@ +import { identityOverrideProcessor } from '../../field'; +import { ThresholdsMode } from '../../types'; + +export const mockStandardProperties = () => { + const title = { + id: 'title', + path: 'title', + name: 'Title', + description: "Field's title", + editor: () => null, + override: () => null, + process: identityOverrideProcessor, + settings: { + placeholder: 'none', + expandTemplateVars: true, + }, + shouldApply: () => true, + }; + + const unit = { + id: 'unit', + path: 'unit', + name: 'Unit', + description: 'Value units', + + editor: () => null, + override: () => null, + process: identityOverrideProcessor, + + settings: { + placeholder: 'none', + }, + + shouldApply: () => true, + }; + + const min = { + id: 'min', + path: 'min', + name: 'Min', + description: 'Minimum expected value', + + editor: () => null, + override: () => null, + process: identityOverrideProcessor, + + settings: { + placeholder: 'auto', + }, + shouldApply: () => true, + }; + + const max = { + id: 'max', + path: 'max', + name: 'Max', + description: 'Maximum expected value', + + editor: () => null, + override: () => null, + process: identityOverrideProcessor, + + settings: { + placeholder: 'auto', + }, + + shouldApply: () => true, + }; + + const decimals = { + id: 'decimals', + path: 'decimals', + name: 'Decimals', + description: 'Number of decimal to be shown for a value', + + editor: () => null, + override: () => null, + process: identityOverrideProcessor, + + settings: { + placeholder: 'auto', + min: 0, + max: 15, + integer: true, + }, + + shouldApply: () => true, + }; + + const thresholds = { + id: 'thresholds', + path: 'thresholds', + name: 'Thresholds', + description: 'Manage thresholds', + + editor: () => null, + override: () => null, + process: identityOverrideProcessor, + settings: {}, + defaultValue: { + mode: ThresholdsMode.Absolute, + steps: [ + { value: -Infinity, color: 'green' }, + { value: 80, color: 'red' }, + ], + }, + shouldApply: () => true, + }; + + const mappings = { + id: 'mappings', + path: 'mappings', + name: 'Value mappings', + description: 'Manage value mappings', + + editor: () => null, + override: () => null, + process: identityOverrideProcessor, + settings: {}, + defaultValue: [], + shouldApply: () => true, + }; + + const noValue = { + id: 'noValue', + path: 'noValue', + name: 'No Value', + description: 'What to show when there is no value', + + editor: () => null, + override: () => null, + process: identityOverrideProcessor, + + settings: { + placeholder: '-', + }, + // ??? any optionsUi with no value + shouldApply: () => true, + }; + + const links = { + id: 'links', + path: 'links', + name: 'DataLinks', + description: 'Manage date links', + editor: () => null, + override: () => null, + process: identityOverrideProcessor, + settings: { + placeholder: '-', + }, + shouldApply: () => true, + }; + + const color = { + id: 'color', + path: 'color', + name: 'Color', + description: 'Customise color', + editor: () => null, + override: () => null, + process: identityOverrideProcessor, + settings: { + placeholder: '-', + }, + shouldApply: () => true, + }; + + return [unit, min, max, decimals, title, noValue, thresholds, mappings, links, color]; +}; diff --git a/packages/grafana-ui/src/components/FieldConfigs/fieldOverrides.test.ts b/packages/grafana-ui/src/components/FieldConfigs/fieldOverrides.test.ts deleted file mode 100644 index bbd9bc690aa..00000000000 --- a/packages/grafana-ui/src/components/FieldConfigs/fieldOverrides.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { - FieldConfig, - FieldConfigSource, - InterpolateFunction, - GrafanaTheme, - FieldMatcherID, - MutableDataFrame, - DataFrame, - FieldType, - applyFieldOverrides, - toDataFrame, - standardFieldConfigEditorRegistry, - standardEditorsRegistry, -} from '@grafana/data'; - -import { getTheme } from '../../themes'; -import { getStandardFieldConfigs, getStandardOptionEditors } from '../../utils'; - -describe('FieldOverrides', () => { - beforeAll(() => { - standardEditorsRegistry.setInit(getStandardOptionEditors); - standardFieldConfigEditorRegistry.setInit(getStandardFieldConfigs); - }); - - const f0 = new MutableDataFrame(); - f0.add({ title: 'AAA', value: 100, value2: 1234 }, true); - f0.add({ title: 'BBB', value: -20 }, true); - f0.add({ title: 'CCC', value: 200, value2: 1000 }, true); - expect(f0.length).toEqual(3); - - // Hardcode the max value - f0.fields[1].config.max = 0; - f0.fields[1].config.decimals = 6; - - const src: FieldConfigSource = { - defaults: { - unit: 'xyz', - decimals: 2, - }, - overrides: [ - { - matcher: { id: FieldMatcherID.numeric }, - properties: [ - { prop: 'decimals', value: 1 }, // Numeric - { prop: 'title', value: 'Kittens' }, // Text - ], - }, - ], - }; - - it('will merge FieldConfig with default values', () => { - const field: FieldConfig = { - min: 0, - max: 100, - }; - const f1 = { - unit: 'ms', - dateFormat: '', // should be ignored - max: parseFloat('NOPE'), // should be ignored - min: null, // should alo be ignored! - }; - - const f: DataFrame = toDataFrame({ - fields: [{ type: FieldType.number, name: 'x', config: field, values: [] }], - }); - const processed = applyFieldOverrides({ - data: [f], - standard: standardFieldConfigEditorRegistry, - fieldOptions: { - defaults: f1 as FieldConfig, - overrides: [], - }, - replaceVariables: v => v, - theme: getTheme(), - })[0]; - const out = processed.fields[0].config; - - expect(out.min).toEqual(0); - expect(out.max).toEqual(100); - expect(out.unit).toEqual('ms'); - }); - - it('will apply field overrides', () => { - const data = applyFieldOverrides({ - data: [f0], // the frame - fieldOptions: src as FieldConfigSource, // defaults + overrides - replaceVariables: (undefined as any) as InterpolateFunction, - theme: (undefined as any) as GrafanaTheme, - })[0]; - const valueColumn = data.fields[1]; - const config = valueColumn.config; - - // Keep max from the original setting - expect(config.max).toEqual(0); - - // Don't Automatically pick the min value - expect(config.min).toEqual(undefined); - - // The default value applied - expect(config.unit).toEqual('xyz'); - - // The default value applied - expect(config.title).toEqual('Kittens'); - - // The override applied - expect(config.decimals).toEqual(1); - }); - - it('will apply set min/max when asked', () => { - const data = applyFieldOverrides({ - data: [f0], // the frame - fieldOptions: src as FieldConfigSource, // defaults + overrides - replaceVariables: (undefined as any) as InterpolateFunction, - theme: (undefined as any) as GrafanaTheme, - autoMinMax: true, - })[0]; - const valueColumn = data.fields[1]; - const config = valueColumn.config; - - // Keep max from the original setting - expect(config.max).toEqual(0); - - // Don't Automatically pick the min value - expect(config.min).toEqual(-20); - }); -}); diff --git a/packages/grafana-ui/src/components/Table/Table.story.tsx b/packages/grafana-ui/src/components/Table/Table.story.tsx index ba133ef82ae..8c7a0ff1d50 100644 --- a/packages/grafana-ui/src/components/Table/Table.story.tsx +++ b/packages/grafana-ui/src/components/Table/Table.story.tsx @@ -77,7 +77,7 @@ function buildData(theme: GrafanaTheme, overrides: ConfigOverrideRule[]): DataFr return applyFieldOverrides({ data: [data], - fieldOptions: { + fieldConfig: { overrides, defaults: {}, }, @@ -105,10 +105,10 @@ export const BarGaugeCell = () => { { matcher: { id: FieldMatcherID.byName, options: 'Progress' }, properties: [ - { prop: 'width', value: '200', custom: true }, - { prop: 'displayMode', value: 'gradient-gauge', custom: true }, - { prop: 'min', value: '0' }, - { prop: 'max', value: '100' }, + { id: 'width', value: '200', isCustom: true }, + { id: 'displayMode', value: 'gradient-gauge', isCustom: true }, + { id: 'min', value: '0' }, + { id: 'max', value: '100' }, ], }, ]); @@ -141,11 +141,11 @@ export const ColoredCells = () => { { matcher: { id: FieldMatcherID.byName, options: 'Progress' }, properties: [ - { prop: 'width', value: '80', custom: true }, - { prop: 'displayMode', value: 'color-background', custom: true }, - { prop: 'min', value: '0' }, - { prop: 'max', value: '100' }, - { prop: 'thresholds', value: defaultThresholds }, + { id: 'width', value: '80', isCustom: true }, + { id: 'displayMode', value: 'color-background', isCustom: true }, + { id: 'min', value: '0' }, + { id: 'max', value: '100' }, + { id: 'thresholds', value: defaultThresholds }, ], }, ]); diff --git a/packages/grafana-ui/src/utils/standardEditors.tsx b/packages/grafana-ui/src/utils/standardEditors.tsx index 67c2fe86236..3fabc7fd04b 100644 --- a/packages/grafana-ui/src/utils/standardEditors.tsx +++ b/packages/grafana-ui/src/utils/standardEditors.tsx @@ -17,6 +17,7 @@ import { ValueMapping, ValueMappingFieldConfigSettings, valueMappingsOverrideProcessor, + ThresholdsMode, } from '@grafana/data'; import { NumberValueEditor, Forms, StringValueEditor, Select } from '../components'; import { ValueMappingsValueEditor } from '../components/OptionsUI/mappings'; @@ -32,6 +33,7 @@ import { StatsPickerEditor } from '../components/OptionsUI/stats'; export const getStandardFieldConfigs = () => { const title: FieldPropertyEditorItem = { id: 'title', + path: 'title', name: 'Title', description: "Field's title", editor: standardEditorsRegistry.get('text').editor as any, @@ -46,6 +48,7 @@ export const getStandardFieldConfigs = () => { const unit: FieldPropertyEditorItem = { id: 'unit', + path: 'unit', name: 'Unit', description: 'Value units', @@ -62,6 +65,7 @@ export const getStandardFieldConfigs = () => { const min: FieldPropertyEditorItem = { id: 'min', + path: 'min', name: 'Min', description: 'Minimum expected value', @@ -77,6 +81,7 @@ export const getStandardFieldConfigs = () => { const max: FieldPropertyEditorItem = { id: 'max', + path: 'max', name: 'Max', description: 'Maximum expected value', @@ -93,6 +98,7 @@ export const getStandardFieldConfigs = () => { const decimals: FieldPropertyEditorItem = { id: 'decimals', + path: 'decimals', name: 'Decimals', description: 'Number of decimal to be shown for a value', @@ -112,37 +118,41 @@ export const getStandardFieldConfigs = () => { const thresholds: FieldPropertyEditorItem = { id: 'thresholds', + path: 'thresholds', name: 'Thresholds', description: 'Manage thresholds', editor: standardEditorsRegistry.get('thresholds').editor as any, override: standardEditorsRegistry.get('thresholds').editor as any, process: thresholdsOverrideProcessor, - - settings: { - // ?? + settings: {}, + defaultValue: { + mode: ThresholdsMode.Absolute, + steps: [ + { value: -Infinity, color: 'green' }, + { value: 80, color: 'red' }, + ], }, - shouldApply: field => field.type === FieldType.number, }; const mappings: FieldPropertyEditorItem = { id: 'mappings', + path: 'mappings', name: 'Value mappings', description: 'Manage value mappings', editor: standardEditorsRegistry.get('mappings').editor as any, override: standardEditorsRegistry.get('mappings').editor as any, process: valueMappingsOverrideProcessor, - settings: { - // ?? - }, - + settings: {}, + defaultValue: [], shouldApply: field => field.type === FieldType.number, }; const noValue: FieldPropertyEditorItem = { id: 'noValue', + path: 'noValue', name: 'No Value', description: 'What to show when there is no value', @@ -159,6 +169,7 @@ export const getStandardFieldConfigs = () => { const links: FieldPropertyEditorItem = { id: 'links', + path: 'links', name: 'DataLinks', description: 'Manage date links', editor: standardEditorsRegistry.get('links').editor as any, @@ -172,6 +183,7 @@ export const getStandardFieldConfigs = () => { const color: FieldPropertyEditorItem = { id: 'color', + path: 'color', name: 'Color', description: 'Customise color', editor: standardEditorsRegistry.get('color').editor as any, diff --git a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx index 82c0ca5671d..496f4416d0d 100644 --- a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx +++ b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx @@ -181,7 +181,7 @@ export class PanelInspector extends PureComponent { const processed = applyFieldOverrides({ data, theme: config.theme, - fieldOptions: { defaults: {}, overrides: [] }, + fieldConfig: { defaults: {}, overrides: [] }, replaceVariables: (value: string) => { return value; }, diff --git a/public/app/features/dashboard/components/PanelEditor/DynamicConfigValueEditor.tsx b/public/app/features/dashboard/components/PanelEditor/DynamicConfigValueEditor.tsx index 9e055348c27..90749d48530 100644 --- a/public/app/features/dashboard/components/PanelEditor/DynamicConfigValueEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/DynamicConfigValueEditor.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { DynamicConfigValue, FieldConfigEditorRegistry, FieldOverrideContext, GrafanaTheme } from '@grafana/data'; +import { DynamicConfigValue, FieldConfigOptionsRegistry, FieldOverrideContext, GrafanaTheme } from '@grafana/data'; import { FieldConfigItemHeaderTitle, selectThemeVariant, stylesFactory, useTheme } from '@grafana/ui'; import { css } from 'emotion'; interface DynamicConfigValueEditorProps { property: DynamicConfigValue; - editorsRegistry: FieldConfigEditorRegistry; + registry: FieldConfigOptionsRegistry; onChange: (value: DynamicConfigValue) => void; context: FieldOverrideContext; onRemove: () => void; @@ -14,13 +14,13 @@ interface DynamicConfigValueEditorProps { export const DynamicConfigValueEditor: React.FC = ({ property, context, - editorsRegistry, + registry, onChange, onRemove, }) => { const theme = useTheme(); const styles = getStyles(theme); - const item = editorsRegistry?.getIfExists(property.prop); + const item = registry?.getIfExists(property.id); if (!item) { return null; diff --git a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx index 143a9bde6d0..194a9219c4d 100644 --- a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx @@ -5,10 +5,8 @@ import { DataFrame, FieldPropertyEditorItem, VariableSuggestionsScope, - standardFieldConfigEditorRegistry, PanelPlugin, SelectableValue, - FieldConfigProperty, } from '@grafana/data'; import { Forms, fieldMatchersUI, ValuePicker, useTheme } from '@grafana/ui'; import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_srv'; @@ -18,7 +16,6 @@ import { css } from 'emotion'; interface Props { plugin: PanelPlugin; config: FieldConfigSource; - include?: FieldConfigProperty[]; // Ordered list of which fields should be shown/included onChange: (config: FieldConfigSource) => void; /* Helpful for IntelliSense */ data: DataFrame[]; @@ -62,33 +59,12 @@ export const OverrideFieldConfigEditor: React.FC = props => { const renderOverrides = () => { const { config, data, plugin } = props; - const { customFieldConfigs } = plugin; + const { fieldConfigRegistry } = plugin; if (config.overrides.length === 0) { return null; } - let configPropertiesOptions = plugin.standardFieldConfigProperties.map(i => { - const editor = standardFieldConfigEditorRegistry.get(i); - return { - label: editor.name, - value: editor.id, - description: editor.description, - custom: false, - }; - }); - - if (customFieldConfigs) { - configPropertiesOptions = configPropertiesOptions.concat( - customFieldConfigs.list().map(i => ({ - label: i.name, - value: i.id, - description: i.description, - custom: true, - })) - ); - } - return (
{config.overrides.map((o, i) => { @@ -100,8 +76,7 @@ export const OverrideFieldConfigEditor: React.FC = props => { override={o} onChange={value => onOverrideChange(i, value)} onRemove={() => onOverrideRemove(i)} - configPropertiesOptions={configPropertiesOptions} - customPropertiesRegistry={customFieldConfigs} + registry={fieldConfigRegistry} /> ); })} @@ -135,7 +110,7 @@ export const OverrideFieldConfigEditor: React.FC = props => { ); }; -export const DefaultFieldConfigEditor: React.FC = ({ include, data, onChange, config, plugin }) => { +export const DefaultFieldConfigEditor: React.FC = ({ data, onChange, config, plugin }) => { const setDefaultValue = useCallback( (name: string, value: any, custom: boolean) => { const defaults = { ...config.defaults }; @@ -167,16 +142,20 @@ export const DefaultFieldConfigEditor: React.FC = ({ include, data, onCha ); const renderEditor = useCallback( - (item: FieldPropertyEditorItem, custom: boolean) => { + (item: FieldPropertyEditorItem) => { const defaults = config.defaults; - const value = custom ? (defaults.custom ? defaults.custom[item.id] : undefined) : (defaults as any)[item.id]; + const value = item.isCustom + ? defaults.custom + ? defaults.custom[item.path] + : undefined + : (defaults as any)[item.path]; return ( - + setDefaultValue(item.id, v, custom)} + onChange={v => setDefaultValue(item.path, v, item.isCustom)} context={{ data, getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(data, scope), @@ -188,28 +167,6 @@ export const DefaultFieldConfigEditor: React.FC = ({ include, data, onCha [config] ); - const renderStandardConfigs = useCallback(() => { - if (include && include.length === 0) { - return null; - } - if (include) { - return <>{include.map(f => renderEditor(standardFieldConfigEditorRegistry.get(f), false))}; - } - return <>{standardFieldConfigEditorRegistry.list().map(f => renderEditor(f, false))}; - }, [plugin, config]); - - const renderCustomConfigs = useCallback(() => { - if (!plugin.customFieldConfigs) { - return null; - } - - return plugin.customFieldConfigs.list().map(f => renderEditor(f, true)); - }, [plugin, config]); - - return ( - <> - {plugin.customFieldConfigs && renderCustomConfigs()} - {renderStandardConfigs()} - - ); + // render all field configs + return <>{plugin.fieldConfigRegistry.list().map(renderEditor)}; }; diff --git a/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx b/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx index b22af5991d3..f445476a87d 100644 --- a/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx +++ b/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx @@ -60,7 +60,6 @@ export const OptionsPaneContent: React.FC<{ plugin={plugin} onChange={onFieldConfigsChange} data={data.series} - include={plugin.standardFieldConfigProperties} /> ); diff --git a/public/app/features/dashboard/components/PanelEditor/OverrideEditor.tsx b/public/app/features/dashboard/components/PanelEditor/OverrideEditor.tsx index f13d389cf6f..b34901f4750 100644 --- a/public/app/features/dashboard/components/PanelEditor/OverrideEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/OverrideEditor.tsx @@ -3,10 +3,8 @@ import { ConfigOverrideRule, DataFrame, DynamicConfigValue, - FieldConfigEditorRegistry, - standardFieldConfigEditorRegistry, + FieldConfigOptionsRegistry, VariableSuggestionsScope, - SelectableValue, GrafanaTheme, } from '@grafana/data'; import { fieldMatchersUI, stylesFactory, useTheme, ValuePicker, selectThemeVariant } from '@grafana/ui'; @@ -21,18 +19,10 @@ interface OverrideEditorProps { override: ConfigOverrideRule; onChange: (config: ConfigOverrideRule) => void; onRemove: () => void; - customPropertiesRegistry?: FieldConfigEditorRegistry; - configPropertiesOptions: Array>; + registry: FieldConfigOptionsRegistry; } -export const OverrideEditor: React.FC = ({ - data, - override, - onChange, - onRemove, - customPropertiesRegistry, - configPropertiesOptions, -}) => { +export const OverrideEditor: React.FC = ({ data, override, onChange, onRemove, registry }) => { const theme = useTheme(); const onMatcherConfigChange = useCallback( (matcherConfig: any) => { @@ -59,10 +49,10 @@ export const OverrideEditor: React.FC = ({ ); const onDynamicConfigValueAdd = useCallback( - (prop: string, custom?: boolean) => { + (id: string, custom?: boolean) => { const propertyConfig: DynamicConfigValue = { - prop, - custom, + id, + isCustom: custom, }; if (override.properties) { override.properties.push(propertyConfig); @@ -74,6 +64,15 @@ export const OverrideEditor: React.FC = ({ [override, onChange] ); + let configPropertiesOptions = registry.list().map(item => { + return { + label: item.name, + value: item.id, + description: item.description, + custom: item.isCustom, + }; + }); + const matcherUi = fieldMatchersUI.get(override.matcher.id); const styles = getStyles(theme); return ( @@ -90,20 +89,19 @@ export const OverrideEditor: React.FC = ({
{override.properties.map((p, j) => { - const reg = p.custom ? customPropertiesRegistry : standardFieldConfigEditorRegistry; - const item = reg?.getIfExists(p.prop); + const item = registry.getIfExists(p.id); if (!item) { - return
Unknown property: {p.prop}
; + return
Unknown property: {p.id}
; } return ( -
+
onDynamicConfigValueChange(j, value)} onRemove={() => onDynamicConfigValueRemove(j)} property={p} - editorsRegistry={reg} + registry={registry} context={{ data, getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(data, scope), diff --git a/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx b/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx index 8429c5770c9..8344ce599f3 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx @@ -22,7 +22,7 @@ export const PanelOptionsEditor: React.FC> = ({ plu {optionEditors.list().map(e => { return ( - onOptionChange(e.id, value)} item={e} /> + onOptionChange(e.path, value)} item={e} /> ); })} diff --git a/public/app/features/dashboard/state/PanelModel.test.ts b/public/app/features/dashboard/state/PanelModel.test.ts index aac9ae7af06..258c30556b0 100644 --- a/public/app/features/dashboard/state/PanelModel.test.ts +++ b/public/app/features/dashboard/state/PanelModel.test.ts @@ -1,10 +1,46 @@ import { PanelModel } from './PanelModel'; import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks'; -import { PanelProps, FieldConfigProperty } from '@grafana/data'; +import { + FieldConfigProperty, + identityOverrideProcessor, + PanelProps, + standardFieldConfigEditorRegistry, +} from '@grafana/data'; import { ComponentClass } from 'react'; class TablePanelCtrl {} +export const mockStandardProperties = () => { + const unit = { + id: 'unit', + path: 'unit', + name: 'Unit', + description: 'Value units', + // @ts-ignore + editor: () => null, + // @ts-ignore + override: () => null, + process: identityOverrideProcessor, + shouldApply: () => true, + }; + + const decimals = { + id: 'decimals', + path: 'decimals', + name: 'Decimals', + description: 'Number of decimal to be shown for a value', + // @ts-ignore + editor: () => null, + // @ts-ignore + override: () => null, + process: identityOverrideProcessor, + shouldApply: () => true, + }; + + return [unit, decimals]; +}; +standardFieldConfigEditorRegistry.setInit(() => mockStandardProperties()); + describe('PanelModel', () => { describe('when creating new panel model', () => { let model: any; @@ -79,9 +115,16 @@ describe('PanelModel', () => { TablePanelCtrl // angular ); panelPlugin.setDefaults(defaultOptionsMock); - panelPlugin.useStandardFieldConfig([FieldConfigProperty.Unit, FieldConfigProperty.Decimals], { - [FieldConfigProperty.Unit]: 'flop', - [FieldConfigProperty.Decimals]: 2, + /* panelPlugin.useStandardFieldConfig([FieldConfigOptionId.Unit, FieldConfigOptionId.Decimals], { + [FieldConfigOptionId.Unit]: 'flop', + [FieldConfigOptionId.Decimals]: 2, + }); */ + panelPlugin.useFieldConfig({ + standardOptions: [FieldConfigProperty.Unit, FieldConfigProperty.Decimals], + standardOptionsDefaults: { + [FieldConfigProperty.Unit]: 'flop', + [FieldConfigProperty.Decimals]: 2, + }, }); model.pluginLoaded(panelPlugin); }); @@ -100,9 +143,9 @@ describe('PanelModel', () => { it('should apply field config defaults', () => { // default unit is overriden by model - expect(model.getFieldOverrideOptions().fieldOptions.defaults.unit).toBe('mpg'); + expect(model.getFieldOverrideOptions().fieldConfig.defaults.unit).toBe('mpg'); // default decimals are aplied - expect(model.getFieldOverrideOptions().fieldOptions.defaults.decimals).toBe(2); + expect(model.getFieldOverrideOptions().fieldConfig.defaults.decimals).toBe(2); }); it('should set model props on instance', () => { diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index 5c2ad580604..f6dff94460b 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -415,9 +415,9 @@ export class PanelModel implements DataConfigSource { } return { - fieldOptions: this.fieldConfig, + fieldConfig: this.fieldConfig, replaceVariables: this.replaceVariables, - custom: this.plugin.customFieldConfigs, + fieldConfigRegistry: this.plugin.fieldConfigRegistry, theme: config.theme, }; } diff --git a/public/app/features/dashboard/state/PanelQueryRunner.test.ts b/public/app/features/dashboard/state/PanelQueryRunner.test.ts index 2d44a648130..895d51cd223 100644 --- a/public/app/features/dashboard/state/PanelQueryRunner.test.ts +++ b/public/app/features/dashboard/state/PanelQueryRunner.test.ts @@ -210,7 +210,7 @@ describe('PanelQueryRunner', () => { }, { getFieldOverrideOptions: () => ({ - fieldOptions: { + fieldConfig: { defaults: { unit: 'm/s', }, diff --git a/public/app/features/panel/panellinks/fieldDisplayValuesProxy.test.ts b/public/app/features/panel/panellinks/fieldDisplayValuesProxy.test.ts index 1187350cd94..f69de4cd36f 100644 --- a/public/app/features/panel/panellinks/fieldDisplayValuesProxy.test.ts +++ b/public/app/features/panel/panellinks/fieldDisplayValuesProxy.test.ts @@ -21,7 +21,7 @@ describe('getFieldDisplayValuesProxy', () => { ], }), ], - fieldOptions: { + fieldConfig: { defaults: {}, overrides: [], }, diff --git a/public/app/features/panel/panellinks/linkSuppliers.test.ts b/public/app/features/panel/panellinks/linkSuppliers.test.ts index dea4ef5e5dc..ee10e211e8e 100644 --- a/public/app/features/panel/panellinks/linkSuppliers.test.ts +++ b/public/app/features/panel/panellinks/linkSuppliers.test.ts @@ -129,7 +129,7 @@ describe('getLinksFromLogsField', () => { ], }), ], - fieldOptions: { + fieldConfig: { defaults: {}, overrides: [], }, diff --git a/public/app/plugins/panel/bargauge/module.tsx b/public/app/plugins/panel/bargauge/module.tsx index 6b44bfd0a3a..0bea792ad71 100644 --- a/public/app/plugins/panel/bargauge/module.tsx +++ b/public/app/plugins/panel/bargauge/module.tsx @@ -9,12 +9,13 @@ import { barGaugePanelMigrationHandler } from './BarGaugeMigrations'; export const plugin = new PanelPlugin(BarGaugePanel) .setDefaults(defaults) .setEditor(BarGaugePanelEditor) + .useFieldConfig() .setPanelOptions(builder => { addStandardDataReduceOptions(builder); builder .addRadio({ - id: 'displayMode', + path: 'displayMode', name: 'Display mode', description: 'Controls the bar style', settings: { @@ -26,11 +27,10 @@ export const plugin = new PanelPlugin(BarGaugePanel) }, }) .addBooleanSwitch({ - id: 'showUnfilled', + path: 'showUnfilled', name: 'Show unfilled area', description: 'When enabled renders the unfilled region as gray', }); }) .setPanelChangeHandler(sharedSingleStatPanelChangedHandler) - .setMigrationHandler(barGaugePanelMigrationHandler) - .useStandardFieldConfig(); + .setMigrationHandler(barGaugePanelMigrationHandler); diff --git a/public/app/plugins/panel/gauge/module.tsx b/public/app/plugins/panel/gauge/module.tsx index 16ad4b049c9..e2292bdac4c 100644 --- a/public/app/plugins/panel/gauge/module.tsx +++ b/public/app/plugins/panel/gauge/module.tsx @@ -8,21 +8,20 @@ import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMig export const plugin = new PanelPlugin(GaugePanel) .setDefaults(defaults) .setEditor(GaugePanelEditor) + .useFieldConfig() .setPanelOptions(builder => { addStandardDataReduceOptions(builder); - builder .addBooleanSwitch({ - id: 'showThresholdLabels', + path: 'showThresholdLabels', name: 'Show threshold Labels', description: 'Render the threshold values around the gauge bar', }) .addBooleanSwitch({ - id: 'showThresholdMarkers', + path: 'showThresholdMarkers', name: 'Show threshold markers', description: 'Renders the thresholds as an outer bar', }); }) .setPanelChangeHandler(gaugePanelChangedHandler) - .setMigrationHandler(gaugePanelMigrationHandler) - .useStandardFieldConfig(); + .setMigrationHandler(gaugePanelMigrationHandler); diff --git a/public/app/plugins/panel/piechart/module.tsx b/public/app/plugins/panel/piechart/module.tsx index d3bca4a506e..c5dedf9ac67 100644 --- a/public/app/plugins/panel/piechart/module.tsx +++ b/public/app/plugins/panel/piechart/module.tsx @@ -1,11 +1,8 @@ -import { PanelPlugin, FieldConfigProperty } from '@grafana/data'; +import { PanelPlugin } from '@grafana/data'; import { PieChartPanelEditor } from './PieChartPanelEditor'; import { PieChartPanel } from './PieChartPanel'; import { PieChartOptions, defaults } from './types'; export const plugin = new PanelPlugin(PieChartPanel) .setDefaults(defaults) - .useStandardFieldConfig(null, { - [FieldConfigProperty.Unit]: 'short', - }) .setEditor(PieChartPanelEditor); diff --git a/public/app/plugins/panel/stat/module.tsx b/public/app/plugins/panel/stat/module.tsx index c872adfa174..8c18855525b 100644 --- a/public/app/plugins/panel/stat/module.tsx +++ b/public/app/plugins/panel/stat/module.tsx @@ -7,12 +7,13 @@ import { StatPanelEditor } from './StatPanelEditor'; export const plugin = new PanelPlugin(StatPanel) .setDefaults(defaults) .setEditor(StatPanelEditor) + .useFieldConfig() .setPanelOptions(builder => { addStandardDataReduceOptions(builder); builder .addRadio({ - id: 'colorMode', + path: 'colorMode', name: 'Color mode', description: 'Color either the value or the background', settings: { @@ -23,7 +24,7 @@ export const plugin = new PanelPlugin(StatPanel) }, }) .addRadio({ - id: 'graphMode', + path: 'graphMode', name: 'Graph mode', description: 'Stat panel graph / sparkline mode', settings: { @@ -34,7 +35,7 @@ export const plugin = new PanelPlugin(StatPanel) }, }) .addRadio({ - id: 'justifyMode', + path: 'justifyMode', name: 'Justify mode', description: 'Value & title posititioning', settings: { @@ -47,5 +48,4 @@ export const plugin = new PanelPlugin(StatPanel) }) .setNoPadding() .setPanelChangeHandler(sharedSingleStatPanelChangedHandler) - .setMigrationHandler(sharedSingleStatMigrationHandler) - .useStandardFieldConfig(); + .setMigrationHandler(sharedSingleStatMigrationHandler); diff --git a/public/app/plugins/panel/stat/types.ts b/public/app/plugins/panel/stat/types.ts index 8426d9c138c..7500f66f8f3 100644 --- a/public/app/plugins/panel/stat/types.ts +++ b/public/app/plugins/panel/stat/types.ts @@ -1,13 +1,5 @@ import { SingleStatBaseOptions, BigValueColorMode, BigValueGraphMode, BigValueJustifyMode } from '@grafana/ui'; -import { - VizOrientation, - ReducerID, - ReduceDataOptions, - SelectableValue, - ThresholdsMode, - standardEditorsRegistry, - FieldConfigProperty, -} from '@grafana/data'; +import { VizOrientation, ReducerID, ReduceDataOptions, SelectableValue, standardEditorsRegistry } from '@grafana/data'; import { PanelOptionsEditorBuilder } from '@grafana/data/src/utils/OptionsUIBuilders'; // Structure copied from angular @@ -37,20 +29,9 @@ export const commonValueOptionDefaults: ReduceDataOptions = { calcs: [ReducerID.mean], }; -export const standardFieldConfigDefaults: Partial> = { - [FieldConfigProperty.Thresholds]: { - mode: ThresholdsMode.Absolute, - steps: [ - { value: -Infinity, color: 'green' }, - { value: 80, color: 'red' }, - ], - }, - [FieldConfigProperty.Mappings]: [], -}; - export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder) { builder.addRadio({ - id: 'reduceOptions.values', + path: 'reduceOptions.values', name: 'Show', description: 'Calculate a single value per colum or series or show each row', settings: { @@ -62,7 +43,7 @@ export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder< }); builder.addNumberInput({ - id: 'reduceOptions.limit', + path: 'reduceOptions.limit', name: 'Limit', description: 'Max number of rows to display', settings: { @@ -75,13 +56,14 @@ export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder< builder.addCustomEditor({ id: 'reduceOptions.calcs', + path: 'reduceOptions.calcs', name: 'Value', description: 'Choose a reducer function / calculation', editor: standardEditorsRegistry.get('stats-picker').editor as any, }); builder.addRadio({ - id: 'orientation', + path: 'orientation', name: 'Orientation', description: 'Stacking direction in case of multiple series or fields', settings: { diff --git a/public/app/plugins/panel/table2/module.tsx b/public/app/plugins/panel/table2/module.tsx index 6e21fbaac8a..1450105b530 100644 --- a/public/app/plugins/panel/table2/module.tsx +++ b/public/app/plugins/panel/table2/module.tsx @@ -4,35 +4,37 @@ import { CustomFieldConfig, defaults, Options } from './types'; export const plugin = new PanelPlugin(TablePanel) .setDefaults(defaults) - .setCustomFieldOptions(builder => { - builder - .addNumberInput({ - id: 'width', - name: 'Column width', - description: 'column width (for table)', - settings: { - placeholder: 'auto', - min: 20, - max: 300, - }, - }) - .addSelect({ - id: 'displayMode', - name: 'Cell display mode', - description: 'Color value, background, show as gauge, etc', - settings: { - options: [ - { value: 'auto', label: 'Auto' }, - { value: 'color-background', label: 'Color background' }, - { value: 'gradient-gauge', label: 'Gradient gauge' }, - { value: 'lcd-gauge', label: 'LCD gauge' }, - ], - }, - }); + .useFieldConfig({ + useCustomConfig: builder => { + builder + .addNumberInput({ + path: 'width', + name: 'Column width', + description: 'column width (for table)', + settings: { + placeholder: 'auto', + min: 20, + max: 300, + }, + }) + .addSelect({ + path: 'displayMode', + name: 'Cell display mode', + description: 'Color value, background, show as gauge, etc', + settings: { + options: [ + { value: 'auto', label: 'Auto' }, + { value: 'color-background', label: 'Color background' }, + { value: 'gradient-gauge', label: 'Gradient gauge' }, + { value: 'lcd-gauge', label: 'LCD gauge' }, + ], + }, + }); + }, }) .setPanelOptions(builder => { builder.addBooleanSwitch({ - id: 'showHeader', + path: 'showHeader', name: 'Show header', description: "To display table's header or not to display", });