From 9d858220ce6193baf2f131708d1c2c26691b8a94 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 9 Mar 2020 15:09:32 +0100 Subject: [PATCH] FieldOverrides: Add value mappings editor to standard config properties registry (#22648) --- .../src/field/fieldDisplay.test.ts | 69 +++++++++++++++++++ .../grafana-data/src/field/fieldOverrides.ts | 8 ++- .../src/components/FieldConfigs/mappings.tsx | 44 ++++++++++++ .../standardFieldConfigEditors.tsx | 25 ++++++- 4 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 packages/grafana-ui/src/components/FieldConfigs/mappings.tsx diff --git a/packages/grafana-data/src/field/fieldDisplay.test.ts b/packages/grafana-data/src/field/fieldDisplay.test.ts index 2694ab24d1e..d274c688e3f 100644 --- a/packages/grafana-data/src/field/fieldDisplay.test.ts +++ b/packages/grafana-data/src/field/fieldDisplay.test.ts @@ -6,8 +6,25 @@ import { ThresholdsMode } from '../types/thresholds'; import { GrafanaTheme } from '../types/theme'; import { MappingType, FieldConfig } from '../types'; import { validateFieldConfig } from './fieldOverrides'; +import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry'; describe('FieldDisplay', () => { + beforeAll(() => { + // Since FieldConfigEditors belong to grafana-ui we need to mock those here + // as grafana-ui code cannot be imported in grafana-data. + // TODO: figure out a way to share standard editors between data/ui tests + const mappings = { + id: 'mappings', // Match field properties + process: (value: any) => value, + shouldApply: () => true, + } as any; + + console.log('Init tegistry'); + standardFieldConfigEditorRegistry.setInit(() => { + return [mappings]; + }); + }); + it('show first numeric values', () => { const options = createDisplayOptions({ fieldOptions: { @@ -148,6 +165,58 @@ describe('FieldDisplay', () => { const display = getFieldDisplayValues(options); expect(display[0].display.numeric).toEqual(0); }); + + describe('Value mapping', () => { + it('should apply value mapping', () => { + const options = createDisplayOptions({ + fieldOptions: { + calcs: [ReducerID.first], + override: {}, + defaults: { + mappings: [ + { + id: 1, + operator: '', + text: 'Value mapped to text', + type: MappingType.ValueToText, + value: 1, + }, + ], + }, + }, + }); + + const result = getFieldDisplayValues(options); + expect(result[0].display.text).toEqual('Value mapped to text'); + }); + it('should apply range value mapping', () => { + const mappedValue = 'Range mapped to text'; + const options = createDisplayOptions({ + fieldOptions: { + values: true, + override: {}, + defaults: { + mappings: [ + { + id: 1, + operator: '', + text: mappedValue, + type: MappingType.RangeToText, + value: 1, + from: 1, + to: 3, + }, + ], + }, + }, + }); + const result = getFieldDisplayValues(options); + + expect(result[0].display.text).toEqual(mappedValue); + expect(result[2].display.text).toEqual('5'); + expect(result[3].display.text).toEqual(mappedValue); + }); + }); }); function createEmptyDisplayOptions(extend = {}): GetFieldDisplayValuesOptions { diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index af2c58500f7..134158a33ec 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -136,8 +136,6 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra } } - // console.log(config) - // Try harder to set a real value that is not 'other' let type = field.type; if (!type || type === FieldType.other) { @@ -242,7 +240,6 @@ function setDynamicConfigValue(config: FieldConfig, value: DynamicConfigValue, c export function setFieldConfigDefaults(config: FieldConfig, defaults: FieldConfig, context: FieldOverrideEnv) { if (defaults) { const keys = Object.keys(defaults); - for (const key of keys) { if (key === 'custom') { if (!context.custom) { @@ -275,6 +272,11 @@ const processFieldConfigValue = ( const currentConfig = destination[key]; if (currentConfig === null || currentConfig === undefined) { const item = registry.getIfExists(key); + if (!item) { + console.warn(`No processor available for ${key} config property`); + return; + } + if (item && item.shouldApply(context.field!)) { const val = item.process(source[key], context, item.settings); if (val !== undefined && val !== null) { diff --git a/packages/grafana-ui/src/components/FieldConfigs/mappings.tsx b/packages/grafana-ui/src/components/FieldConfigs/mappings.tsx new file mode 100644 index 00000000000..79b79816f33 --- /dev/null +++ b/packages/grafana-ui/src/components/FieldConfigs/mappings.tsx @@ -0,0 +1,44 @@ +import React from 'react'; + +import { FieldOverrideContext, FieldOverrideEditorProps, FieldConfigEditorProps, ValueMapping } from '@grafana/data'; +import { ValueMappingsEditor } from '..'; + +export interface ValueMappingFieldConfigSettings {} + +export const valueMappingsOverrideProcessor = ( + value: any, + context: FieldOverrideContext, + settings: ValueMappingFieldConfigSettings +) => { + return value as ValueMapping[]; // !!!! likely not !!!! +}; + +export class ValueMappingsValueEditor extends React.PureComponent< + FieldConfigEditorProps +> { + constructor(props: FieldConfigEditorProps) { + super(props); + } + + render() { + const { onChange } = this.props; + let value = this.props.value; + if (!value) { + value = []; + } + + return ; + } +} + +export class ValueMappingsOverrideEditor extends React.PureComponent< + FieldOverrideEditorProps +> { + constructor(props: FieldOverrideEditorProps) { + super(props); + } + + render() { + return
VALUE MAPPINGS OVERRIDE EDITOR {this.props.item.name}
; + } +} diff --git a/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditors.tsx b/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditors.tsx index a57d54ff024..9064915e9e5 100644 --- a/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditors.tsx +++ b/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditors.tsx @@ -1,4 +1,4 @@ -import { DataLink, FieldPropertyEditorItem, FieldType, ThresholdsConfig } from '@grafana/data'; +import { DataLink, FieldPropertyEditorItem, FieldType, ThresholdsConfig, ValueMapping } from '@grafana/data'; import { StringFieldConfigSettings, StringOverrideEditor, stringOverrideProcessor, StringValueEditor } from './string'; import { NumberFieldConfigSettings, NumberOverrideEditor, numberOverrideProcessor, NumberValueEditor } from './number'; import { UnitOverrideEditor, UnitValueEditor } from './units'; @@ -9,6 +9,12 @@ import { ThresholdsValueEditor, } from './thresholds'; import { DataLinksOverrideEditor, dataLinksOverrideProcessor, DataLinksValueEditor } from './links'; +import { + ValueMappingFieldConfigSettings, + ValueMappingsOverrideEditor, + valueMappingsOverrideProcessor, + ValueMappingsValueEditor, +} from './mappings'; export const getStandardFieldConfigs = () => { const title: FieldPropertyEditorItem = { @@ -108,6 +114,21 @@ export const getStandardFieldConfigs = () => { shouldApply: field => field.type === FieldType.number, }; + const mappings: FieldPropertyEditorItem = { + id: 'mappings', // Match field properties + name: 'Value mappings', + description: 'Manage value mappings', + + editor: ValueMappingsValueEditor, + override: ValueMappingsOverrideEditor, + process: valueMappingsOverrideProcessor, + settings: { + // ?? + }, + + shouldApply: field => field.type === FieldType.number, + }; + const noValue: FieldPropertyEditorItem = { id: 'noValue', // Match field properties name: 'No Value', @@ -137,5 +158,5 @@ export const getStandardFieldConfigs = () => { shouldApply: () => true, }; - return [unit, min, max, decimals, thresholds, title, noValue, links]; + return [unit, min, max, decimals, thresholds, mappings, title, noValue, links]; };