Commit dcf6bbc1 by Dominik Prokop Committed by GitHub

NewPanelEditor: Options/FieldConfig API for defaults and common options selection (#23214)

* Add "some" typesafety to panel options/field config APIs

* Allow selected common field config properties config, allow option defaults config via fluent API

* Update packages/grafana-data/src/panel/PanelPlugin.ts

Co-Authored-By: Ryan McKinley <ryantxu@gmail.com>

* Add defaults support for custom field config

* Enable defaults setting for standard and custom field configs

* Remove setFieldConfigDefaults from PanelPlugin API and replace it with useStandardFieldConfig

* Update API for standard field config defaults

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
parent a92bcb78
...@@ -67,6 +67,9 @@ export const stringOverrideProcessor = ( ...@@ -67,6 +67,9 @@ export const stringOverrideProcessor = (
context: FieldOverrideContext, context: FieldOverrideContext,
settings?: StringFieldConfigSettings settings?: StringFieldConfigSettings
) => { ) => {
if (value === null || value === undefined) {
return value;
}
if (settings && settings.expandTemplateVars && context.replaceVariables) { if (settings && settings.expandTemplateVars && context.replaceVariables) {
return context.replaceVariables(value, context.field!.config.scopedVars); return context.replaceVariables(value, context.field!.config.scopedVars);
} }
......
...@@ -12,4 +12,4 @@ export * from './datetime'; ...@@ -12,4 +12,4 @@ export * from './datetime';
export * from './text'; export * from './text';
export * from './valueFormats'; export * from './valueFormats';
export * from './field'; export * from './field';
export { PanelPlugin } from './panel/PanelPlugin'; export { PanelPlugin, defaultStandardFieldConfigProperties } from './panel/PanelPlugin';
import React from 'react'; import React from 'react';
import { identityOverrideProcessor } from '../field'; import { identityOverrideProcessor, standardEditorsRegistry } from '../field';
import { PanelPlugin } from './PanelPlugin'; import { PanelPlugin, standardFieldConfigProperties } from './PanelPlugin';
import { StandardFieldConfigProperties } from '../types';
describe('PanelPlugin', () => { describe('PanelPlugin', () => {
describe('declarative options', () => { describe('declarative options', () => {
beforeAll(() => {
standardEditorsRegistry.setInit(() => {
return [
{
id: 'number',
},
] as any;
});
});
test('field config UI API', () => { test('field config UI API', () => {
const panel = new PanelPlugin(() => { const panel = new PanelPlugin(() => {
return <div>Panel</div>; return <div>Panel</div>;
...@@ -45,4 +55,190 @@ describe('PanelPlugin', () => { ...@@ -45,4 +55,190 @@ describe('PanelPlugin', () => {
expect(panel.optionEditors!.list()).toHaveLength(1); expect(panel.optionEditors!.list()).toHaveLength(1);
}); });
}); });
describe('default options', () => {
describe('panel options', () => {
test('default values', () => {
const panel = new PanelPlugin(() => {
return <div>Panel</div>;
});
panel.setPanelOptions(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: () => <div>Editor</div>,
settings: {},
defaultValue: { value: 'Custom default value' },
});
});
const expectedDefaults = {
numericOption: 10,
customOption: { value: 'Custom default value' },
};
expect(panel.defaults).toEqual(expectedDefaults);
});
test('default values for nested paths', () => {
const panel = new PanelPlugin(() => {
return <div>Panel</div>;
});
panel.setPanelOptions(builder => {
builder.addNumberInput({
id: 'numericOption.nested',
name: 'Option editor',
description: 'Option editor description',
defaultValue: 10,
});
});
const expectedDefaults = {
numericOption: { nested: 10 },
};
expect(panel.defaults).toEqual(expectedDefaults);
});
});
describe('field config options', () => {
test('default values', () => {
const panel = new PanelPlugin(() => {
return <div>Panel</div>;
});
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: () => <div>Editor</div>,
override: () => <div>Override editor</div>,
process: identityOverrideProcessor,
shouldApply: () => true,
settings: {},
defaultValue: { value: 'Custom default value' },
});
});
const expectedDefaults = {
numericOption: 10,
customOption: { value: 'Custom default value' },
};
expect(panel.fieldConfigDefaults.defaults.custom).toEqual(expectedDefaults);
});
test('default values for nested paths', () => {
const panel = new PanelPlugin(() => {
return <div>Panel</div>;
});
panel.setCustomFieldOptions(builder => {
builder.addNumberInput({
id: 'numericOption.nested',
name: 'Option editor',
description: 'Option editor description',
defaultValue: 10,
});
});
const expectedDefaults = {
numericOption: { nested: 10 },
};
expect(panel.fieldConfigDefaults.defaults.custom).toEqual(expectedDefaults);
});
});
describe('standard field config options', () => {
test('standard config', () => {
const panel = new PanelPlugin(() => {
return <div>Panel</div>;
});
panel.useStandardFieldConfig();
expect(panel.standardFieldConfigProperties).toEqual(Array.from(standardFieldConfigProperties.keys()));
});
test('selected standard config', () => {
const panel = new PanelPlugin(() => {
return <div>Panel</div>;
});
panel.useStandardFieldConfig([StandardFieldConfigProperties.Min, StandardFieldConfigProperties.Thresholds]);
expect(panel.standardFieldConfigProperties).toEqual(['min', 'thresholds']);
});
describe('default values', () => {
test('setting default values', () => {
const panel = new PanelPlugin(() => {
return <div>Panel</div>;
});
panel.useStandardFieldConfig([StandardFieldConfigProperties.Color, StandardFieldConfigProperties.Min], {
[StandardFieldConfigProperties.Color]: '#ff00ff',
[StandardFieldConfigProperties.Min]: 10,
});
expect(panel.standardFieldConfigProperties).toEqual(['color', 'min']);
expect(panel.fieldConfigDefaults).toEqual({
defaults: {
min: 10,
color: '#ff00ff',
},
overrides: [],
});
});
it('should ignore defaults that are not specified as availeble properties', () => {
const panel = new PanelPlugin(() => {
return <div>Panel</div>;
});
panel.useStandardFieldConfig([StandardFieldConfigProperties.Color], {
[StandardFieldConfigProperties.Color]: '#ff00ff',
[StandardFieldConfigProperties.Min]: 10,
});
expect(panel.standardFieldConfigProperties).toEqual(['color']);
expect(panel.fieldConfigDefaults).toEqual({
defaults: {
color: '#ff00ff',
},
overrides: [],
});
});
});
});
});
}); });
...@@ -8,26 +8,48 @@ import { ...@@ -8,26 +8,48 @@ import {
PanelPluginMeta, PanelPluginMeta,
PanelProps, PanelProps,
PanelTypeChangedHandler, PanelTypeChangedHandler,
StandardFieldConfigProperties,
} from '../types'; } from '../types';
import { FieldConfigEditorBuilder, PanelOptionsEditorBuilder } from '../utils/OptionsUIBuilders'; import { FieldConfigEditorBuilder, PanelOptionsEditorBuilder } from '../utils/OptionsUIBuilders';
import { ComponentClass, ComponentType } from 'react'; import { ComponentClass, ComponentType } from 'react';
import set from 'lodash/set';
import { deprecationWarning } from '../utils';
export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta> { export const defaultStandardFieldConfigProperties: StandardFieldConfigProperties[] = [
private customFieldConfigsUIBuilder = new FieldConfigEditorBuilder(); StandardFieldConfigProperties.Min,
StandardFieldConfigProperties.Max,
StandardFieldConfigProperties.Title,
StandardFieldConfigProperties.Unit,
StandardFieldConfigProperties.Decimals,
StandardFieldConfigProperties.NoValue,
StandardFieldConfigProperties.Color,
StandardFieldConfigProperties.Thresholds,
StandardFieldConfigProperties.Mappings,
StandardFieldConfigProperties.Links,
];
export const standardFieldConfigProperties = new Map(defaultStandardFieldConfigProperties.map(p => [p, undefined]));
export class PanelPlugin<TOptions = any, TFieldConfigOptions extends object = any> extends GrafanaPlugin<
PanelPluginMeta
> {
private _defaults?: TOptions;
private _standardFieldConfigProperties?: Map<StandardFieldConfigProperties, any>;
private _fieldConfigDefaults: FieldConfigSource<TFieldConfigOptions> = {
defaults: {},
overrides: [],
};
private _customFieldConfigs?: FieldConfigEditorRegistry; private _customFieldConfigs?: FieldConfigEditorRegistry;
private registerCustomFieldConfigs?: (builder: FieldConfigEditorBuilder) => void; private customFieldConfigsUIBuilder = new FieldConfigEditorBuilder<TFieldConfigOptions>();
private registerCustomFieldConfigs?: (builder: FieldConfigEditorBuilder<TFieldConfigOptions>) => void;
private optionsUIBuilder = new PanelOptionsEditorBuilder();
private _optionEditors?: PanelOptionEditorsRegistry; private _optionEditors?: PanelOptionEditorsRegistry;
private registerOptionEditors?: (builder: PanelOptionsEditorBuilder) => void; private optionsUIBuilder = new PanelOptionsEditorBuilder<TOptions>();
private registerOptionEditors?: (builder: PanelOptionsEditorBuilder<TOptions>) => void;
panel: ComponentType<PanelProps<TOptions>>; panel: ComponentType<PanelProps<TOptions>>;
editor?: ComponentClass<PanelEditorProps<TOptions>>; editor?: ComponentClass<PanelEditorProps<TOptions>>;
defaults?: TOptions;
fieldConfigDefaults?: FieldConfigSource = {
defaults: {},
overrides: [],
};
onPanelMigration?: PanelMigrationHandler<TOptions>; onPanelMigration?: PanelMigrationHandler<TOptions>;
onPanelTypeChanged?: PanelTypeChangedHandler<TOptions>; onPanelTypeChanged?: PanelTypeChangedHandler<TOptions>;
noPadding?: boolean; noPadding?: boolean;
...@@ -42,6 +64,66 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta> ...@@ -42,6 +64,66 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta>
this.panel = panel; this.panel = panel;
} }
get defaults() {
let result = this._defaults || {};
if (!this._defaults) {
const editors = this.optionEditors;
if (!editors || editors.list().length === 0) {
return null;
}
for (const editor of editors.list()) {
set(result, editor.id, editor.defaultValue);
}
}
return result;
}
get fieldConfigDefaults(): FieldConfigSource<TFieldConfigOptions> {
let customPropertiesDefaults = this._fieldConfigDefaults.defaults.custom;
if (!customPropertiesDefaults) {
customPropertiesDefaults = {} as TFieldConfigOptions;
}
const editors = this.customFieldConfigs;
if (editors && editors.list().length !== 0) {
for (const editor of editors.list()) {
set(customPropertiesDefaults, editor.id, editor.defaultValue);
}
}
return {
defaults: {
...(this._standardFieldConfigProperties ? Object.fromEntries(this._standardFieldConfigProperties) : {}),
custom:
Object.keys(customPropertiesDefaults).length > 0
? {
...customPropertiesDefaults,
}
: undefined,
...this._fieldConfigDefaults.defaults,
},
// 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
*/
setDefaults(defaults: TOptions) {
deprecationWarning('PanelPlugin', 'setDefaults', 'setPanelOptions');
this._defaults = defaults;
return this;
}
get customFieldConfigs() { get customFieldConfigs() {
if (!this._customFieldConfigs && this.registerCustomFieldConfigs) { if (!this._customFieldConfigs && this.registerCustomFieldConfigs) {
this.registerCustomFieldConfigs(this.customFieldConfigsUIBuilder); this.registerCustomFieldConfigs(this.customFieldConfigsUIBuilder);
...@@ -65,11 +147,6 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta> ...@@ -65,11 +147,6 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta>
return this; return this;
} }
setDefaults(defaults: TOptions) {
this.defaults = defaults;
return this;
}
setNoPadding() { setNoPadding() {
this.noPadding = true; this.noPadding = true;
return this; return this;
...@@ -134,7 +211,7 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta> ...@@ -134,7 +211,7 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta>
* *
* @public * @public
**/ **/
setCustomFieldOptions(builder: (builder: FieldConfigEditorBuilder) => void) { setCustomFieldOptions(builder: (builder: FieldConfigEditorBuilder<TFieldConfigOptions>) => void) {
// builder is applied lazily when custom field configs are accessed // builder is applied lazily when custom field configs are accessed
this.registerCustomFieldConfigs = builder; this.registerCustomFieldConfigs = builder;
return this; return this;
...@@ -170,22 +247,63 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta> ...@@ -170,22 +247,63 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta>
* *
* @public * @public
**/ **/
setPanelOptions(builder: (builder: PanelOptionsEditorBuilder) => void) { setPanelOptions(builder: (builder: PanelOptionsEditorBuilder<TOptions>) => void) {
// builder is applied lazily when options UI is created // builder is applied lazily when options UI is created
this.registerOptionEditors = builder; this.registerOptionEditors = builder;
return this; return this;
} }
/** /**
* Enables configuration of panel's default field config * Allows specyfing which standard field config options panel should use and defining default values
*
* @example
* ```typescript
*
* import { ShapePanel } from './ShapePanel';
*
* interface ShapePanelOptions {}
*
* // when plugin should use all standard options
* export const plugin = new PanelPlugin<ShapePanelOptions>(ShapePanel)
* .useStandardFieldConfig();
*
* // 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<ShapePanelOptions>(ShapePanel)
* .useStandardFieldConfig([StandardFieldConfigProperties.Min, StandardFieldConfigProperties.Max, StandardFieldConfigProperties.Links]);
*
* // when standard option's default value needs to be provided
* export const plugin = new PanelPlugin<ShapePanelOptions>(ShapePanel)
* .useStandardFieldConfig([StandardFieldConfigProperties.Min, StandardFieldConfigProperties.Max], {
* [StandardFieldConfigProperties.Min]: 20,
* [StandardFieldConfigProperties.Max]: 100
* });
*
* ```
*
* @public
*/ */
setFieldConfigDefaults(defaultConfig: Partial<FieldConfigSource>) { useStandardFieldConfig(
this.fieldConfigDefaults = { properties?: StandardFieldConfigProperties[],
defaults: {}, defauls?: Partial<Record<StandardFieldConfigProperties, any>>
overrides: [], ) {
...defaultConfig, if (!properties) {
}; this._standardFieldConfigProperties = standardFieldConfigProperties;
return this;
} else {
this._standardFieldConfigProperties = new Map(properties.map(p => [p, standardFieldConfigProperties.get(p)]));
}
if (defauls) {
Object.keys(defauls).map(k => {
if (properties.indexOf(k as StandardFieldConfigProperties) > -1) {
this._standardFieldConfigProperties!.set(
k as StandardFieldConfigProperties,
defauls[k as StandardFieldConfigProperties]
);
}
});
}
return this; return this;
} }
} }
...@@ -5,52 +5,59 @@ import { NumberFieldConfigSettings, SelectFieldConfigSettings, StringFieldConfig ...@@ -5,52 +5,59 @@ import { NumberFieldConfigSettings, SelectFieldConfigSettings, StringFieldConfig
/** /**
* Option editor registry item * Option editor registry item
*/ */
export interface OptionsEditorItem<TSettings, TEditorProps> extends RegistryItem { export interface OptionsEditorItem<TOptions, TSettings, TEditorProps, TValue> extends RegistryItem {
id: (keyof TOptions & string) | string;
editor: ComponentType<TEditorProps>; editor: ComponentType<TEditorProps>;
settings?: TSettings; settings?: TSettings;
defaultValue?: TValue;
} }
/** /**
* Configuration of option editor registry item * Configuration of option editor registry item
*/ */
interface OptionEditorConfig<TSettings> { interface OptionEditorConfig<TOptions, TSettings, TValue = any> {
id: string; id: keyof TOptions & string;
name: string; name: string;
description: string; description: string;
settings?: TSettings; settings?: TSettings;
defaultValue?: TValue;
} }
/** /**
* Describes an API for option editors UI builder * Describes an API for option editors UI builder
*/ */
export interface OptionsUIRegistryBuilderAPI<TEditorProps, T extends OptionsEditorItem<any, TEditorProps>> { export interface OptionsUIRegistryBuilderAPI<
TOptions,
TEditorProps,
T extends OptionsEditorItem<TOptions, any, TEditorProps, any>
> {
addNumberInput?<TSettings extends NumberFieldConfigSettings = NumberFieldConfigSettings>( addNumberInput?<TSettings extends NumberFieldConfigSettings = NumberFieldConfigSettings>(
config: OptionEditorConfig<TSettings> config: OptionEditorConfig<TOptions, TSettings, number>
): this; ): this;
addTextInput?<TSettings extends StringFieldConfigSettings = StringFieldConfigSettings>( addTextInput?<TSettings extends StringFieldConfigSettings = StringFieldConfigSettings>(
config: OptionEditorConfig<TSettings> config: OptionEditorConfig<TOptions, TSettings, string>
): this; ): this;
addSelect?<TOption, TSettings extends SelectFieldConfigSettings<TOption>>( addSelect?<TOption, TSettings extends SelectFieldConfigSettings<TOption>>(
config: OptionEditorConfig<TSettings> config: OptionEditorConfig<TOptions, TSettings, TOption>
): this; ): this;
addRadio?<TOption, TSettings extends SelectFieldConfigSettings<TOption> = SelectFieldConfigSettings<TOption>>( addRadio?<TOption, TSettings extends SelectFieldConfigSettings<TOption> = SelectFieldConfigSettings<TOption>>(
config: OptionEditorConfig<TSettings> config: OptionEditorConfig<TOptions, TSettings, TOption>
): this; ): this;
addBooleanSwitch?<TSettings = any>(config: OptionEditorConfig<TSettings>): this; addBooleanSwitch?<TSettings = any>(config: OptionEditorConfig<TOptions, TSettings, boolean>): this;
addUnitPicker?<TSettings = any>(config: OptionEditorConfig<TSettings>): this; addUnitPicker?<TSettings = any>(config: OptionEditorConfig<TOptions, TSettings, string>): this;
addColorPicker?<TSettings = any>(config: OptionEditorConfig<TSettings>): this; addColorPicker?<TSettings = any>(config: OptionEditorConfig<TOptions, TSettings, string>): this;
/** /**
* Enables custom editor definition * Enables custom editor definition
* @param config * @param config
*/ */
addCustomEditor<TSettings>(config: OptionsEditorItem<TSettings, TEditorProps>): this; addCustomEditor<TSettings, TValue>(config: OptionsEditorItem<TOptions, TSettings, TEditorProps, TValue>): this;
/** /**
* Returns registry of option editors * Returns registry of option editors
...@@ -58,11 +65,14 @@ export interface OptionsUIRegistryBuilderAPI<TEditorProps, T extends OptionsEdit ...@@ -58,11 +65,14 @@ export interface OptionsUIRegistryBuilderAPI<TEditorProps, T extends OptionsEdit
getRegistry: () => Registry<T>; getRegistry: () => Registry<T>;
} }
export abstract class OptionsUIRegistryBuilder<TEditorProps, T extends OptionsEditorItem<any, TEditorProps>> export abstract class OptionsUIRegistryBuilder<
implements OptionsUIRegistryBuilderAPI<TEditorProps, T> { TOptions,
TEditorProps,
T extends OptionsEditorItem<TOptions, any, TEditorProps, any>
> implements OptionsUIRegistryBuilderAPI<TOptions, TEditorProps, T> {
private properties: T[] = []; private properties: T[] = [];
addCustomEditor<TValue>(config: T & OptionsEditorItem<TValue, TEditorProps>): this { addCustomEditor<TSettings, TValue>(config: T & OptionsEditorItem<TOptions, TSettings, TEditorProps, TValue>): this {
this.properties.push(config); this.properties.push(config);
return this; return this;
} }
......
...@@ -21,7 +21,7 @@ export enum FieldType { ...@@ -21,7 +21,7 @@ export enum FieldType {
* *
* Plugins may extend this with additional properties. Something like series overrides * Plugins may extend this with additional properties. Something like series overrides
*/ */
export interface FieldConfig { export interface FieldConfig<TOptions extends object = any> {
title?: string; // The display value for this field. This supports template variables blank is auto title?: string; // The display value for this field. This supports template variables blank is auto
filterable?: boolean; filterable?: boolean;
...@@ -50,7 +50,7 @@ export interface FieldConfig { ...@@ -50,7 +50,7 @@ export interface FieldConfig {
noValue?: string; noValue?: string;
// Panel Specific Values // Panel Specific Values
custom?: Record<string, any>; custom?: TOptions;
scopedVars?: ScopedVars; scopedVars?: ScopedVars;
} }
......
...@@ -25,9 +25,9 @@ export interface ConfigOverrideRule { ...@@ -25,9 +25,9 @@ export interface ConfigOverrideRule {
properties: DynamicConfigValue[]; properties: DynamicConfigValue[];
} }
export interface FieldConfigSource { export interface FieldConfigSource<TOptions extends object = any> {
// Defatuls applied to all numeric fields // Defatuls applied to all numeric fields
defaults: FieldConfig; defaults: FieldConfig<TOptions>;
// Rules to override individual values // Rules to override individual values
overrides: ConfigOverrideRule[]; overrides: ConfigOverrideRule[];
...@@ -54,16 +54,17 @@ export interface FieldOverrideEditorProps<TValue, TSettings> extends Omit<Standa ...@@ -54,16 +54,17 @@ export interface FieldOverrideEditorProps<TValue, TSettings> extends Omit<Standa
context: FieldOverrideContext; context: FieldOverrideContext;
} }
export interface FieldConfigEditorConfig<TSettings = any> { export interface FieldConfigEditorConfig<TOptions, TSettings = any, TValue = any> {
id: string; id: (keyof TOptions & string) | string;
name: string; name: string;
description: string; description: string;
settings?: TSettings; settings?: TSettings;
shouldApply?: (field: Field) => boolean; shouldApply?: (field: Field) => boolean;
defaultValue?: TValue;
} }
export interface FieldPropertyEditorItem<TValue = any, TSettings extends {} = any> export interface FieldPropertyEditorItem<TOptions = any, TValue = any, TSettings extends {} = any>
extends OptionsEditorItem<TSettings, FieldConfigEditorProps<TValue, TSettings>> { extends OptionsEditorItem<TOptions, TSettings, FieldConfigEditorProps<TValue, TSettings>, TValue> {
// An editor that can be filled in with context info (template variables etc) // An editor that can be filled in with context info (template variables etc)
override: ComponentType<FieldOverrideEditorProps<TValue, TSettings>>; override: ComponentType<FieldOverrideEditorProps<TValue, TSettings>>;
...@@ -86,3 +87,16 @@ export interface ApplyFieldOverrideOptions { ...@@ -86,3 +87,16 @@ export interface ApplyFieldOverrideOptions {
standard?: FieldConfigEditorRegistry; standard?: FieldConfigEditorRegistry;
custom?: FieldConfigEditorRegistry; custom?: FieldConfigEditorRegistry;
} }
export enum StandardFieldConfigProperties {
Unit = 'unit',
Min = 'min',
Max = 'max',
Decimals = 'decimals',
Title = 'title',
NoValue = 'noValue',
Thresholds = 'thresholds',
Mappings = 'mappings',
Links = 'links',
Color = 'color',
}
...@@ -115,14 +115,15 @@ export type PanelOptionEditorsRegistry = Registry<PanelOptionsEditorItem>; ...@@ -115,14 +115,15 @@ export type PanelOptionEditorsRegistry = Registry<PanelOptionsEditorItem>;
export interface PanelOptionsEditorProps<TValue> extends StandardEditorProps<TValue> {} export interface PanelOptionsEditorProps<TValue> extends StandardEditorProps<TValue> {}
export interface PanelOptionsEditorItem<TValue = any, TSettings = any> export interface PanelOptionsEditorItem<TOptions = any, TValue = any, TSettings = any>
extends OptionsEditorItem<TSettings, PanelOptionsEditorProps<TValue>> {} extends OptionsEditorItem<TOptions, TSettings, PanelOptionsEditorProps<TValue>, TValue> {}
export interface PanelOptionsEditorConfig<TSettings = any> { export interface PanelOptionsEditorConfig<TOptions, TSettings = any, TValue = any> {
id: string; id: (keyof TOptions & string) | string;
name: string; name: string;
description: string; description: string;
settings?: TSettings; settings?: TSettings;
defaultValue?: TValue;
} }
export interface PanelMenuItem { export interface PanelMenuItem {
......
...@@ -26,11 +26,12 @@ import { ...@@ -26,11 +26,12 @@ import {
/** /**
* Fluent API for declarative creation of field config option editors * Fluent API for declarative creation of field config option editors
*/ */
export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder< export class FieldConfigEditorBuilder<TOptions> extends OptionsUIRegistryBuilder<
TOptions,
FieldConfigEditorProps<any, any>, FieldConfigEditorProps<any, any>,
FieldPropertyEditorItem FieldPropertyEditorItem<TOptions>
> { > {
addNumberInput<TSettings>(config: FieldConfigEditorConfig<TSettings & NumberFieldConfigSettings>) { addNumberInput<TSettings>(config: FieldConfigEditorConfig<TOptions, TSettings & NumberFieldConfigSettings, number>) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
override: standardEditorsRegistry.get('number').editor as any, override: standardEditorsRegistry.get('number').editor as any,
...@@ -41,7 +42,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder< ...@@ -41,7 +42,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder<
}); });
} }
addTextInput<TSettings>(config: FieldConfigEditorConfig<TSettings & StringFieldConfigSettings>) { addTextInput<TSettings>(config: FieldConfigEditorConfig<TOptions, TSettings & StringFieldConfigSettings, string>) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
override: standardEditorsRegistry.get('text').editor as any, override: standardEditorsRegistry.get('text').editor as any,
...@@ -52,7 +53,9 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder< ...@@ -52,7 +53,9 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder<
}); });
} }
addSelect<TOption, TSettings = any>(config: FieldConfigEditorConfig<TSettings & SelectFieldConfigSettings<TOption>>) { addSelect<TOption, TSettings extends SelectFieldConfigSettings<TOption>>(
config: FieldConfigEditorConfig<TOptions, TSettings, TOption>
) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
override: standardEditorsRegistry.get('select').editor as any, override: standardEditorsRegistry.get('select').editor as any,
...@@ -64,7 +67,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder< ...@@ -64,7 +67,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder<
}); });
} }
addRadio<TOption, TSettings = any>(config: FieldConfigEditorConfig<TSettings & SelectFieldConfigSettings<TOption>>) { addRadio<TOption, TSettings = any>(config: FieldConfigEditorConfig<TOptions, TSettings, TOption>) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
override: standardEditorsRegistry.get('radio').editor as any, override: standardEditorsRegistry.get('radio').editor as any,
...@@ -76,7 +79,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder< ...@@ -76,7 +79,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder<
}); });
} }
addBooleanSwitch<TSettings = any>(config: FieldConfigEditorConfig<TSettings>) { addBooleanSwitch<TSettings = any>(config: FieldConfigEditorConfig<TOptions, TSettings, boolean>) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
editor: standardEditorsRegistry.get('boolean').editor as any, editor: standardEditorsRegistry.get('boolean').editor as any,
...@@ -87,7 +90,9 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder< ...@@ -87,7 +90,9 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder<
}); });
} }
addColorPicker<TSettings = any>(config: FieldConfigEditorConfig<TSettings & ColorFieldConfigSettings>) { addColorPicker<TSettings = any>(
config: FieldConfigEditorConfig<TOptions, TSettings & ColorFieldConfigSettings, string>
) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
editor: standardEditorsRegistry.get('color').editor as any, editor: standardEditorsRegistry.get('color').editor as any,
...@@ -98,7 +103,9 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder< ...@@ -98,7 +103,9 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder<
}); });
} }
addUnitPicker<TSettings = any>(config: FieldConfigEditorConfig<TSettings & UnitFieldConfigSettings>) { addUnitPicker<TSettings = any>(
config: FieldConfigEditorConfig<TOptions, TSettings & UnitFieldConfigSettings, string>
) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
editor: standardEditorsRegistry.get('unit').editor as any, editor: standardEditorsRegistry.get('unit').editor as any,
...@@ -113,43 +120,53 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder< ...@@ -113,43 +120,53 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder<
/** /**
* Fluent API for declarative creation of panel options * Fluent API for declarative creation of panel options
*/ */
export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilder<StandardEditorProps, PanelOptionsEditorItem> { export class PanelOptionsEditorBuilder<TOptions> extends OptionsUIRegistryBuilder<
addNumberInput<TSettings>(config: PanelOptionsEditorConfig<TSettings & NumberFieldConfigSettings>) { TOptions,
StandardEditorProps,
PanelOptionsEditorItem<TOptions>
> {
addNumberInput<TSettings>(config: PanelOptionsEditorConfig<TOptions, TSettings & NumberFieldConfigSettings, number>) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
editor: standardEditorsRegistry.get('number').editor as any, editor: standardEditorsRegistry.get('number').editor as any,
}); });
} }
addTextInput<TSettings>(config: PanelOptionsEditorConfig<TSettings & StringFieldConfigSettings>) { addTextInput<TSettings>(config: PanelOptionsEditorConfig<TOptions, TSettings & StringFieldConfigSettings, string>) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
editor: standardEditorsRegistry.get('text').editor as any, editor: standardEditorsRegistry.get('text').editor as any,
}); });
} }
addSelect<TOption, TSettings>(config: PanelOptionsEditorConfig<TSettings & SelectFieldConfigSettings<TOption>>) { addSelect<TOption, TSettings extends SelectFieldConfigSettings<TOption>>(
config: PanelOptionsEditorConfig<TOptions, TSettings, TOption>
) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
editor: standardEditorsRegistry.get('select').editor as any, editor: standardEditorsRegistry.get('select').editor as any,
}); });
} }
addRadio<TOption, TSettings>(config: PanelOptionsEditorConfig<TSettings & SelectFieldConfigSettings<TOption>>) { addRadio<TOption, TSettings extends SelectFieldConfigSettings<TOption>>(
config: PanelOptionsEditorConfig<TOptions, TSettings, TOption>
) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
editor: standardEditorsRegistry.get('radio').editor as any, editor: standardEditorsRegistry.get('radio').editor as any,
}); });
} }
addBooleanSwitch<TSettings = any>(config: PanelOptionsEditorConfig<TSettings>) { addBooleanSwitch<TSettings = any>(config: PanelOptionsEditorConfig<TOptions, TSettings, boolean>) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
editor: standardEditorsRegistry.get('boolean').editor as any, editor: standardEditorsRegistry.get('boolean').editor as any,
}); });
} }
addColorPicker<TSettings = any>(config: PanelOptionsEditorConfig<TSettings & ColorFieldConfigSettings>): this { addColorPicker<TSettings = any>(
config: PanelOptionsEditorConfig<TOptions, TSettings & ColorFieldConfigSettings, string>
): this {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
editor: standardEditorsRegistry.get('color').editor as any, editor: standardEditorsRegistry.get('color').editor as any,
...@@ -157,7 +174,9 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilder<Standard ...@@ -157,7 +174,9 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilder<Standard
}); });
} }
addUnitPicker<TSettings = any>(config: PanelOptionsEditorConfig<TSettings & UnitFieldConfigSettings>): this { addUnitPicker<TSettings = any>(
config: PanelOptionsEditorConfig<TOptions, TSettings & UnitFieldConfigSettings, string>
): this {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
editor: standardEditorsRegistry.get('unit').editor as any, editor: standardEditorsRegistry.get('unit').editor as any,
......
...@@ -6,7 +6,7 @@ const history: KeyValue<number> = {}; ...@@ -6,7 +6,7 @@ const history: KeyValue<number> = {};
export const deprecationWarning = (file: string, oldName: string, newName?: string) => { export const deprecationWarning = (file: string, oldName: string, newName?: string) => {
let message = `[Deprecation warning] ${file}: ${oldName} is deprecated`; let message = `[Deprecation warning] ${file}: ${oldName} is deprecated`;
if (newName) { if (newName) {
message += `. Use ${newName} instead`; message += `. Use ${newName} instead`;
} }
const now = Date.now(); const now = Date.now();
const last = history[message]; const last = history[message];
......
...@@ -5,6 +5,13 @@ import Forms from '../Forms'; ...@@ -5,6 +5,13 @@ import Forms from '../Forms';
export const StringValueEditor: React.FC<FieldConfigEditorProps<string, StringFieldConfigSettings>> = ({ export const StringValueEditor: React.FC<FieldConfigEditorProps<string, StringFieldConfigSettings>> = ({
value, value,
onChange, onChange,
item,
}) => { }) => {
return <Forms.Input value={value || ''} onChange={e => onChange(e.currentTarget.value)} />; return (
<Forms.Input
placeholder={item.settings?.placeholder}
value={value || ''}
onChange={e => onChange(e.currentTarget.value)}
/>
);
}; };
...@@ -30,7 +30,7 @@ import { StatsPickerEditor } from '../components/OptionsUI/stats'; ...@@ -30,7 +30,7 @@ import { StatsPickerEditor } from '../components/OptionsUI/stats';
* Returns collection of common field config properties definitions * Returns collection of common field config properties definitions
*/ */
export const getStandardFieldConfigs = () => { export const getStandardFieldConfigs = () => {
const title: FieldPropertyEditorItem<string, StringFieldConfigSettings> = { const title: FieldPropertyEditorItem<any, string, StringFieldConfigSettings> = {
id: 'title', id: 'title',
name: 'Title', name: 'Title',
description: "Field's title", description: "Field's title",
...@@ -38,13 +38,13 @@ export const getStandardFieldConfigs = () => { ...@@ -38,13 +38,13 @@ export const getStandardFieldConfigs = () => {
override: standardEditorsRegistry.get('text').editor as any, override: standardEditorsRegistry.get('text').editor as any,
process: stringOverrideProcessor, process: stringOverrideProcessor,
settings: { settings: {
placeholder: 'auto', placeholder: 'none',
expandTemplateVars: true, expandTemplateVars: true,
}, },
shouldApply: field => field.type !== FieldType.time, shouldApply: field => field.type !== FieldType.time,
}; };
const unit: FieldPropertyEditorItem<string, StringFieldConfigSettings> = { const unit: FieldPropertyEditorItem<any, string, StringFieldConfigSettings> = {
id: 'unit', id: 'unit',
name: 'Unit', name: 'Unit',
description: 'Value units', description: 'Value units',
...@@ -60,7 +60,7 @@ export const getStandardFieldConfigs = () => { ...@@ -60,7 +60,7 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type === FieldType.number, shouldApply: field => field.type === FieldType.number,
}; };
const min: FieldPropertyEditorItem<number, NumberFieldConfigSettings> = { const min: FieldPropertyEditorItem<any, number, NumberFieldConfigSettings> = {
id: 'min', id: 'min',
name: 'Min', name: 'Min',
description: 'Minimum expected value', description: 'Minimum expected value',
...@@ -75,7 +75,7 @@ export const getStandardFieldConfigs = () => { ...@@ -75,7 +75,7 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type === FieldType.number, shouldApply: field => field.type === FieldType.number,
}; };
const max: FieldPropertyEditorItem<number, NumberFieldConfigSettings> = { const max: FieldPropertyEditorItem<any, number, NumberFieldConfigSettings> = {
id: 'max', id: 'max',
name: 'Max', name: 'Max',
description: 'Maximum expected value', description: 'Maximum expected value',
...@@ -91,7 +91,7 @@ export const getStandardFieldConfigs = () => { ...@@ -91,7 +91,7 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type === FieldType.number, shouldApply: field => field.type === FieldType.number,
}; };
const decimals: FieldPropertyEditorItem<number, NumberFieldConfigSettings> = { const decimals: FieldPropertyEditorItem<any, number, NumberFieldConfigSettings> = {
id: 'decimals', id: 'decimals',
name: 'Decimals', name: 'Decimals',
description: 'Number of decimal to be shown for a value', description: 'Number of decimal to be shown for a value',
...@@ -110,7 +110,7 @@ export const getStandardFieldConfigs = () => { ...@@ -110,7 +110,7 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type === FieldType.number, shouldApply: field => field.type === FieldType.number,
}; };
const thresholds: FieldPropertyEditorItem<ThresholdsConfig, ThresholdsFieldConfigSettings> = { const thresholds: FieldPropertyEditorItem<any, ThresholdsConfig, ThresholdsFieldConfigSettings> = {
id: 'thresholds', id: 'thresholds',
name: 'Thresholds', name: 'Thresholds',
description: 'Manage thresholds', description: 'Manage thresholds',
...@@ -126,7 +126,7 @@ export const getStandardFieldConfigs = () => { ...@@ -126,7 +126,7 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type === FieldType.number, shouldApply: field => field.type === FieldType.number,
}; };
const mappings: FieldPropertyEditorItem<ValueMapping[], ValueMappingFieldConfigSettings> = { const mappings: FieldPropertyEditorItem<any, ValueMapping[], ValueMappingFieldConfigSettings> = {
id: 'mappings', id: 'mappings',
name: 'Value mappings', name: 'Value mappings',
description: 'Manage value mappings', description: 'Manage value mappings',
...@@ -141,7 +141,7 @@ export const getStandardFieldConfigs = () => { ...@@ -141,7 +141,7 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type === FieldType.number, shouldApply: field => field.type === FieldType.number,
}; };
const noValue: FieldPropertyEditorItem<string, StringFieldConfigSettings> = { const noValue: FieldPropertyEditorItem<any, string, StringFieldConfigSettings> = {
id: 'noValue', id: 'noValue',
name: 'No Value', name: 'No Value',
description: 'What to show when there is no value', description: 'What to show when there is no value',
...@@ -157,7 +157,7 @@ export const getStandardFieldConfigs = () => { ...@@ -157,7 +157,7 @@ export const getStandardFieldConfigs = () => {
shouldApply: () => true, shouldApply: () => true,
}; };
const links: FieldPropertyEditorItem<DataLink[], StringFieldConfigSettings> = { const links: FieldPropertyEditorItem<any, DataLink[], StringFieldConfigSettings> = {
id: 'links', id: 'links',
name: 'DataLinks', name: 'DataLinks',
description: 'Manage date links', description: 'Manage date links',
...@@ -170,7 +170,7 @@ export const getStandardFieldConfigs = () => { ...@@ -170,7 +170,7 @@ export const getStandardFieldConfigs = () => {
shouldApply: () => true, shouldApply: () => true,
}; };
const color: FieldPropertyEditorItem<string, StringFieldConfigSettings> = { const color: FieldPropertyEditorItem<any, string, StringFieldConfigSettings> = {
id: 'color', id: 'color',
name: 'Color', name: 'Color',
description: 'Customise color', description: 'Customise color',
...@@ -217,7 +217,7 @@ export const getStandardOptionEditors = () => { ...@@ -217,7 +217,7 @@ export const getStandardOptionEditors = () => {
description: 'Allows option selection', description: 'Allows option selection',
editor: props => ( editor: props => (
<Forms.Select <Forms.Select
defaultValue={props.value} value={props.value}
onChange={e => props.onChange(e.value)} onChange={e => props.onChange(e.value)}
options={props.item.settings?.options} options={props.item.settings?.options}
/> />
......
...@@ -8,6 +8,7 @@ import { ...@@ -8,6 +8,7 @@ import {
standardFieldConfigEditorRegistry, standardFieldConfigEditorRegistry,
PanelPlugin, PanelPlugin,
SelectableValue, SelectableValue,
StandardFieldConfigProperties,
} from '@grafana/data'; } from '@grafana/data';
import { Forms, fieldMatchersUI, ValuePicker, useTheme } from '@grafana/ui'; import { Forms, fieldMatchersUI, ValuePicker, useTheme } from '@grafana/ui';
import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_srv'; import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
...@@ -17,7 +18,7 @@ import { css } from 'emotion'; ...@@ -17,7 +18,7 @@ import { css } from 'emotion';
interface Props { interface Props {
plugin: PanelPlugin; plugin: PanelPlugin;
config: FieldConfigSource; config: FieldConfigSource;
include?: string[]; // Ordered list of which fields should be shown/included include?: StandardFieldConfigProperties[]; // Ordered list of which fields should be shown/included
onChange: (config: FieldConfigSource) => void; onChange: (config: FieldConfigSource) => void;
/* Helpful for IntelliSense */ /* Helpful for IntelliSense */
data: DataFrame[]; data: DataFrame[];
...@@ -67,12 +68,15 @@ export const OverrideFieldConfigEditor: React.FC<Props> = props => { ...@@ -67,12 +68,15 @@ export const OverrideFieldConfigEditor: React.FC<Props> = props => {
return null; return null;
} }
let configPropertiesOptions = standardFieldConfigEditorRegistry.list().map(i => ({ let configPropertiesOptions = plugin.standardFieldConfigProperties.map(i => {
label: i.name, const editor = standardFieldConfigEditorRegistry.get(i);
value: i.id, return {
description: i.description, label: editor.name,
custom: false, value: editor.id,
})); description: editor.description,
custom: false,
};
});
if (customFieldConfigs) { if (customFieldConfigs) {
configPropertiesOptions = configPropertiesOptions.concat( configPropertiesOptions = configPropertiesOptions.concat(
...@@ -185,6 +189,9 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ include, data, onCha ...@@ -185,6 +189,9 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ include, data, onCha
); );
const renderStandardConfigs = useCallback(() => { const renderStandardConfigs = useCallback(() => {
if (include && include.length === 0) {
return null;
}
if (include) { if (include) {
return <>{include.map(f => renderEditor(standardFieldConfigEditorRegistry.get(f), false))}</>; return <>{include.map(f => renderEditor(standardFieldConfigEditorRegistry.get(f), false))}</>;
} }
......
...@@ -60,6 +60,7 @@ export const OptionsPaneContent: React.FC<{ ...@@ -60,6 +60,7 @@ export const OptionsPaneContent: React.FC<{
plugin={plugin} plugin={plugin}
onChange={onFieldConfigsChange} onChange={onFieldConfigsChange}
data={data.series} data={data.series}
include={plugin.standardFieldConfigProperties}
/> />
</Container> </Container>
); );
......
import { PanelModel } from './PanelModel'; import { PanelModel } from './PanelModel';
import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks'; import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks';
import { ConfigOverrideRule, PanelProps } from '@grafana/data'; import { PanelProps, StandardFieldConfigProperties } from '@grafana/data';
import { ComponentClass } from 'react'; import { ComponentClass } from 'react';
class TablePanelCtrl {} class TablePanelCtrl {}
...@@ -70,13 +70,6 @@ describe('PanelModel', () => { ...@@ -70,13 +70,6 @@ describe('PanelModel', () => {
}; };
model = new PanelModel(modelJson); model = new PanelModel(modelJson);
const overrideMock: ConfigOverrideRule = {
matcher: {
id: '2',
options: {},
},
properties: [],
};
const panelPlugin = getPanelPlugin( const panelPlugin = getPanelPlugin(
{ {
...@@ -86,12 +79,9 @@ describe('PanelModel', () => { ...@@ -86,12 +79,9 @@ describe('PanelModel', () => {
TablePanelCtrl // angular TablePanelCtrl // angular
); );
panelPlugin.setDefaults(defaultOptionsMock); panelPlugin.setDefaults(defaultOptionsMock);
panelPlugin.setFieldConfigDefaults({ panelPlugin.useStandardFieldConfig([StandardFieldConfigProperties.Unit, StandardFieldConfigProperties.Decimals], {
defaults: { [StandardFieldConfigProperties.Unit]: 'flop',
unit: 'flop', [StandardFieldConfigProperties.Decimals]: 2,
decimals: 2,
},
overrides: [overrideMock],
}); });
model.pluginLoaded(panelPlugin); model.pluginLoaded(panelPlugin);
}); });
...@@ -108,10 +98,6 @@ describe('PanelModel', () => { ...@@ -108,10 +98,6 @@ describe('PanelModel', () => {
expect(model.getOptions().arrayWith2Values.length).toBe(1); expect(model.getOptions().arrayWith2Values.length).toBe(1);
}); });
it('should merge override field config options', () => {
expect(model.getFieldOverrideOptions().fieldOptions.overrides.length).toBe(2);
});
it('should apply field config defaults', () => { it('should apply field config defaults', () => {
// default unit is overriden by model // default unit is overriden by model
expect(model.getFieldOverrideOptions().fieldOptions.defaults.unit).toBe('mpg'); expect(model.getFieldOverrideOptions().fieldOptions.defaults.unit).toBe('mpg');
......
import { sharedSingleStatPanelChangedHandler } from '@grafana/ui'; import { sharedSingleStatPanelChangedHandler } from '@grafana/ui';
import { PanelPlugin } from '@grafana/data'; import { defaultStandardFieldConfigProperties, PanelPlugin } from '@grafana/data';
import { BarGaugePanel } from './BarGaugePanel'; import { BarGaugePanel } from './BarGaugePanel';
import { BarGaugeOptions, defaults } from './types'; import { BarGaugeOptions, defaults } from './types';
import { standardFieldConfig, addStandardDataReduceOptions } from '../stat/types'; import { standardFieldConfigDefaults, addStandardDataReduceOptions } from '../stat/types';
import { BarGaugePanelEditor } from './BarGaugePanelEditor'; import { BarGaugePanelEditor } from './BarGaugePanelEditor';
import { barGaugePanelMigrationHandler } from './BarGaugeMigrations'; import { barGaugePanelMigrationHandler } from './BarGaugeMigrations';
export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel) export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel)
.setDefaults(defaults) .setDefaults(defaults)
.setEditor(BarGaugePanelEditor) .setEditor(BarGaugePanelEditor)
.setFieldConfigDefaults(standardFieldConfig)
.setPanelOptions(builder => { .setPanelOptions(builder => {
addStandardDataReduceOptions(builder); addStandardDataReduceOptions(builder);
...@@ -33,4 +32,5 @@ export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel) ...@@ -33,4 +32,5 @@ export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel)
}); });
}) })
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler) .setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
.setMigrationHandler(barGaugePanelMigrationHandler); .setMigrationHandler(barGaugePanelMigrationHandler)
.useStandardFieldConfig(defaultStandardFieldConfigProperties, standardFieldConfigDefaults);
import { PanelPlugin } from '@grafana/data'; import { defaultStandardFieldConfigProperties, PanelPlugin } from '@grafana/data';
import { GaugePanelEditor } from './GaugePanelEditor'; import { GaugePanelEditor } from './GaugePanelEditor';
import { GaugePanel } from './GaugePanel'; import { GaugePanel } from './GaugePanel';
import { GaugeOptions, defaults } from './types'; import { GaugeOptions, defaults } from './types';
import { standardFieldConfig, addStandardDataReduceOptions } from '../stat/types'; import { standardFieldConfigDefaults, addStandardDataReduceOptions } from '../stat/types';
import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMigrations'; import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMigrations';
export const plugin = new PanelPlugin<GaugeOptions>(GaugePanel) export const plugin = new PanelPlugin<GaugeOptions>(GaugePanel)
.setDefaults(defaults) .setDefaults(defaults)
.setFieldConfigDefaults(standardFieldConfig)
.setEditor(GaugePanelEditor) .setEditor(GaugePanelEditor)
.setPanelOptions(builder => { .setPanelOptions(builder => {
addStandardDataReduceOptions(builder); addStandardDataReduceOptions(builder);
...@@ -25,4 +24,5 @@ export const plugin = new PanelPlugin<GaugeOptions>(GaugePanel) ...@@ -25,4 +24,5 @@ export const plugin = new PanelPlugin<GaugeOptions>(GaugePanel)
}); });
}) })
.setPanelChangeHandler(gaugePanelChangedHandler) .setPanelChangeHandler(gaugePanelChangedHandler)
.setMigrationHandler(gaugePanelMigrationHandler); .setMigrationHandler(gaugePanelMigrationHandler)
.useStandardFieldConfig(defaultStandardFieldConfigProperties, standardFieldConfigDefaults);
import { PanelPlugin } from '@grafana/data'; import { defaultStandardFieldConfigProperties, PanelPlugin, StandardFieldConfigProperties } from '@grafana/data';
import { PieChartPanelEditor } from './PieChartPanelEditor'; import { PieChartPanelEditor } from './PieChartPanelEditor';
import { PieChartPanel } from './PieChartPanel'; import { PieChartPanel } from './PieChartPanel';
import { PieChartOptions, defaults } from './types'; import { PieChartOptions, defaults } from './types';
export const plugin = new PanelPlugin<PieChartOptions>(PieChartPanel) export const plugin = new PanelPlugin<PieChartOptions>(PieChartPanel)
.setDefaults(defaults) .setDefaults(defaults)
.setFieldConfigDefaults({ defaults: { unit: 'short' } }) .useStandardFieldConfig(defaultStandardFieldConfigProperties, {
[StandardFieldConfigProperties.Unit]: 'short',
})
.setEditor(PieChartPanelEditor); .setEditor(PieChartPanelEditor);
import { sharedSingleStatMigrationHandler, sharedSingleStatPanelChangedHandler } from '@grafana/ui'; import { sharedSingleStatMigrationHandler, sharedSingleStatPanelChangedHandler } from '@grafana/ui';
import { PanelPlugin } from '@grafana/data'; import { defaultStandardFieldConfigProperties, PanelPlugin } from '@grafana/data';
import { StatPanelOptions, defaults, standardFieldConfig, addStandardDataReduceOptions } from './types'; import { StatPanelOptions, defaults, standardFieldConfigDefaults, addStandardDataReduceOptions } from './types';
import { StatPanel } from './StatPanel'; import { StatPanel } from './StatPanel';
import { StatPanelEditor } from './StatPanelEditor'; import { StatPanelEditor } from './StatPanelEditor';
export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel) export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
.setDefaults(defaults) .setDefaults(defaults)
.setFieldConfigDefaults(standardFieldConfig)
.setEditor(StatPanelEditor) .setEditor(StatPanelEditor)
.setPanelOptions(builder => { .setPanelOptions(builder => {
addStandardDataReduceOptions(builder); addStandardDataReduceOptions(builder);
...@@ -48,4 +47,5 @@ export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel) ...@@ -48,4 +47,5 @@ export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
}) })
.setNoPadding() .setNoPadding()
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler) .setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
.setMigrationHandler(sharedSingleStatMigrationHandler); .setMigrationHandler(sharedSingleStatMigrationHandler)
.useStandardFieldConfig(defaultStandardFieldConfigProperties, standardFieldConfigDefaults);
...@@ -4,9 +4,9 @@ import { ...@@ -4,9 +4,9 @@ import {
ReducerID, ReducerID,
ReduceDataOptions, ReduceDataOptions,
SelectableValue, SelectableValue,
FieldConfigSource,
ThresholdsMode, ThresholdsMode,
standardEditorsRegistry, standardEditorsRegistry,
StandardFieldConfigProperties,
} from '@grafana/data'; } from '@grafana/data';
import { PanelOptionsEditorBuilder } from '@grafana/data/src/utils/OptionsUIBuilders'; import { PanelOptionsEditorBuilder } from '@grafana/data/src/utils/OptionsUIBuilders';
...@@ -37,21 +37,18 @@ export const commonValueOptionDefaults: ReduceDataOptions = { ...@@ -37,21 +37,18 @@ export const commonValueOptionDefaults: ReduceDataOptions = {
calcs: [ReducerID.mean], calcs: [ReducerID.mean],
}; };
export const standardFieldConfig: FieldConfigSource = { export const standardFieldConfigDefaults: Partial<Record<StandardFieldConfigProperties, any>> = {
defaults: { [StandardFieldConfigProperties.Thresholds]: {
thresholds: { mode: ThresholdsMode.Absolute,
mode: ThresholdsMode.Absolute, steps: [
steps: [ { value: -Infinity, color: 'green' },
{ value: -Infinity, color: 'green' }, { value: 80, color: 'red' }, // 80%
{ value: 80, color: 'red' }, // 80% ],
],
},
mappings: [],
}, },
overrides: [], [StandardFieldConfigProperties.Mappings]: [],
}; };
export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder) { export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder<StatPanelOptions>) {
builder.addRadio({ builder.addRadio({
id: 'reduceOptions.values', id: 'reduceOptions.values',
name: 'Show', name: 'Show',
......
import { PanelPlugin } from '@grafana/data'; import { PanelPlugin } from '@grafana/data';
import { TablePanel } from './TablePanel'; import { TablePanel } from './TablePanel';
import { Options, defaults } from './types'; import { CustomFieldConfig, defaults, Options } from './types';
export const plugin = new PanelPlugin<Options>(TablePanel) export const plugin = new PanelPlugin<Options, CustomFieldConfig>(TablePanel)
.setDefaults(defaults) .setDefaults(defaults)
.setCustomFieldOptions(builder => { .setCustomFieldOptions(builder => {
builder builder
...@@ -15,6 +15,7 @@ export const plugin = new PanelPlugin<Options>(TablePanel) ...@@ -15,6 +15,7 @@ export const plugin = new PanelPlugin<Options>(TablePanel)
min: 20, min: 20,
max: 300, max: 300,
}, },
defaultValue: 1,
}) })
.addSelect({ .addSelect({
id: 'displayMode', id: 'displayMode',
......
...@@ -2,6 +2,11 @@ export interface Options { ...@@ -2,6 +2,11 @@ export interface Options {
showHeader: boolean; showHeader: boolean;
} }
export interface CustomFieldConfig {
width: number;
displayMode: string;
}
export const defaults: Options = { export const defaults: Options = {
showHeader: true, showHeader: true,
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment