From 9e53b44f35af326009d9bb22d2d7750b12266b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 19 Jun 2021 07:36:59 +0200 Subject: [PATCH] FieldOverrides: Support changing display name in override and have it be matched by later rule (#35893) * FieldOverrides: Support changing display name in override that can be matched by later rule * Fixed test --- .../src/field/fieldOverrides.test.ts | 29 ++++++- .../grafana-data/src/field/fieldOverrides.ts | 86 +++++++++---------- .../src/field/overrides/processors.ts | 10 +++ .../src/utils/tests/mockStandardProperties.ts | 4 +- .../grafana-ui/src/utils/standardEditors.tsx | 3 +- .../dashgrid/SeriesVisibilityConfigFactory.ts | 8 +- 6 files changed, 86 insertions(+), 54 deletions(-) diff --git a/packages/grafana-data/src/field/fieldOverrides.test.ts b/packages/grafana-data/src/field/fieldOverrides.test.ts index 1c0e0279120..d8762e5e1db 100644 --- a/packages/grafana-data/src/field/fieldOverrides.test.ts +++ b/packages/grafana-data/src/field/fieldOverrides.test.ts @@ -314,7 +314,7 @@ describe('applyFieldOverrides', () => { data.fields[1].getLinks!({ valueRowIndex: 0 }); expect(data.fields[1].config.decimals).toEqual(1); - expect(replaceVariablesCalls[0].__value.value.text).toEqual('100.0'); + expect(replaceVariablesCalls[3].__value.value.text).toEqual('100.0'); }); it('creates a deep clone of field config', () => { @@ -328,6 +328,33 @@ describe('applyFieldOverrides', () => { expect(data.fields[1].config).not.toBe(f0.fields[1].config); expect(data.fields[1].config.custom).not.toBe(f0.fields[1].config.custom); }); + + it('Can modify displayName and have it affect later overrides', () => { + const config: FieldConfigSource = { + defaults: {}, + overrides: [ + { + matcher: { id: FieldMatcherID.byName, options: 'value' }, + properties: [{ id: 'displayName', value: 'Kittens' }], + }, + { + matcher: { id: FieldMatcherID.byName, options: 'Kittens' }, + properties: [{ id: 'displayName', value: 'Kittens improved' }], + }, + ], + }; + + const data = applyFieldOverrides({ + data: [f0], // the frame + fieldConfig: config, + replaceVariables: (str: string) => str, + fieldConfigRegistry: customFieldRegistry, + theme: createTheme(), + })[0]; + + expect(data.fields[1].config.displayName).toBe('Kittens improved'); + expect(getFieldDisplayName(data.fields[1], data)).toBe('Kittens improved'); + }); }); describe('setFieldConfigDefaults', () => { diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index d98f72b165b..892b789d65a 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -28,7 +28,7 @@ import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; import { DataLinkBuiltInVars, locationUtil } from '../utils'; import { formattedValueToString } from '../valueFormats'; import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy'; -import { getFieldDisplayName, getFrameDisplayName } from './fieldState'; +import { getFrameDisplayName } from './fieldState'; import { getTimeField } from '../dataframe/processDataFrame'; import { mapInternalLinkToExplore } from '../utils/dataLinks'; import { getTemplateProxyForField } from './templateProxies'; @@ -97,33 +97,37 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra } } - return options.data.map((frame, index) => { + return options.data.map((originalFrame, index) => { // Need to define this new frame here as it's passed to the getLinkSupplier function inside the fields loop - const newFrame: DataFrame = { ...frame }; + const newFrame: DataFrame = { ...originalFrame }; + // Copy fields + newFrame.fields = newFrame.fields.map((field) => { + return { + ...field, + config: cloneDeep(field.config), + state: { + ...field.state, + }, + }; + }); const scopedVars: ScopedVars = { - __series: { text: 'Series', value: { name: getFrameDisplayName(frame, index) } }, // might be missing + __series: { text: 'Series', value: { name: getFrameDisplayName(newFrame, index) } }, // might be missing }; - const fields: Field[] = frame.fields.map((field) => { - // Config is mutable within this scope - const fieldScopedVars = { ...scopedVars }; - const displayName = getFieldDisplayName(field, frame, options.data); + for (const field of newFrame.fields) { + const config = field.config; - fieldScopedVars['__field'] = { - text: 'Field', - value: getTemplateProxyForField(field, frame, options.data), + field.state!.scopedVars = { + ...scopedVars, + __field: { + text: 'Field', + value: getTemplateProxyForField(field, newFrame, options.data), + }, }; - field.state = { - ...field.state, - scopedVars: fieldScopedVars, - displayName, - }; - - const config: FieldConfig = { ...cloneDeep(field.config) }; const context = { - field, + field: field, data: options.data!, dataFrameIndex: index, replaceVariables: options.replaceVariables, @@ -133,9 +137,10 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra // Anything in the field config that's not set by the datasource // will be filled in by panel's field configuration setFieldConfigDefaults(config, source.defaults, context); + // Find any matching rules and then override for (const rule of override) { - if (rule.match(field, frame, options.data!)) { + if (rule.match(field, newFrame, options.data!)) { for (const prop of rule.properties) { // config.scopedVars is set already here setDynamicConfigValue(config, prop, context); @@ -169,44 +174,32 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra seriesIndex++; } - // Overwrite the configs - const newField: Field = { - ...field, - config, - type, - state: { - ...field.state, - displayName: null, - seriesIndex, - range, - }, - }; + field.state!.seriesIndex = seriesIndex; + field.state!.range = range; + field.type = type; // and set the display processor using it - newField.display = getDisplayProcessor({ - field: newField, + field.display = getDisplayProcessor({ + field: field, theme: options.theme, timeZone: options.timeZone, }); // Wrap the display with a cache to avoid double calls - if (newField.config.unit !== 'dateTimeFromNow') { - newField.display = cachingDisplayProcessor(newField.display, 2500); + if (field.config.unit !== 'dateTimeFromNow') { + field.display = cachingDisplayProcessor(field.display, 2500); } // Attach data links supplier - newField.getLinks = getLinksSupplier( + field.getLinks = getLinksSupplier( newFrame, - newField, - fieldScopedVars, + field, + field.state!.scopedVars, context.replaceVariables, options.timeZone ); + } - return newField; - }); - - newFrame.fields = fields; return newFrame; }); } @@ -236,6 +229,7 @@ export interface FieldOverrideEnv extends FieldOverrideContext { export function setDynamicConfigValue(config: FieldConfig, value: DynamicConfigValue, context: FieldOverrideEnv) { const reg = context.fieldConfigRegistry; const item = reg.getIfExists(value.id); + if (!item) { return; } @@ -280,12 +274,12 @@ export function setFieldConfigDefaults(config: FieldConfig, defaults: FieldConfi validateFieldConfig(config); } -const processFieldConfigValue = ( +function processFieldConfigValue( destination: Record, // it's mutable source: Record, fieldConfigProperty: FieldConfigPropertyItem, context: FieldOverrideEnv -) => { +) { const currentConfig = get(destination, fieldConfigProperty.path); if (currentConfig === null || currentConfig === undefined) { const item = context.fieldConfigRegistry.getIfExists(fieldConfigProperty.id); @@ -300,7 +294,7 @@ const processFieldConfigValue = ( } } } -}; +} /** * This checks that all options on FieldConfig make sense. It mutates any value that needs diff --git a/packages/grafana-data/src/field/overrides/processors.ts b/packages/grafana-data/src/field/overrides/processors.ts index 0a36bfcf0e8..a3419affaa1 100644 --- a/packages/grafana-data/src/field/overrides/processors.ts +++ b/packages/grafana-data/src/field/overrides/processors.ts @@ -24,6 +24,16 @@ export const numberOverrideProcessor = ( return parseFloat(value); }; +export const displayNameOverrideProcessor = ( + value: any, + context: FieldOverrideContext, + settings?: StringFieldConfigSettings +) => { + // clear the cached display name + delete context.field?.state?.displayName; + return stringOverrideProcessor(value, context, settings); +}; + export interface SliderFieldConfigSettings { min: number; max: number; diff --git a/packages/grafana-data/src/utils/tests/mockStandardProperties.ts b/packages/grafana-data/src/utils/tests/mockStandardProperties.ts index 0f452c5668d..85ad66e660f 100644 --- a/packages/grafana-data/src/utils/tests/mockStandardProperties.ts +++ b/packages/grafana-data/src/utils/tests/mockStandardProperties.ts @@ -1,4 +1,4 @@ -import { identityOverrideProcessor } from '../../field'; +import { displayNameOverrideProcessor, identityOverrideProcessor } from '../../field'; import { ThresholdsMode } from '../../types'; export const mockStandardProperties = () => { @@ -9,7 +9,7 @@ export const mockStandardProperties = () => { description: "Field's display name", editor: () => null, override: () => null, - process: identityOverrideProcessor, + process: displayNameOverrideProcessor, settings: { placeholder: 'none', expandTemplateVars: true, diff --git a/packages/grafana-ui/src/utils/standardEditors.tsx b/packages/grafana-ui/src/utils/standardEditors.tsx index 891314b344f..a180a708673 100644 --- a/packages/grafana-ui/src/utils/standardEditors.tsx +++ b/packages/grafana-ui/src/utils/standardEditors.tsx @@ -22,6 +22,7 @@ import { FieldColor, FieldColorConfigSettings, StatsPickerConfigSettings, + displayNameOverrideProcessor, } from '@grafana/data'; import { Switch } from '../components/Switch/Switch'; @@ -55,7 +56,7 @@ export const getStandardFieldConfigs = () => { description: 'Change the field or series name', editor: standardEditorsRegistry.get('text').editor as any, override: standardEditorsRegistry.get('text').editor as any, - process: stringOverrideProcessor, + process: displayNameOverrideProcessor, settings: { placeholder: 'none', expandTemplateVars: true, diff --git a/public/app/features/dashboard/dashgrid/SeriesVisibilityConfigFactory.ts b/public/app/features/dashboard/dashgrid/SeriesVisibilityConfigFactory.ts index 12c8ad2ce9d..f2bee65df82 100644 --- a/public/app/features/dashboard/dashgrid/SeriesVisibilityConfigFactory.ts +++ b/public/app/features/dashboard/dashgrid/SeriesVisibilityConfigFactory.ts @@ -31,7 +31,7 @@ export function seriesVisibilityConfigFactory( return { ...fieldConfig, - overrides: [override, ...fieldConfig.overrides], + overrides: [...fieldConfig.overrides, override], }; } @@ -40,7 +40,7 @@ export function seriesVisibilityConfigFactory( return { ...fieldConfig, - overrides: [override, ...fieldConfig.overrides], + overrides: [...fieldConfig.overrides, override], }; } @@ -61,7 +61,7 @@ export function seriesVisibilityConfigFactory( return { ...fieldConfig, - overrides: [override, ...overridesCopy], + overrides: [...overridesCopy, override], }; } @@ -76,7 +76,7 @@ export function seriesVisibilityConfigFactory( return { ...fieldConfig, - overrides: [override, ...overridesCopy], + overrides: [...overridesCopy, override], }; }