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
This commit is contained in:
Torkel Ödegaard 2021-06-19 07:36:59 +02:00 committed by GitHub
parent c56c1d73ed
commit 9e53b44f35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 86 additions and 54 deletions

View File

@ -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', () => {

View File

@ -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<string, any>, // it's mutable
source: Record<string, any>,
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

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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],
};
}