Commit 2ea4a36b by Dominik Prokop Committed by GitHub

Field color: handling color changes when switching panel types (#28875)

* FieldColor: Per panel settings to filter out supported modes

* Updates

* Updated solution

* Update panel plugin API for standard options support

* Update FieldColorConfigSettings interface

* Change color mode correctly when changing plugin type

* Render only applicable color modes in field color config editor

* Apply field config API changes

* TS fixes

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
parent 2887f3f6
...@@ -24,6 +24,7 @@ export const fieldColorModeRegistry = new Registry<FieldColorMode>(() => { ...@@ -24,6 +24,7 @@ export const fieldColorModeRegistry = new Registry<FieldColorMode>(() => {
{ {
id: FieldColorModeId.Thresholds, id: FieldColorModeId.Thresholds,
name: 'From thresholds', name: 'From thresholds',
isByValue: true,
description: 'Derive colors from thresholds', description: 'Derive colors from thresholds',
getCalculator: (_field, theme) => { getCalculator: (_field, theme) => {
return (_value, _percent, threshold) => { return (_value, _percent, threshold) => {
......
...@@ -306,7 +306,6 @@ const processFieldConfigValue = ( ...@@ -306,7 +306,6 @@ const processFieldConfigValue = (
const currentConfig = get(destination, fieldConfigProperty.path); const currentConfig = get(destination, fieldConfigProperty.path);
if (currentConfig === null || currentConfig === undefined) { if (currentConfig === null || currentConfig === undefined) {
const item = context.fieldConfigRegistry.getIfExists(fieldConfigProperty.id); const item = context.fieldConfigRegistry.getIfExists(fieldConfigProperty.id);
// console.log(item);
if (!item) { if (!item) {
return; return;
} }
......
...@@ -120,8 +120,15 @@ export const booleanOverrideProcessor = ( ...@@ -120,8 +120,15 @@ export const booleanOverrideProcessor = (
return value; // !!!! likely not !!!! return value; // !!!! likely not !!!!
}; };
export interface ColorFieldConfigSettings { export interface FieldColorConfigSettings {
allowUndefined?: boolean; /**
textWhenUndefined?: string; // Pick Color * When switching to a visualization that does not support by value coloring then Grafana will
disableNamedColors?: boolean; * switch to a by series palette based color mode
*/
byValueSupport?: boolean;
/**
* When switching to a visualization that has this set to true then Grafana will change color mode
* to from thresholds if it was set to a by series palette
*/
preferThresholdsMode?: boolean;
} }
...@@ -9,11 +9,11 @@ describe('PanelPlugin', () => { ...@@ -9,11 +9,11 @@ describe('PanelPlugin', () => {
standardFieldConfigEditorRegistry.setInit(() => { standardFieldConfigEditorRegistry.setInit(() => {
return [ return [
{ {
id: 'min', id: FieldConfigProperty.Min,
path: 'min', path: 'min',
}, },
{ {
id: 'max', id: FieldConfigProperty.Max,
path: 'max', path: 'max',
}, },
] as any; ] as any;
...@@ -210,15 +210,15 @@ describe('PanelPlugin', () => { ...@@ -210,15 +210,15 @@ describe('PanelPlugin', () => {
expect(panel.fieldConfigRegistry.list()).toHaveLength(2); expect(panel.fieldConfigRegistry.list()).toHaveLength(2);
}); });
test('selected standard config', () => { test('disabling standard config properties', () => {
const panel = new PanelPlugin(() => { const panel = new PanelPlugin(() => {
return <div>Panel</div>; return <div>Panel</div>;
}); });
panel.useFieldConfig({ panel.useFieldConfig({
standardOptions: [FieldConfigProperty.Min, FieldConfigProperty.Max], disableStandardOptions: [FieldConfigProperty.Min],
}); });
expect(panel.fieldConfigRegistry.list()).toHaveLength(2); expect(panel.fieldConfigRegistry.list()).toHaveLength(1);
}); });
describe('default values', () => { describe('default values', () => {
...@@ -228,10 +228,9 @@ describe('PanelPlugin', () => { ...@@ -228,10 +228,9 @@ describe('PanelPlugin', () => {
}); });
panel.useFieldConfig({ panel.useFieldConfig({
standardOptions: [FieldConfigProperty.Max, FieldConfigProperty.Min], standardOptions: {
standardOptionsDefaults: { [FieldConfigProperty.Max]: { defaultValue: 20 },
[FieldConfigProperty.Max]: 20, [FieldConfigProperty.Min]: { defaultValue: 10 },
[FieldConfigProperty.Min]: 10,
}, },
}); });
...@@ -247,17 +246,16 @@ describe('PanelPlugin', () => { ...@@ -247,17 +246,16 @@ describe('PanelPlugin', () => {
}); });
}); });
it('should ignore defaults that are not specified as available properties', () => { it('should disable properties independently from the default values settings', () => {
const panel = new PanelPlugin(() => { const panel = new PanelPlugin(() => {
return <div>Panel</div>; return <div>Panel</div>;
}); });
panel.useFieldConfig({ panel.useFieldConfig({
standardOptions: [FieldConfigProperty.Max], standardOptions: {
standardOptionsDefaults: { [FieldConfigProperty.Max]: { defaultValue: 20 },
[FieldConfigProperty.Max]: 20,
[FieldConfigProperty.Min]: 10,
}, },
disableStandardOptions: [FieldConfigProperty.Min],
}); });
expect(panel.fieldConfigRegistry.list()).toHaveLength(1); expect(panel.fieldConfigRegistry.list()).toHaveLength(1);
......
...@@ -15,33 +15,38 @@ import set from 'lodash/set'; ...@@ -15,33 +15,38 @@ import set from 'lodash/set';
import { deprecationWarning } from '../utils'; import { deprecationWarning } from '../utils';
import { FieldConfigOptionsRegistry, standardFieldConfigEditorRegistry } from '../field'; import { FieldConfigOptionsRegistry, standardFieldConfigEditorRegistry } from '../field';
type StandardOptionConfig = {
defaultValue?: any;
settings?: any;
};
export interface SetFieldConfigOptionsArgs<TFieldConfigOptions = any> { export interface SetFieldConfigOptionsArgs<TFieldConfigOptions = any> {
/** /**
* Array of standard field config properties * Configuration object of the standard field config properites
* *
* @example * @example
* ```typescript * ```typescript
* { * {
* standardOptions: [FieldConfigProperty.Min, FieldConfigProperty.Max, FieldConfigProperty.Unit] * standardOptions: {
* [FieldConfigProperty.Decimals]: {
* defaultValue: 3
* }
* }
* } * }
* ``` * ```
*/ */
standardOptions?: FieldConfigProperty[]; standardOptions?: Partial<Record<FieldConfigProperty, StandardOptionConfig>>;
/** /**
* Object specifying standard option properties default values * Array of standard field config properties that should not be available in the panel
*
* @example * @example
* ```typescript * ```typescript
* { * {
* standardOptionsDefaults: { * disableStandardOptions: [FieldConfigProperty.Min, FieldConfigProperty.Max, FieldConfigProperty.Unit]
* [FieldConfigProperty.Min]: 20,
* [FieldConfigProperty.Max]: 100
* }
* } * }
* ``` * ```
*/ */
standardOptionsDefaults?: Partial<Record<FieldConfigProperty, any>>; disableStandardOptions?: FieldConfigProperty[];
/** /**
* Function that allows custom field config properties definition. * Function that allows custom field config properties definition.
...@@ -305,13 +310,13 @@ export class PanelPlugin<TOptions = any, TFieldConfigOptions extends object = an ...@@ -305,13 +310,13 @@ export class PanelPlugin<TOptions = any, TFieldConfigOptions extends object = an
* *
* @public * @public
*/ */
useFieldConfig(config?: SetFieldConfigOptionsArgs<TFieldConfigOptions>) { useFieldConfig(config: SetFieldConfigOptionsArgs<TFieldConfigOptions> = {}) {
// builder is applied lazily when custom field configs are accessed // builder is applied lazily when custom field configs are accessed
this._initConfigRegistry = () => { this._initConfigRegistry = () => {
const registry = new FieldConfigOptionsRegistry(); const registry = new FieldConfigOptionsRegistry();
// Add custom options // Add custom options
if (config && config.useCustomConfig) { if (config.useCustomConfig) {
const builder = new FieldConfigEditorBuilder<TFieldConfigOptions>(); const builder = new FieldConfigEditorBuilder<TFieldConfigOptions>();
config.useCustomConfig(builder); config.useCustomConfig(builder);
...@@ -326,20 +331,32 @@ export class PanelPlugin<TOptions = any, TFieldConfigOptions extends object = an ...@@ -326,20 +331,32 @@ export class PanelPlugin<TOptions = any, TFieldConfigOptions extends object = an
} }
} }
if (config && config.standardOptions) { for (let fieldConfigProp of standardFieldConfigEditorRegistry.list()) {
for (const standardOption of config.standardOptions) { if (config.disableStandardOptions) {
const standardEditor = standardFieldConfigEditorRegistry.get(standardOption); const isDisabled = config.disableStandardOptions.indexOf(fieldConfigProp.id as FieldConfigProperty) > -1;
registry.register({ if (isDisabled) {
...standardEditor, continue;
defaultValue: }
(config.standardOptionsDefaults && config.standardOptionsDefaults[standardOption]) ||
standardEditor.defaultValue,
});
} }
} else { if (config.standardOptions) {
for (const fieldConfigProp of standardFieldConfigEditorRegistry.list()) { const customDefault: any = config.standardOptions[fieldConfigProp.id as FieldConfigProperty]?.defaultValue;
registry.register(fieldConfigProp); const customSettings: any = config.standardOptions[fieldConfigProp.id as FieldConfigProperty]?.settings;
if (customDefault) {
fieldConfigProp = {
...fieldConfigProp,
defaultValue: customDefault,
};
}
if (customSettings) {
fieldConfigProp = {
...fieldConfigProp,
settings: fieldConfigProp.settings ? { ...fieldConfigProp.settings, ...customSettings } : customSettings,
};
}
} }
registry.register(fieldConfigProp);
} }
return registry; return registry;
......
...@@ -13,12 +13,10 @@ import { ...@@ -13,12 +13,10 @@ import {
StringFieldConfigSettings, StringFieldConfigSettings,
NumberFieldConfigSettings, NumberFieldConfigSettings,
SliderFieldConfigSettings, SliderFieldConfigSettings,
ColorFieldConfigSettings,
identityOverrideProcessor, identityOverrideProcessor,
UnitFieldConfigSettings, UnitFieldConfigSettings,
unitOverrideProcessor, unitOverrideProcessor,
} from '../field'; } from '../field';
import { FieldColor } from '../types';
/** /**
* Fluent API for declarative creation of field config option editors * Fluent API for declarative creation of field config option editors
...@@ -104,9 +102,7 @@ export class FieldConfigEditorBuilder<TOptions> extends OptionsUIRegistryBuilder ...@@ -104,9 +102,7 @@ export class FieldConfigEditorBuilder<TOptions> extends OptionsUIRegistryBuilder
}); });
} }
addColorPicker<TSettings = any>( addColorPicker<TSettings = any>(config: FieldConfigEditorConfig<TOptions, TSettings, string>) {
config: FieldConfigEditorConfig<TOptions, TSettings & ColorFieldConfigSettings, FieldColor>
) {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
id: config.path, id: config.path,
...@@ -203,9 +199,7 @@ export class PanelOptionsEditorBuilder<TOptions> extends OptionsUIRegistryBuilde ...@@ -203,9 +199,7 @@ export class PanelOptionsEditorBuilder<TOptions> extends OptionsUIRegistryBuilde
}); });
} }
addColorPicker<TSettings = any>( addColorPicker<TSettings = any>(config: PanelOptionsEditorConfig<TOptions, TSettings, string>): this {
config: PanelOptionsEditorConfig<TOptions, TSettings & ColorFieldConfigSettings, string>
): this {
return this.addCustomEditor({ return this.addCustomEditor({
...config, ...config,
id: config.path, id: config.path,
......
...@@ -545,7 +545,7 @@ export function getBarGradient(props: Props, maxSize: number): string { ...@@ -545,7 +545,7 @@ export function getBarGradient(props: Props, maxSize: number): string {
return gradient + ')'; return gradient + ')';
} }
if (mode.colors) { if (mode.isContinuous && mode.colors) {
const scheme = mode.colors.map(item => getColorForTheme(item, theme)); const scheme = mode.colors.map(item => getColorForTheme(item, theme));
for (let i = 0; i < scheme.length; i++) { for (let i = 0; i < scheme.length; i++) {
const color = scheme[i]; const color = scheme[i];
......
...@@ -28,12 +28,6 @@ export const ColorValueEditor: React.FC<Props> = ({ value, onChange }) => { ...@@ -28,12 +28,6 @@ export const ColorValueEditor: React.FC<Props> = ({ value, onChange }) => {
color={value ? getColorForTheme(value, theme) : theme.colors.formInputBorder} color={value ? getColorForTheme(value, theme) : theme.colors.formInputBorder}
/> />
</div> </div>
{/* <div className={styles.colorText} onClick={showColorPicker}>
{value ?? settings?.textWhenUndefined ?? 'Pick Color'}
</div>
{value && settings?.allowUndefined && (
<Icon className={styles.trashIcon} name="trash-alt" onClick={() => onChange(undefined)} />
)} */}
</div> </div>
); );
}} }}
......
...@@ -8,13 +8,14 @@ import { ...@@ -8,13 +8,14 @@ import {
FieldColorMode, FieldColorMode,
GrafanaTheme, GrafanaTheme,
getColorForTheme, getColorForTheme,
FieldColorConfigSettings,
} from '@grafana/data'; } from '@grafana/data';
import { Select } from '../Select/Select'; import { Select } from '../Select/Select';
import { ColorValueEditor } from './color'; import { ColorValueEditor } from './color';
import { useStyles, useTheme } from '../../themes/ThemeContext'; import { useStyles, useTheme } from '../../themes/ThemeContext';
import { css } from 'emotion'; import { css } from 'emotion';
export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | undefined, {}>> = ({ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | undefined, FieldColorConfigSettings>> = ({
value, value,
onChange, onChange,
item, item,
...@@ -22,7 +23,11 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde ...@@ -22,7 +23,11 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
const theme = useTheme(); const theme = useTheme();
const styles = useStyles(getStyles); const styles = useStyles(getStyles);
const options = fieldColorModeRegistry.list().map(mode => { const availableOptions = item.settings?.byValueSupport
? fieldColorModeRegistry.list()
: fieldColorModeRegistry.list().filter(m => !m.isByValue);
const options = availableOptions.map(mode => {
let suffix = mode.isByValue ? ' (by value)' : ''; let suffix = mode.isByValue ? ' (by value)' : '';
return { return {
......
...@@ -20,6 +20,7 @@ import { ...@@ -20,6 +20,7 @@ import {
identityOverrideProcessor, identityOverrideProcessor,
TimeZone, TimeZone,
FieldColor, FieldColor,
FieldColorConfigSettings,
} from '@grafana/data'; } from '@grafana/data';
import { Switch } from '../components/Switch/Switch'; import { Switch } from '../components/Switch/Switch';
...@@ -204,15 +205,18 @@ export const getStandardFieldConfigs = () => { ...@@ -204,15 +205,18 @@ export const getStandardFieldConfigs = () => {
getItemsCount: value => (value ? value.length : 0), getItemsCount: value => (value ? value.length : 0),
}; };
const color: FieldConfigPropertyItem<any, FieldColor | undefined, {}> = { const color: FieldConfigPropertyItem<any, FieldColor | undefined, FieldColorConfigSettings> = {
id: 'color', id: 'color',
path: 'color', path: 'color',
name: 'Color scheme', name: 'Color scheme',
description: 'Select palette, gradient or single color',
editor: standardEditorsRegistry.get('fieldColor').editor as any, editor: standardEditorsRegistry.get('fieldColor').editor as any,
override: standardEditorsRegistry.get('fieldColor').editor as any, override: standardEditorsRegistry.get('fieldColor').editor as any,
process: identityOverrideProcessor, process: identityOverrideProcessor,
shouldApply: () => true, shouldApply: () => true,
settings: {
byValueSupport: true,
preferThresholdsMode: true,
},
category, category,
}; };
......
...@@ -8,6 +8,8 @@ import { ...@@ -8,6 +8,8 @@ import {
standardFieldConfigEditorRegistry, standardFieldConfigEditorRegistry,
PanelData, PanelData,
DataSourceInstanceSettings, DataSourceInstanceSettings,
FieldColorModeId,
FieldColorConfigSettings,
} from '@grafana/data'; } from '@grafana/data';
import { ComponentClass } from 'react'; import { ComponentClass } from 'react';
import { PanelQueryRunner } from './PanelQueryRunner'; import { PanelQueryRunner } from './PanelQueryRunner';
...@@ -55,7 +57,20 @@ export const mockStandardProperties = () => { ...@@ -55,7 +57,20 @@ export const mockStandardProperties = () => {
shouldApply: () => true, shouldApply: () => true,
}; };
return [unit, decimals, boolean]; const fieldColor = {
id: 'color',
path: 'color',
name: 'color',
description: '',
// @ts-ignore
editor: () => null,
// @ts-ignore
override: () => null,
process: identityOverrideProcessor,
shouldApply: () => true,
};
return [unit, decimals, boolean, fieldColor];
}; };
standardFieldConfigEditorRegistry.setInit(() => mockStandardProperties()); standardFieldConfigEditorRegistry.setInit(() => mockStandardProperties());
...@@ -147,10 +162,13 @@ describe('PanelModel', () => { ...@@ -147,10 +162,13 @@ describe('PanelModel', () => {
}); });
panelPlugin.useFieldConfig({ panelPlugin.useFieldConfig({
standardOptions: [FieldConfigProperty.Unit, FieldConfigProperty.Decimals], standardOptions: {
standardOptionsDefaults: { [FieldConfigProperty.Unit]: {
[FieldConfigProperty.Unit]: 'flop', defaultValue: 'flop',
[FieldConfigProperty.Decimals]: 2, },
[FieldConfigProperty.Decimals]: {
defaultValue: 2,
},
}, },
}); });
model.pluginLoaded(panelPlugin); model.pluginLoaded(panelPlugin);
...@@ -239,6 +257,17 @@ describe('PanelModel', () => { ...@@ -239,6 +257,17 @@ describe('PanelModel', () => {
describe('when changing panel type', () => { describe('when changing panel type', () => {
beforeEach(() => { beforeEach(() => {
const newPlugin = getPanelPlugin({ id: 'graph' }); const newPlugin = getPanelPlugin({ id: 'graph' });
newPlugin.useFieldConfig({
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byThresholdsSupport: true,
},
},
},
});
newPlugin.setPanelOptions(builder => { newPlugin.setPanelOptions(builder => {
builder.addBooleanSwitch({ builder.addBooleanSwitch({
name: 'Show thresholds labels', name: 'Show thresholds labels',
...@@ -285,6 +314,66 @@ describe('PanelModel', () => { ...@@ -285,6 +314,66 @@ describe('PanelModel', () => {
}); });
}); });
describe('when changing panel type to one that does not support by value color mode', () => {
beforeEach(() => {
model.fieldConfig.defaults.color = { mode: FieldColorModeId.Thresholds };
const newPlugin = getPanelPlugin({ id: 'graph' });
newPlugin.useFieldConfig({
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byValueSupport: false,
},
},
},
});
model.editSourceId = 1001;
model.changePlugin(newPlugin);
model.alert = { id: 2 };
});
it('should change color mode', () => {
expect(model.fieldConfig.defaults.color.mode).toBe(FieldColorModeId.PaletteClassic);
});
});
describe('when changing panel type from one not supporting by value color mode to one that supports it', () => {
const prepareModel = (colorOptions?: FieldColorConfigSettings) => {
const newModel = new PanelModel(modelJson);
newModel.fieldConfig.defaults.color = { mode: FieldColorModeId.PaletteClassic };
const newPlugin = getPanelPlugin({ id: 'graph' });
newPlugin.useFieldConfig({
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byValueSupport: true,
...colorOptions,
},
},
},
});
newModel.editSourceId = 1001;
newModel.changePlugin(newPlugin);
newModel.alert = { id: 2 };
return newModel;
};
it('should keep supported mode', () => {
const testModel = prepareModel();
expect(testModel.fieldConfig.defaults.color!.mode).toBe(FieldColorModeId.PaletteClassic);
});
it('should change to thresholds mode when it prefers to', () => {
const testModel = prepareModel({ preferThresholdsMode: true });
expect(testModel.fieldConfig.defaults.color!.mode).toBe(FieldColorModeId.Thresholds);
});
});
describe('when changing to react panel from angular panel', () => { describe('when changing to react panel from angular panel', () => {
let panelQueryRunner: any; let panelQueryRunner: any;
......
...@@ -12,6 +12,10 @@ import { ...@@ -12,6 +12,10 @@ import {
DataQueryResponseData, DataQueryResponseData,
DataTransformerConfig, DataTransformerConfig,
eventFactory, eventFactory,
FieldColorConfigSettings,
FieldColorModeId,
fieldColorModeRegistry,
FieldConfigProperty,
FieldConfigSource, FieldConfigSource,
PanelEvents, PanelEvents,
PanelPlugin, PanelPlugin,
...@@ -322,7 +326,35 @@ export class PanelModel implements DataConfigSource { ...@@ -322,7 +326,35 @@ export class PanelModel implements DataConfigSource {
} }
}); });
this.fieldConfig = applyFieldConfigDefaults(this.fieldConfig, this.plugin!.fieldConfigDefaults); this.fieldConfig = applyFieldConfigDefaults(this.fieldConfig, plugin.fieldConfigDefaults);
this.validateFieldColorMode(plugin);
}
private validateFieldColorMode(plugin: PanelPlugin) {
// adjust to prefered field color setting if needed
const color = plugin.fieldConfigRegistry.getIfExists(FieldConfigProperty.Color);
if (color && color.settings) {
const colorSettings = color.settings as FieldColorConfigSettings;
const mode = fieldColorModeRegistry.getIfExists(this.fieldConfig.defaults.color?.mode);
// When no support fo value colors, use classic palette
if (!colorSettings.byValueSupport) {
if (!mode || mode.isByValue) {
this.fieldConfig.defaults.color = { mode: FieldColorModeId.PaletteClassic };
return;
}
}
// When supporting value colors and prefering thresholds, use Thresholds mode.
// Otherwise keep current mode
if (colorSettings.byValueSupport && colorSettings.preferThresholdsMode) {
if (!mode || !mode.isByValue) {
this.fieldConfig.defaults.color = { mode: FieldColorModeId.Thresholds };
return;
}
}
}
} }
pluginLoaded(plugin: PanelPlugin) { pluginLoaded(plugin: PanelPlugin) {
......
...@@ -12,11 +12,11 @@ import { axesEditorComponent } from './axes_editor'; ...@@ -12,11 +12,11 @@ import { axesEditorComponent } from './axes_editor';
import config from 'app/core/config'; import config from 'app/core/config';
import TimeSeries from 'app/core/time_series2'; import TimeSeries from 'app/core/time_series2';
import { getProcessedDataFrames } from 'app/features/dashboard/state/runRequest'; import { getProcessedDataFrames } from 'app/features/dashboard/state/runRequest';
import { PanelEvents, PanelPlugin, DataFrame, FieldConfigProperty, getColorForTheme } from '@grafana/data'; import { DataFrame, FieldConfigProperty, getColorForTheme, PanelEvents, PanelPlugin } from '@grafana/data';
import { GraphContextMenuCtrl } from './GraphContextMenuCtrl'; import { GraphContextMenuCtrl } from './GraphContextMenuCtrl';
import { graphPanelMigrationHandler } from './GraphMigrations'; import { graphPanelMigrationHandler } from './GraphMigrations';
import { DataWarning, GraphPanelOptions, GraphFieldConfig } from './types'; import { DataWarning, GraphFieldConfig, GraphPanelOptions } from './types';
import { auto } from 'angular'; import { auto } from 'angular';
import { AnnotationsSrv } from 'app/features/annotations/all'; import { AnnotationsSrv } from 'app/features/annotations/all';
...@@ -388,10 +388,14 @@ export class GraphCtrl extends MetricsPanelCtrl { ...@@ -388,10 +388,14 @@ export class GraphCtrl extends MetricsPanelCtrl {
// Use new react style configuration // Use new react style configuration
export const plugin = new PanelPlugin<GraphPanelOptions, GraphFieldConfig>(null) export const plugin = new PanelPlugin<GraphPanelOptions, GraphFieldConfig>(null)
.useFieldConfig({ .useFieldConfig({
standardOptions: [ disableStandardOptions: [
FieldConfigProperty.DisplayName, FieldConfigProperty.NoValue,
FieldConfigProperty.Unit, FieldConfigProperty.Thresholds,
FieldConfigProperty.Links, // previously saved as dataLinks on options FieldConfigProperty.Max,
FieldConfigProperty.Min,
FieldConfigProperty.Decimals,
FieldConfigProperty.Color,
FieldConfigProperty.Mappings,
], ],
}) })
.setMigrationHandler(graphPanelMigrationHandler); .setMigrationHandler(graphPanelMigrationHandler);
......
import { FieldConfigProperty, PanelPlugin } from '@grafana/data'; import { PanelPlugin } from '@grafana/data';
import { GraphPanel } from './GraphPanel'; import { GraphPanel } from './GraphPanel';
import { Options } from './types'; import { Options } from './types';
export const plugin = new PanelPlugin<Options>(GraphPanel) export const plugin = new PanelPlugin<Options>(GraphPanel).useFieldConfig().setPanelOptions(builder => {
.useFieldConfig({ standardOptions: [FieldConfigProperty.Unit, FieldConfigProperty.Decimals] }) builder
.setPanelOptions(builder => { .addBooleanSwitch({
builder path: 'graph.showBars',
.addBooleanSwitch({ name: 'Show bars',
path: 'graph.showBars', description: '',
name: 'Show bars', defaultValue: false,
description: '', })
defaultValue: false, .addBooleanSwitch({
}) path: 'graph.showLines',
.addBooleanSwitch({ name: 'Show lines',
path: 'graph.showLines', description: '',
name: 'Show lines', defaultValue: true,
description: '', })
defaultValue: true, .addBooleanSwitch({
}) path: 'graph.showPoints',
.addBooleanSwitch({ name: 'Show poins',
path: 'graph.showPoints', description: '',
name: 'Show poins', defaultValue: false,
description: '', })
defaultValue: false, .addBooleanSwitch({
}) path: 'legend.isVisible',
.addBooleanSwitch({ name: 'Show legend',
path: 'legend.isVisible', description: '',
name: 'Show legend', defaultValue: true,
description: '', })
defaultValue: true, .addBooleanSwitch({
}) path: 'legend.asTable',
.addBooleanSwitch({ name: 'Display legend as table',
path: 'legend.asTable', description: '',
name: 'Display legend as table', defaultValue: false,
description: '', })
defaultValue: false, .addRadio({
}) path: 'legend.placement',
.addRadio({ name: 'Legend placement',
path: 'legend.placement', description: '',
name: 'Legend placement', defaultValue: 'under',
description: '', settings: {
defaultValue: 'under', options: [
settings: { { value: 'under', label: 'Below graph' },
options: [ { value: 'right', label: 'Right to the graph' },
{ value: 'under', label: 'Below graph' }, ],
{ value: 'right', label: 'Right to the graph' }, },
], })
}, .addRadio({
}) path: 'tooltipOptions.mode',
.addRadio({ name: 'Tooltip mode',
path: 'tooltipOptions.mode', description: '',
name: 'Tooltip mode', defaultValue: 'single',
description: '', settings: {
defaultValue: 'single', options: [
settings: { { value: 'single', label: 'Single series' },
options: [ { value: 'multi', label: 'All series' },
{ value: 'single', label: 'Single series' }, ],
{ value: 'multi', label: 'All series' }, },
], });
}, });
});
});
import { FieldConfigProperty, PanelPlugin } from '@grafana/data'; import { FieldColorModeId, FieldConfigProperty, PanelPlugin } from '@grafana/data';
import { AxisSide, GraphCustomFieldConfig } from '@grafana/ui'; import { AxisSide, GraphCustomFieldConfig } from '@grafana/ui';
import { GraphPanel } from './GraphPanel'; import { GraphPanel } from './GraphPanel';
import { Options } from './types'; import { Options } from './types';
export const plugin = new PanelPlugin<Options, GraphCustomFieldConfig>(GraphPanel) export const plugin = new PanelPlugin<Options, GraphCustomFieldConfig>(GraphPanel)
.useFieldConfig({ .useFieldConfig({
standardOptions: [ standardOptions: {
// FieldConfigProperty.Min, [FieldConfigProperty.Color]: {
// FieldConfigProperty.Max, settings: {
FieldConfigProperty.Color, byValueSupport: false,
FieldConfigProperty.Unit, },
FieldConfigProperty.DisplayName, defaultValue: {
FieldConfigProperty.Decimals, mode: FieldColorModeId.PaletteClassic,
// NOT: FieldConfigProperty.Thresholds, },
FieldConfigProperty.Mappings, },
], },
useCustomConfig: builder => { useCustomConfig: builder => {
builder builder
.addBooleanSwitch({ .addBooleanSwitch({
......
import { sharedSingleStatMigrationHandler, BigValueTextMode } from '@grafana/ui'; import { BigValueTextMode, sharedSingleStatMigrationHandler } from '@grafana/ui';
import { PanelPlugin } from '@grafana/data'; import { PanelPlugin } from '@grafana/data';
import { StatPanelOptions, addStandardDataReduceOptions } from './types'; import { addStandardDataReduceOptions, StatPanelOptions } from './types';
import { StatPanel } from './StatPanel'; import { StatPanel } from './StatPanel';
import { statPanelChangedHandler } from './StatMigrations'; import { statPanelChangedHandler } from './StatMigrations';
......
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