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