mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
FieldConfig: support overrides model (#20986)
This commit is contained in:
parent
1f73e2aadf
commit
1aa39ee458
@ -1,39 +1,13 @@
|
||||
import merge from 'lodash/merge';
|
||||
import { getFieldProperties, getFieldDisplayValues, GetFieldDisplayValuesOptions } from './fieldDisplay';
|
||||
import { getFieldDisplayValues, GetFieldDisplayValuesOptions } from './fieldDisplay';
|
||||
import { toDataFrame } from '../dataframe/processDataFrame';
|
||||
import { ReducerID } from '../transformations/fieldReducer';
|
||||
import { Threshold } from '../types/threshold';
|
||||
import { GrafanaTheme } from '../types/theme';
|
||||
import { MappingType } from '../types';
|
||||
import { setFieldConfigDefaults } from './fieldOverrides';
|
||||
|
||||
describe('FieldDisplay', () => {
|
||||
it('Construct simple field properties', () => {
|
||||
const f0 = {
|
||||
min: 0,
|
||||
max: 100,
|
||||
};
|
||||
const f1 = {
|
||||
unit: 'ms',
|
||||
dateFormat: '', // should be ignored
|
||||
max: parseFloat('NOPE'), // should be ignored
|
||||
min: null,
|
||||
};
|
||||
let field = getFieldProperties(f0, f1);
|
||||
expect(field.min).toEqual(0);
|
||||
expect(field.max).toEqual(100);
|
||||
expect(field.unit).toEqual('ms');
|
||||
|
||||
// last one overrieds
|
||||
const f2 = {
|
||||
unit: 'none', // ignore 'none'
|
||||
max: -100, // lower than min! should flip min/max
|
||||
};
|
||||
field = getFieldProperties(f0, f1, f2);
|
||||
expect(field.max).toEqual(0);
|
||||
expect(field.min).toEqual(-100);
|
||||
expect(field.unit).toEqual('ms');
|
||||
});
|
||||
|
||||
it('show first numeric values', () => {
|
||||
const options = createDisplayOptions({
|
||||
fieldOptions: {
|
||||
@ -89,7 +63,7 @@ describe('FieldDisplay', () => {
|
||||
});
|
||||
|
||||
it('should restore -Infinity value for base threshold', () => {
|
||||
const field = getFieldProperties({
|
||||
const field = {
|
||||
thresholds: [
|
||||
({
|
||||
color: '#73BF69',
|
||||
@ -100,7 +74,8 @@ describe('FieldDisplay', () => {
|
||||
value: 50,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
setFieldConfigDefaults(field);
|
||||
expect(field.thresholds!.length).toEqual(2);
|
||||
expect(field.thresholds![0].value).toBe(-Infinity);
|
||||
});
|
||||
@ -130,7 +105,7 @@ describe('FieldDisplay', () => {
|
||||
const mapEmptyToText = '0';
|
||||
const options = createEmptyDisplayOptions({
|
||||
fieldOptions: {
|
||||
override: {
|
||||
defaults: {
|
||||
mappings: [
|
||||
{
|
||||
id: 1,
|
||||
@ -203,8 +178,8 @@ function createDisplayOptions(extend = {}): GetFieldDisplayValuesOptions {
|
||||
},
|
||||
fieldOptions: {
|
||||
calcs: [],
|
||||
override: {},
|
||||
defaults: {},
|
||||
overrides: [],
|
||||
},
|
||||
theme: {} as GrafanaTheme,
|
||||
};
|
||||
|
@ -1,26 +1,29 @@
|
||||
import toNumber from 'lodash/toNumber';
|
||||
import toString from 'lodash/toString';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
import { getDisplayProcessor } from './displayProcessor';
|
||||
import { getFlotPairs } from '../utils/flotPairs';
|
||||
import { FieldConfig, DataFrame, FieldType } from '../types/dataFrame';
|
||||
import { InterpolateFunction } from '../types/panel';
|
||||
import {
|
||||
FieldConfig,
|
||||
DataFrame,
|
||||
FieldType,
|
||||
DisplayValue,
|
||||
DisplayValueAlignmentFactors,
|
||||
FieldConfigSource,
|
||||
InterpolateFunction,
|
||||
} from '../types';
|
||||
import { DataFrameView } from '../dataframe/DataFrameView';
|
||||
import { GraphSeriesValue } from '../types/graph';
|
||||
import { DisplayValue, DisplayValueAlignmentFactors } from '../types/displayValue';
|
||||
import { GrafanaTheme } from '../types/theme';
|
||||
import { ReducerID, reduceField } from '../transformations/fieldReducer';
|
||||
import { ScopedVars } from '../types/ScopedVars';
|
||||
import { getTimeField } from '../dataframe/processDataFrame';
|
||||
import { applyFieldOverrides } from './fieldOverrides';
|
||||
|
||||
export interface FieldDisplayOptions {
|
||||
export interface FieldDisplayOptions extends FieldConfigSource {
|
||||
values?: boolean; // If true show each row value
|
||||
limit?: number; // if showing all values limit
|
||||
calcs: string[]; // when !values, pick one value for the whole field
|
||||
|
||||
defaults: FieldConfig; // Use these values unless otherwise stated
|
||||
override: FieldConfig; // Set these values regardless of the source
|
||||
}
|
||||
|
||||
// TODO: use built in variables, same as for data links?
|
||||
@ -81,27 +84,21 @@ export interface GetFieldDisplayValuesOptions {
|
||||
export const DEFAULT_FIELD_DISPLAY_VALUES_LIMIT = 25;
|
||||
|
||||
export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): FieldDisplay[] => {
|
||||
const { data, replaceVariables, fieldOptions } = options;
|
||||
const { defaults, override } = fieldOptions;
|
||||
const { replaceVariables, fieldOptions } = options;
|
||||
const calcs = fieldOptions.calcs.length ? fieldOptions.calcs : [ReducerID.last];
|
||||
|
||||
const values: FieldDisplay[] = [];
|
||||
|
||||
if (data) {
|
||||
if (options.data) {
|
||||
const data = applyFieldOverrides(options.data, fieldOptions, replaceVariables, options.theme);
|
||||
|
||||
let hitLimit = false;
|
||||
const limit = fieldOptions.limit ? fieldOptions.limit : DEFAULT_FIELD_DISPLAY_VALUES_LIMIT;
|
||||
const defaultTitle = getTitleTemplate(fieldOptions.defaults.title, calcs, data);
|
||||
const scopedVars: ScopedVars = {};
|
||||
|
||||
for (let s = 0; s < data.length && !hitLimit; s++) {
|
||||
let series = data[s];
|
||||
if (!series.name) {
|
||||
series = {
|
||||
...series,
|
||||
name: series.refId ? series.refId : `Series[${s}]`,
|
||||
};
|
||||
}
|
||||
|
||||
const series = data[s]; // Name is already set
|
||||
scopedVars['__series'] = { text: 'Series', value: { name: series.name } };
|
||||
|
||||
const { timeField } = getTimeField(series);
|
||||
@ -114,7 +111,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
|
||||
if (field.type !== FieldType.number) {
|
||||
continue;
|
||||
}
|
||||
const config = getFieldProperties(defaults, field.config || {}, override);
|
||||
const config = field.config; // already set by the prepare task
|
||||
|
||||
let name = field.name;
|
||||
if (!name) {
|
||||
@ -123,7 +120,9 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
|
||||
|
||||
scopedVars['__field'] = { text: 'Field', value: { name } };
|
||||
|
||||
const display = getDisplayProcessor({
|
||||
const display =
|
||||
field.display ??
|
||||
getDisplayProcessor({
|
||||
config,
|
||||
theme: options.theme,
|
||||
type: field.type,
|
||||
@ -206,72 +205,6 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
|
||||
return values;
|
||||
};
|
||||
|
||||
const numericFieldProps: any = {
|
||||
decimals: true,
|
||||
min: true,
|
||||
max: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a version of the field with the overries applied. Any property with
|
||||
* value: null | undefined | empty string are skipped.
|
||||
*
|
||||
* For numeric values, only valid numbers will be applied
|
||||
* for units, 'none' will be skipped
|
||||
*/
|
||||
export function applyFieldProperties(field: FieldConfig, props?: FieldConfig): FieldConfig {
|
||||
if (!props) {
|
||||
return field;
|
||||
}
|
||||
const keys = Object.keys(props);
|
||||
if (!keys.length) {
|
||||
return field;
|
||||
}
|
||||
const copy = { ...field } as any; // make a copy that we will manipulate directly
|
||||
for (const key of keys) {
|
||||
const val = (props as any)[key];
|
||||
if (val === null || val === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (numericFieldProps[key]) {
|
||||
const num = toNumber(val);
|
||||
if (!isNaN(num)) {
|
||||
copy[key] = num;
|
||||
}
|
||||
} else if (val) {
|
||||
// skips empty string
|
||||
if (key === 'unit' && val === 'none') {
|
||||
continue;
|
||||
}
|
||||
copy[key] = val;
|
||||
}
|
||||
}
|
||||
return copy as FieldConfig;
|
||||
}
|
||||
|
||||
export function getFieldProperties(...props: FieldConfig[]): FieldConfig {
|
||||
let field = props[0] as FieldConfig;
|
||||
for (let i = 1; i < props.length; i++) {
|
||||
field = applyFieldProperties(field, props[i]);
|
||||
}
|
||||
|
||||
// First value is always -Infinity
|
||||
if (field.thresholds && field.thresholds.length) {
|
||||
field.thresholds[0].value = -Infinity;
|
||||
}
|
||||
|
||||
// Verify that max > min
|
||||
if (field.hasOwnProperty('min') && field.hasOwnProperty('max') && field.min! > field.max!) {
|
||||
return {
|
||||
...field,
|
||||
min: field.max,
|
||||
max: field.min,
|
||||
};
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
export function getDisplayValueAlignmentFactors(values: FieldDisplay[]): DisplayValueAlignmentFactors {
|
||||
const info: DisplayValueAlignmentFactors = {
|
||||
title: '',
|
||||
@ -308,11 +241,10 @@ export function getDisplayValueAlignmentFactors(values: FieldDisplay[]): Display
|
||||
function createNoValuesFieldDisplay(options: GetFieldDisplayValuesOptions): FieldDisplay {
|
||||
const displayName = 'No data';
|
||||
const { fieldOptions } = options;
|
||||
const { defaults, override } = fieldOptions;
|
||||
const { defaults } = fieldOptions;
|
||||
|
||||
const config = getFieldProperties(defaults, {}, override);
|
||||
const displayProcessor = getDisplayProcessor({
|
||||
config,
|
||||
config: defaults,
|
||||
theme: options.theme,
|
||||
type: FieldType.other,
|
||||
});
|
||||
|
89
packages/grafana-data/src/field/fieldOverrides.test.ts
Normal file
89
packages/grafana-data/src/field/fieldOverrides.test.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import { setFieldConfigDefaults, findNumericFieldMinMax, applyFieldOverrides } from './fieldOverrides';
|
||||
import { MutableDataFrame } from '../dataframe';
|
||||
import { FieldConfig, FieldConfigSource, InterpolateFunction, GrafanaTheme } from '../types';
|
||||
import { FieldMatcherID } from '../transformations';
|
||||
|
||||
describe('FieldOverrides', () => {
|
||||
it('will merge FieldConfig with default values', () => {
|
||||
const field: FieldConfig = {
|
||||
min: 0,
|
||||
max: 100,
|
||||
};
|
||||
const f1 = {
|
||||
unit: 'ms',
|
||||
dateFormat: '', // should be ignored
|
||||
max: parseFloat('NOPE'), // should be ignored
|
||||
min: null, // should alo be ignored!
|
||||
};
|
||||
setFieldConfigDefaults(field, f1 as FieldConfig);
|
||||
expect(field.min).toEqual(0);
|
||||
expect(field.max).toEqual(100);
|
||||
expect(field.unit).toEqual('ms');
|
||||
});
|
||||
|
||||
it('will apply field overrides', () => {
|
||||
const f0 = new MutableDataFrame();
|
||||
f0.add({ title: 'AAA', value: 100, value2: 1234 }, true);
|
||||
f0.add({ title: 'BBB', value: -20 }, true);
|
||||
f0.add({ title: 'CCC', value: 200, value2: 1000 }, true);
|
||||
expect(f0.length).toEqual(3);
|
||||
|
||||
// Hardcode the max value
|
||||
f0.fields[1].config.max = 0;
|
||||
f0.fields[1].config.decimals = 6;
|
||||
|
||||
const src: FieldConfigSource = {
|
||||
defaults: {
|
||||
unit: 'xyz',
|
||||
decimals: 2,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
matcher: { id: FieldMatcherID.numeric },
|
||||
properties: [
|
||||
{ path: 'decimals', value: 1 }, // Numeric
|
||||
{ path: 'title', value: 'Kittens' }, // Text
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const data = applyFieldOverrides(
|
||||
[f0], // the frame
|
||||
src, // defaults + overrides
|
||||
(undefined as any) as InterpolateFunction,
|
||||
(undefined as any) as GrafanaTheme
|
||||
)[0];
|
||||
const valueColumn = data.fields[1];
|
||||
const config = valueColumn.config;
|
||||
|
||||
// Keep max from the original setting
|
||||
expect(config.max).toEqual(0);
|
||||
|
||||
// Automatically pick the min value
|
||||
expect(config.min).toEqual(-20);
|
||||
|
||||
// The default value applied
|
||||
expect(config.unit).toEqual('xyz');
|
||||
|
||||
// The default value applied
|
||||
expect(config.title).toEqual('Kittens');
|
||||
|
||||
// The override applied
|
||||
expect(config.decimals).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Global MinMax', () => {
|
||||
it('find global min max', () => {
|
||||
const f0 = new MutableDataFrame();
|
||||
f0.add({ title: 'AAA', value: 100, value2: 1234 }, true);
|
||||
f0.add({ title: 'BBB', value: -20 }, true);
|
||||
f0.add({ title: 'CCC', value: 200, value2: 1000 }, true);
|
||||
expect(f0.length).toEqual(3);
|
||||
|
||||
const minmax = findNumericFieldMinMax([f0]);
|
||||
expect(minmax.min).toEqual(-20);
|
||||
expect(minmax.max).toEqual(1234);
|
||||
});
|
||||
});
|
213
packages/grafana-data/src/field/fieldOverrides.ts
Normal file
213
packages/grafana-data/src/field/fieldOverrides.ts
Normal file
@ -0,0 +1,213 @@
|
||||
import set from 'lodash/set';
|
||||
import {
|
||||
DynamicConfigValue,
|
||||
FieldConfigSource,
|
||||
FieldConfig,
|
||||
InterpolateFunction,
|
||||
GrafanaTheme,
|
||||
DataFrame,
|
||||
Field,
|
||||
FieldType,
|
||||
} from '../types';
|
||||
import { fieldMatchers, ReducerID, reduceField } from '../transformations';
|
||||
import { FieldMatcher } from '../types/transformations';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import toNumber from 'lodash/toNumber';
|
||||
import { getDisplayProcessor } from './displayProcessor';
|
||||
|
||||
interface OverrideProps {
|
||||
match: FieldMatcher;
|
||||
properties: DynamicConfigValue[];
|
||||
}
|
||||
|
||||
interface GlobalMinMax {
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
export function findNumericFieldMinMax(data: DataFrame[]): GlobalMinMax {
|
||||
let min = Number.MAX_VALUE;
|
||||
let max = Number.MIN_VALUE;
|
||||
|
||||
const reducers = [ReducerID.min, ReducerID.max];
|
||||
for (const frame of data) {
|
||||
for (const field of frame.fields) {
|
||||
if (field.type === FieldType.number) {
|
||||
const stats = reduceField({ field, reducers });
|
||||
if (stats[ReducerID.min] < min) {
|
||||
min = stats[ReducerID.min];
|
||||
}
|
||||
if (stats[ReducerID.max] > max) {
|
||||
max = stats[ReducerID.max];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { min, max };
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of the DataFrame with all rules applied
|
||||
*/
|
||||
export function applyFieldOverrides(
|
||||
data: DataFrame[],
|
||||
source: FieldConfigSource,
|
||||
replaceVariables: InterpolateFunction,
|
||||
theme: GrafanaTheme,
|
||||
isUtc?: boolean
|
||||
): DataFrame[] {
|
||||
if (!source) {
|
||||
return data;
|
||||
}
|
||||
let range: GlobalMinMax | undefined = undefined;
|
||||
|
||||
// Prepare the Matchers
|
||||
const override: OverrideProps[] = [];
|
||||
if (source.overrides) {
|
||||
for (const rule of source.overrides) {
|
||||
const info = fieldMatchers.get(rule.matcher.id);
|
||||
if (info) {
|
||||
override.push({
|
||||
match: info.get(rule.matcher),
|
||||
properties: rule.properties,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data.map((frame, index) => {
|
||||
let name = frame.name;
|
||||
if (!name) {
|
||||
name = `Series[${index}]`;
|
||||
}
|
||||
|
||||
const fields = frame.fields.map(field => {
|
||||
// Config is mutable within this scope
|
||||
const config: FieldConfig = { ...field.config } || {};
|
||||
if (field.type === FieldType.number) {
|
||||
setFieldConfigDefaults(config, source.defaults);
|
||||
}
|
||||
|
||||
// Find any matching rules and then override
|
||||
for (const rule of override) {
|
||||
if (rule.match(field)) {
|
||||
for (const prop of rule.properties) {
|
||||
setDynamicConfigValue(config, {
|
||||
value: prop,
|
||||
config,
|
||||
field,
|
||||
data: frame,
|
||||
replaceVariables,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the Min/Max value automatically
|
||||
if (field.type === FieldType.number) {
|
||||
if (!isNumber(config.min) || !isNumber(config.max)) {
|
||||
if (!range) {
|
||||
range = findNumericFieldMinMax(data);
|
||||
}
|
||||
if (!isNumber(config.min)) {
|
||||
config.min = range.min;
|
||||
}
|
||||
if (!isNumber(config.max)) {
|
||||
config.max = range.max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...field,
|
||||
|
||||
// Overwrite the configs
|
||||
config,
|
||||
|
||||
// Set the display processor
|
||||
processor: getDisplayProcessor({
|
||||
type: field.type,
|
||||
config: config,
|
||||
theme,
|
||||
isUtc,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
...frame,
|
||||
fields,
|
||||
name,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
interface DynamicConfigValueOptions {
|
||||
value: DynamicConfigValue;
|
||||
config: FieldConfig;
|
||||
field: Field;
|
||||
data: DataFrame;
|
||||
replaceVariables: InterpolateFunction;
|
||||
}
|
||||
|
||||
const numericFieldProps: any = {
|
||||
decimals: true,
|
||||
min: true,
|
||||
max: true,
|
||||
};
|
||||
|
||||
function prepareConfigValue(key: string, input: any, options?: DynamicConfigValueOptions): any {
|
||||
if (options) {
|
||||
// TODO template variables etc
|
||||
}
|
||||
|
||||
if (numericFieldProps[key]) {
|
||||
const num = toNumber(input);
|
||||
if (isNaN(num)) {
|
||||
return null;
|
||||
}
|
||||
return num;
|
||||
} else if (input) {
|
||||
// skips empty string
|
||||
if (key === 'unit' && input === 'none') {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
export function setDynamicConfigValue(config: FieldConfig, options: DynamicConfigValueOptions) {
|
||||
const { value } = options;
|
||||
const v = prepareConfigValue(value.path, value.value, options);
|
||||
set(config, value.path, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* For numeric values, only valid numbers will be applied
|
||||
* for units, 'none' will be skipped
|
||||
*/
|
||||
export function setFieldConfigDefaults(config: FieldConfig, props?: FieldConfig) {
|
||||
if (props) {
|
||||
const keys = Object.keys(props);
|
||||
for (const key of keys) {
|
||||
const val = prepareConfigValue(key, (props as any)[key]);
|
||||
if (val === null || val === undefined) {
|
||||
continue;
|
||||
}
|
||||
set(config, key, val);
|
||||
}
|
||||
}
|
||||
|
||||
// First value is always -Infinity
|
||||
if (config.thresholds && config.thresholds.length) {
|
||||
config.thresholds[0].value = -Infinity;
|
||||
}
|
||||
|
||||
// Verify that max > min (swap if necessary)
|
||||
if (config.hasOwnProperty('min') && config.hasOwnProperty('max') && config.min! > config.max!) {
|
||||
const tmp = config.max;
|
||||
config.max = config.min;
|
||||
config.min = tmp;
|
||||
}
|
||||
}
|
@ -1,2 +1,4 @@
|
||||
export * from './fieldDisplay';
|
||||
export * from './displayProcessor';
|
||||
|
||||
export { applyFieldOverrides } from './fieldOverrides';
|
||||
|
19
packages/grafana-data/src/types/fieldOverrides.ts
Normal file
19
packages/grafana-data/src/types/fieldOverrides.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { MatcherConfig, FieldConfig } from '../types';
|
||||
|
||||
export interface DynamicConfigValue {
|
||||
path: string;
|
||||
value: any;
|
||||
}
|
||||
|
||||
export interface ConfigOverrideRule {
|
||||
matcher: MatcherConfig;
|
||||
properties: DynamicConfigValue[];
|
||||
}
|
||||
|
||||
export interface FieldConfigSource {
|
||||
// Defatuls applied to all numeric fields
|
||||
defaults: FieldConfig;
|
||||
|
||||
// Rules to override individual values
|
||||
overrides: ConfigOverrideRule[];
|
||||
}
|
@ -12,6 +12,7 @@ export * from './displayValue';
|
||||
export * from './graph';
|
||||
export * from './ScopedVars';
|
||||
export * from './transformations';
|
||||
export * from './fieldOverrides';
|
||||
export * from './vector';
|
||||
export * from './app';
|
||||
export * from './datasource';
|
||||
|
@ -36,4 +36,30 @@ describe('sharedSingleStatMigrationHandler', () => {
|
||||
|
||||
expect(sharedSingleStatMigrationHandler(panel as any)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('Remove unused `overrides` option', () => {
|
||||
const panel = {
|
||||
options: {
|
||||
fieldOptions: {
|
||||
unit: 'watt',
|
||||
stat: 'last',
|
||||
decimals: 5,
|
||||
defaults: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
mappings: [],
|
||||
},
|
||||
override: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
mappings: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
title: 'Usage',
|
||||
type: 'bargauge',
|
||||
};
|
||||
|
||||
expect(sharedSingleStatMigrationHandler(panel as any)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
VizOrientation,
|
||||
PanelModel,
|
||||
FieldDisplayOptions,
|
||||
ConfigOverrideRule,
|
||||
} from '@grafana/data';
|
||||
|
||||
export interface SingleStatBaseOptions {
|
||||
@ -33,7 +34,7 @@ export function sharedSingleStatPanelChangedHandler(
|
||||
const options = {
|
||||
fieldOptions: {
|
||||
defaults: {} as FieldConfig,
|
||||
override: {} as FieldConfig,
|
||||
overrides: [] as ConfigOverrideRule[],
|
||||
calcs: [reducer ? reducer.id : ReducerID.mean],
|
||||
},
|
||||
orientation: VizOrientation.Horizontal,
|
||||
@ -110,6 +111,20 @@ export function sharedSingleStatMigrationHandler(panel: PanelModel<SingleStatBas
|
||||
options = moveThresholdsAndMappingsToField(options);
|
||||
}
|
||||
|
||||
if (previousVersion < 6.6) {
|
||||
// discard the old `override` options and enter an empty array
|
||||
if (options.fieldOptions && options.fieldOptions.override) {
|
||||
const { override, ...rest } = options.fieldOptions;
|
||||
options = {
|
||||
...options,
|
||||
fieldOptions: {
|
||||
...rest,
|
||||
overrides: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return options as SingleStatBaseOptions;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,22 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sharedSingleStatMigrationHandler Remove unused \`overrides\` option 1`] = `
|
||||
Object {
|
||||
"fieldOptions": Object {
|
||||
"decimals": 5,
|
||||
"defaults": Object {
|
||||
"mappings": undefined,
|
||||
"max": 100,
|
||||
"min": 0,
|
||||
"thresholds": undefined,
|
||||
},
|
||||
"overrides": Array [],
|
||||
"stat": "last",
|
||||
"unit": "watt",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sharedSingleStatMigrationHandler from old valueOptions model without pluginVersion 1`] = `
|
||||
Object {
|
||||
"fieldOptions": Object {
|
||||
|
@ -28,7 +28,7 @@ Object {
|
||||
],
|
||||
"unit": "watt",
|
||||
},
|
||||
"override": Object {},
|
||||
"overrides": Array [],
|
||||
"values": false,
|
||||
},
|
||||
"orientation": "vertical",
|
||||
|
@ -35,7 +35,7 @@ export const standardFieldDisplayOptions: FieldDisplayOptions = {
|
||||
],
|
||||
mappings: [],
|
||||
},
|
||||
override: {},
|
||||
overrides: [],
|
||||
};
|
||||
|
||||
export const defaults: StatPanelOptions = {
|
||||
|
Loading…
Reference in New Issue
Block a user