Commit 097dcc45 by Ryan McKinley Committed by GitHub

MinMax: keep global min/main in field state (#29406)

parent ea64dda1
......@@ -77,7 +77,6 @@ export interface GetFieldDisplayValuesOptions {
replaceVariables: InterpolateFunction;
sparkline?: boolean; // Calculate the sparkline
theme: GrafanaTheme;
autoMinMax?: boolean;
timeZone?: TimeZone;
}
......@@ -122,7 +121,11 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
continue;
}
const config = field.config; // already set by the prepare task
let config = field.config; // already set by the prepare task
if (field.state?.range) {
// Us the global min/max values
config = { ...config, ...field.state?.range };
}
const displayName = field.config.displayName ?? defaultDisplayName;
const display =
......
......@@ -293,16 +293,15 @@ describe('applyFieldOverrides', () => {
replaceVariables: (undefined as any) as InterpolateFunction,
getDataSourceSettingsByUid: undefined as any,
theme: getTestTheme(),
autoMinMax: true,
})[0];
const valueColumn = data.fields[1];
const config = valueColumn.config;
const range = valueColumn.state!.range!;
// Keep max from the original setting
expect(config.max).toEqual(0);
expect(range.max).toEqual(0);
// Don't Automatically pick the min value
expect(config.min).toEqual(-20);
expect(range.min).toEqual(-20);
});
it('getLinks should use applied field config', () => {
......@@ -317,7 +316,6 @@ describe('applyFieldOverrides', () => {
}) as InterpolateFunction,
getDataSourceSettingsByUid: undefined as any,
theme: getTestTheme(),
autoMinMax: true,
fieldConfigRegistry: customFieldRegistry,
})[0];
......
......@@ -13,6 +13,7 @@ import {
GrafanaTheme,
InterpolateFunction,
LinkModel,
NumericRange,
ScopedVars,
TimeZone,
ValueLinkConfig,
......@@ -40,12 +41,7 @@ interface OverrideProps {
properties: DynamicConfigValue[];
}
interface GlobalMinMax {
min?: number | null;
max?: number | null;
}
export function findNumericFieldMinMax(data: DataFrame[]): GlobalMinMax {
export function findNumericFieldMinMax(data: DataFrame[]): NumericRange {
let min: number | null = null;
let max: number | null = null;
......@@ -69,7 +65,7 @@ export function findNumericFieldMinMax(data: DataFrame[]): GlobalMinMax {
}
}
return { min, max };
return { min, max, delta: (max ?? 0) - (min ?? 0) };
}
/**
......@@ -88,7 +84,7 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra
const fieldConfigRegistry = options.fieldConfigRegistry ?? standardFieldConfigEditorRegistry;
let seriesIndex = 0;
let range: GlobalMinMax | undefined = undefined;
let globalRange: NumericRange | undefined = undefined;
// Prepare the Matchers
const override: OverrideProps[] = [];
......@@ -178,18 +174,14 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra
}
// Set the Min/Max value automatically
if (options.autoMinMax && field.type === FieldType.number) {
if (!isNumber(config.min) || !isNumber(config.max)) {
if (!range) {
range = findNumericFieldMinMax(options.data!); // Global value
}
if (!isNumber(config.min)) {
config.min = range.min;
}
if (!isNumber(config.max)) {
config.max = range.max;
}
let range: NumericRange | undefined = undefined;
if (field.type === FieldType.number) {
if (!globalRange && (!isNumber(config.min) || !isNumber(config.max))) {
globalRange = findNumericFieldMinMax(options.data!);
}
const min = config.min ?? globalRange!.min;
const max = config.max ?? globalRange!.max;
range = { min, max, delta: max! - min! };
}
// Some color modes needs series index to assign field color so we count
......@@ -207,6 +199,7 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra
...field.state,
displayName: null,
seriesIndex,
range,
},
};
......
......@@ -32,7 +32,6 @@ describe('getFieldDisplayValuesProxy', () => {
getDataSourceSettingsByUid: (val: string) => ({} as any),
timeZone: 'utc',
theme: getTestTheme(),
autoMinMax: true,
})[0];
it('should define all display functions', () => {
......
import { isNumber } from 'lodash';
import { reduceField, ReducerID } from '../transformations/fieldReducer';
import { Field, FieldType, GrafanaTheme, Threshold } from '../types';
import { Field, FieldConfig, FieldType, GrafanaTheme, NumericRange, Threshold } from '../types';
import { getFieldColorModeForField } from './fieldColor';
import { getActiveThresholdForValue } from './thresholds';
......@@ -15,7 +15,7 @@ export type ScaleCalculator = (value: number) => ScaledValue;
export function getScaleCalculator(field: Field, theme: GrafanaTheme): ScaleCalculator {
const mode = getFieldColorModeForField(field);
const getColor = mode.getCalculator(field, theme);
const info = getMinMaxAndDelta(field);
const info = field.state?.range ?? getMinMaxAndDelta(field);
return (value: number) => {
let percent = 0;
......@@ -34,13 +34,7 @@ export function getScaleCalculator(field: Field, theme: GrafanaTheme): ScaleCalc
};
}
interface FieldMinMaxInfo {
min?: number | null;
max?: number | null;
delta: number;
}
function getMinMaxAndDelta(field: Field): FieldMinMaxInfo {
function getMinMaxAndDelta(field: Field): NumericRange {
if (field.type !== FieldType.number) {
return { min: 0, max: 100, delta: 100 };
}
......@@ -70,3 +64,15 @@ function getMinMaxAndDelta(field: Field): FieldMinMaxInfo {
delta: max! - min!,
};
}
export function getFieldConfigWithMinMax(field: Field, local?: boolean): FieldConfig {
const { config } = field;
let { min, max } = config;
if (isNumber(min) && !isNumber(max)) {
return config; // noop
}
if (local || !field.state?.range) {
return { ...config, ...getMinMaxAndDelta(field) };
}
return { ...config, ...field.state.range };
}
......@@ -127,6 +127,13 @@ export interface FieldState {
calcs?: FieldCalcs;
/**
* The numeric range for values in this field. This value will respect the min/max
* set in field config, or when set to `auto` this will have the min/max for all data
* in the response
*/
range?: NumericRange;
/**
* Appropriate values for templating
*/
scopedVars?: ScopedVars;
......@@ -138,6 +145,12 @@ export interface FieldState {
seriesIndex?: number;
}
export interface NumericRange {
min?: number | null;
max?: number | null;
delta: number;
}
export interface DataFrame extends QueryResultBase {
name?: string;
fields: Field[]; // All fields of equal length
......
......@@ -117,7 +117,6 @@ export interface ApplyFieldOverrideOptions {
getDataSourceSettingsByUid: (uid: string) => DataSourceInstanceSettings | undefined;
theme: GrafanaTheme;
timeZone?: TimeZone;
autoMinMax?: boolean;
fieldConfigRegistry?: FieldConfigOptionsRegistry;
}
......
......@@ -2,6 +2,7 @@ import React, { FC } from 'react';
import { ThresholdsConfig, ThresholdsMode, VizOrientation } from '@grafana/data';
import { BarGauge, BarGaugeDisplayMode } from '../BarGauge/BarGauge';
import { TableCellProps, TableCellDisplayMode } from './types';
import { getFieldConfigWithMinMax } from '@grafana/data/src/field/scale';
const defaultScale: ThresholdsConfig = {
mode: ThresholdsMode.Absolute,
......@@ -20,7 +21,7 @@ const defaultScale: ThresholdsConfig = {
export const BarGaugeCell: FC<TableCellProps> = props => {
const { field, column, tableStyles, cell, cellProps } = props;
let { config } = field;
let config = getFieldConfigWithMinMax(field, false);
if (!config.thresholds) {
config = {
...config,
......
......@@ -22,12 +22,10 @@ describe('UPlotConfigBuilder', () => {
"axes": Array [],
"scales": Object {
"scale-x": Object {
"range": undefined,
"time": true,
},
"scale-y": Object {
"range": undefined,
"time": false,
"range": [Function],
},
},
"series": Array [
......
import isNumber from 'lodash/isNumber';
import { Scale } from 'uplot';
import uPlot, { Scale } from 'uplot';
import { PlotConfigBuilder } from '../types';
export interface ScaleProps {
......@@ -11,12 +10,21 @@ export interface ScaleProps {
export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
getConfig() {
const { isTime, scaleKey, min, max } = this.props;
const range = isNumber(min) && isNumber(max) ? [min, max] : undefined;
const { isTime, scaleKey } = this.props;
if (isTime) {
return {
[scaleKey]: {
time: true, // TODO? this should be based on the query range, not the data
},
};
}
return {
[scaleKey]: {
time: !!isTime,
range,
range: (u: uPlot, dataMin: number, dataMax: number) => {
const { min, max } = this.props;
const [smin, smax] = uPlot.rangeNum(min ?? dataMin, max ?? dataMax, 0.1 as any, true);
return [min ?? smin, max ?? smax];
},
},
};
}
......
......@@ -16,7 +16,6 @@ export function loadSnapshotData(panel: PanelModel, dashboard: DashboardModel):
defaults: {},
overrides: [],
},
autoMinMax: true,
replaceVariables: panel.replaceVariables,
getDataSourceSettingsByUid: getDatasourceSrv().getDataSourceSettingsByUid.bind(getDatasourceSrv()),
fieldConfigRegistry: panel.plugin!.fieldConfigRegistry,
......
......@@ -93,7 +93,6 @@ describe('getFieldLinksSupplier', () => {
getDataSourceSettingsByUid: (val: string) => ({} as any),
timeZone: 'utc',
theme: getTheme(),
autoMinMax: true,
})[0];
const rowIndex = 0;
......
......@@ -89,7 +89,6 @@ export class PanelQueryRunner {
...processedData,
series: applyFieldOverrides({
timeZone: timeZone,
autoMinMax: true,
data: processedData.series,
...fieldConfig,
}),
......
......@@ -75,7 +75,6 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
replaceVariables,
theme: config.theme,
data: data.series,
autoMinMax: true,
timeZone,
});
};
......
......@@ -57,7 +57,6 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
replaceVariables,
theme: config.theme,
data: data.series,
autoMinMax: true,
timeZone,
});
};
......
......@@ -103,7 +103,6 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
theme: config.theme,
data: data.series,
sparkline: options.graphMode !== BigValueGraphMode.None,
autoMinMax: true,
timeZone,
});
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment