mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
MinMax: Highlight issues with skipping field overrides (#33672)
* MinMax: Highlight issues with skipping field overrides * check global range * more selective * basic test * moved into getFieldDisplayValues Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
parent
59e8ba6773
commit
fffff70d46
@ -72,6 +72,34 @@ describe('FieldDisplay', () => {
|
||||
expect(display.map((v) => v.display.numeric)).toEqual([1, 3]); // First 2 are from the first field
|
||||
});
|
||||
|
||||
it('should not calculate min max if ensureGlobalRange is false', () => {
|
||||
const options = createDisplayOptions({
|
||||
ensureGlobalRange: false,
|
||||
reduceOptions: {
|
||||
values: true, //
|
||||
limit: 1000,
|
||||
calcs: [],
|
||||
},
|
||||
});
|
||||
const display = getFieldDisplayValues(options);
|
||||
expect(display[0].field.min).toBeUndefined();
|
||||
expect(display[0].field.max).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should ensure global min / max on numerical fields', () => {
|
||||
const options = createDisplayOptions({
|
||||
ensureGlobalRange: true,
|
||||
reduceOptions: {
|
||||
values: true, //
|
||||
limit: 1000,
|
||||
calcs: [],
|
||||
},
|
||||
});
|
||||
const display = getFieldDisplayValues(options);
|
||||
expect(display[0].field.min).toEqual(1);
|
||||
expect(display[0].field.max).toEqual(6);
|
||||
});
|
||||
|
||||
it('Should return field thresholds when there is no data', () => {
|
||||
const options = createEmptyDisplayOptions({
|
||||
fieldConfig: {
|
||||
|
@ -22,6 +22,7 @@ import { getTimeField } from '../dataframe/processDataFrame';
|
||||
import { getFieldMatcher } from '../transformations';
|
||||
import { FieldMatcherID } from '../transformations/matchers/ids';
|
||||
import { getFieldDisplayName } from './fieldState';
|
||||
import { ensureGlobalRangeOnState } from './scale';
|
||||
|
||||
/**
|
||||
* Options for how to turn DataFrames into an array of display values
|
||||
@ -73,6 +74,7 @@ export interface GetFieldDisplayValuesOptions {
|
||||
sparkline?: boolean; // Calculate the sparkline
|
||||
theme: GrafanaTheme2;
|
||||
timeZone?: TimeZone;
|
||||
ensureGlobalRange?: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_FIELD_DISPLAY_VALUES_LIMIT = 25;
|
||||
@ -99,6 +101,10 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
|
||||
|
||||
let hitLimit = false;
|
||||
|
||||
if (options.ensureGlobalRange) {
|
||||
ensureGlobalRangeOnState(data);
|
||||
}
|
||||
|
||||
for (let s = 0; s < data.length && !hitLimit; s++) {
|
||||
const dataFrame = data[s]; // Name is already set
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { ThresholdsMode, Field, FieldType } from '../types';
|
||||
import { sortThresholds } from './thresholds';
|
||||
import { ArrayVector } from '../vector/ArrayVector';
|
||||
import { getScaleCalculator } from './scale';
|
||||
import { ensureGlobalRangeOnState, getScaleCalculator } from './scale';
|
||||
import { createTheme } from '../themes';
|
||||
import { getColorForTheme } from '../utils';
|
||||
import { toDataFrame } from '../dataframe';
|
||||
|
||||
describe('getScaleCalculator', () => {
|
||||
it('should return percent, threshold and color', () => {
|
||||
@ -50,3 +51,26 @@ describe('getScaleCalculator', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ensure global scales', () => {
|
||||
it('should fill in all numeric values', () => {
|
||||
const frame = toDataFrame({
|
||||
fields: [
|
||||
{ type: FieldType.number, values: [1, 2, 3] },
|
||||
{ type: FieldType.number, values: [7, 8, 9] },
|
||||
{ type: FieldType.string, values: ['a', 'b', 'c'] },
|
||||
],
|
||||
});
|
||||
ensureGlobalRangeOnState([frame]);
|
||||
|
||||
expect(frame.fields[0].state!.range).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"delta": 8,
|
||||
"max": 9,
|
||||
"min": 1,
|
||||
}
|
||||
`);
|
||||
|
||||
expect(frame.fields[2].state?.range).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { isNumber } from 'lodash';
|
||||
import { GrafanaTheme2 } from '../themes/types';
|
||||
import { reduceField, ReducerID } from '../transformations/fieldReducer';
|
||||
import { Field, FieldConfig, FieldType, NumericRange, Threshold } from '../types';
|
||||
import { DataFrame, Field, FieldConfig, FieldType, NumericRange, Threshold } from '../types';
|
||||
import { getFieldColorModeForField } from './fieldColor';
|
||||
import { findNumericFieldMinMax } from './fieldOverrides';
|
||||
import { getActiveThresholdForValue } from './thresholds';
|
||||
|
||||
export interface ColorScaleValue {
|
||||
@ -112,3 +113,41 @@ export function getFieldConfigWithMinMax(field: Field, local?: boolean): FieldCo
|
||||
|
||||
return { ...config, ...field.state.range };
|
||||
}
|
||||
|
||||
/**
|
||||
* This will check that each field has a range value stored on state
|
||||
* If the value is missing, the global range will be calculated and
|
||||
* saved in the field state.
|
||||
*
|
||||
* The same process usually happens in `applyFieldOverrieds`, but
|
||||
* when the process can be skipped the global range may be missing
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function ensureGlobalRangeOnState(frames?: DataFrame[]) {
|
||||
if (!frames) {
|
||||
return;
|
||||
}
|
||||
|
||||
let globalRange: NumericRange | undefined = undefined;
|
||||
for (const frame of frames) {
|
||||
for (const field of frame.fields) {
|
||||
if (field.type === FieldType.number) {
|
||||
if (field.state?.range) {
|
||||
continue; // already set
|
||||
}
|
||||
const { config } = field;
|
||||
if (!globalRange && (config.min == null || config.max == null)) {
|
||||
globalRange = findNumericFieldMinMax(frames);
|
||||
}
|
||||
|
||||
const min = config.min ?? globalRange!.min;
|
||||
const max = config.max ?? globalRange!.max;
|
||||
if (!field.state) {
|
||||
field.state = {};
|
||||
}
|
||||
field.state.range = { min, max, delta: max! - min! };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +114,12 @@ export class PanelQueryRunner {
|
||||
fields: frame.fields.map((field, fieldIndex) => ({
|
||||
...field,
|
||||
values: data.series[frameIndex].fields[fieldIndex].values,
|
||||
state: {},
|
||||
state: {
|
||||
...field.state,
|
||||
calcs: undefined,
|
||||
// add global range calculation here? (not optimal for streaming)
|
||||
range: undefined,
|
||||
},
|
||||
})),
|
||||
})),
|
||||
};
|
||||
|
@ -69,9 +69,11 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
|
||||
|
||||
getValues = (): FieldDisplay[] => {
|
||||
const { data, options, replaceVariables, fieldConfig, timeZone } = this.props;
|
||||
|
||||
return getFieldDisplayValues({
|
||||
fieldConfig,
|
||||
reduceOptions: options.reduceOptions,
|
||||
ensureGlobalRange: true,
|
||||
replaceVariables,
|
||||
theme: config.theme2,
|
||||
data: data.series,
|
||||
|
@ -54,6 +54,7 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
|
||||
const { data, options, replaceVariables, fieldConfig, timeZone } = this.props;
|
||||
return getFieldDisplayValues({
|
||||
fieldConfig,
|
||||
ensureGlobalRange: true,
|
||||
reduceOptions: options.reduceOptions,
|
||||
replaceVariables,
|
||||
theme: config.theme2,
|
||||
|
@ -85,6 +85,7 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
|
||||
|
||||
return getFieldDisplayValues({
|
||||
fieldConfig,
|
||||
ensureGlobalRange: true,
|
||||
reduceOptions: options.reduceOptions,
|
||||
replaceVariables,
|
||||
theme: config.theme2,
|
||||
|
Loading…
Reference in New Issue
Block a user