From 097dcc456a617bc67c3d5134e22adc00ad5b79c5 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Sun, 29 Nov 2020 09:22:16 -0800 Subject: [PATCH] MinMax: keep global min/main in field state (#29406) --- .../grafana-data/src/field/fieldDisplay.ts | 7 +++-- .../src/field/fieldOverrides.test.ts | 8 ++--- .../grafana-data/src/field/fieldOverrides.ts | 31 +++++++------------ .../field/getFieldDisplayValuesProxy.test.tsx | 1 - packages/grafana-data/src/field/scale.ts | 24 ++++++++------ packages/grafana-data/src/types/dataFrame.ts | 13 ++++++++ .../grafana-data/src/types/fieldOverrides.ts | 1 - .../src/components/Table/BarGaugeCell.tsx | 3 +- .../uPlot/config/UPlotConfigBuilder.test.ts | 4 +-- .../uPlot/config/UPlotScaleBuilder.ts | 20 ++++++++---- .../dashboard/utils/loadSnapshotData.ts | 1 - .../panel/panellinks/linkSuppliers.test.ts | 1 - .../features/query/state/PanelQueryRunner.ts | 1 - .../plugins/panel/bargauge/BarGaugePanel.tsx | 1 - public/app/plugins/panel/gauge/GaugePanel.tsx | 1 - public/app/plugins/panel/stat/StatPanel.tsx | 1 - 16 files changed, 65 insertions(+), 53 deletions(-) diff --git a/packages/grafana-data/src/field/fieldDisplay.ts b/packages/grafana-data/src/field/fieldDisplay.ts index 9fc5b325fc5..5d24df79091 100644 --- a/packages/grafana-data/src/field/fieldDisplay.ts +++ b/packages/grafana-data/src/field/fieldDisplay.ts @@ -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 = diff --git a/packages/grafana-data/src/field/fieldOverrides.test.ts b/packages/grafana-data/src/field/fieldOverrides.test.ts index 6551b922acf..d8ebd19e783 100644 --- a/packages/grafana-data/src/field/fieldOverrides.test.ts +++ b/packages/grafana-data/src/field/fieldOverrides.test.ts @@ -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]; diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index 839aaa28aba..a1f141a80a1 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -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, }, }; diff --git a/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx b/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx index e1dc2a5aad9..26aa02f9261 100644 --- a/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx +++ b/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx @@ -32,7 +32,6 @@ describe('getFieldDisplayValuesProxy', () => { getDataSourceSettingsByUid: (val: string) => ({} as any), timeZone: 'utc', theme: getTestTheme(), - autoMinMax: true, })[0]; it('should define all display functions', () => { diff --git a/packages/grafana-data/src/field/scale.ts b/packages/grafana-data/src/field/scale.ts index 32933c5dc26..35d76d35095 100644 --- a/packages/grafana-data/src/field/scale.ts +++ b/packages/grafana-data/src/field/scale.ts @@ -1,6 +1,6 @@ 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 }; +} diff --git a/packages/grafana-data/src/types/dataFrame.ts b/packages/grafana-data/src/types/dataFrame.ts index 3fdd660a80b..d634ab11e6c 100644 --- a/packages/grafana-data/src/types/dataFrame.ts +++ b/packages/grafana-data/src/types/dataFrame.ts @@ -126,6 +126,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 */ @@ -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 diff --git a/packages/grafana-data/src/types/fieldOverrides.ts b/packages/grafana-data/src/types/fieldOverrides.ts index 335b3e2d5d9..cedce20cbd2 100644 --- a/packages/grafana-data/src/types/fieldOverrides.ts +++ b/packages/grafana-data/src/types/fieldOverrides.ts @@ -117,7 +117,6 @@ export interface ApplyFieldOverrideOptions { getDataSourceSettingsByUid: (uid: string) => DataSourceInstanceSettings | undefined; theme: GrafanaTheme; timeZone?: TimeZone; - autoMinMax?: boolean; fieldConfigRegistry?: FieldConfigOptionsRegistry; } diff --git a/packages/grafana-ui/src/components/Table/BarGaugeCell.tsx b/packages/grafana-ui/src/components/Table/BarGaugeCell.tsx index 99ba69f3759..e4fd2f2491f 100644 --- a/packages/grafana-ui/src/components/Table/BarGaugeCell.tsx +++ b/packages/grafana-ui/src/components/Table/BarGaugeCell.tsx @@ -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 = props => { const { field, column, tableStyles, cell, cellProps } = props; - let { config } = field; + let config = getFieldConfigWithMinMax(field, false); if (!config.thresholds) { config = { ...config, diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts index a02d84d6a3f..9d5e6b2c19c 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts @@ -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 [ diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts index 796f7c432aa..e3f822b422b 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts @@ -1,5 +1,4 @@ -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 { 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]; + }, }, }; } diff --git a/public/app/features/dashboard/utils/loadSnapshotData.ts b/public/app/features/dashboard/utils/loadSnapshotData.ts index 534ab23c468..64b1e40c5cb 100644 --- a/public/app/features/dashboard/utils/loadSnapshotData.ts +++ b/public/app/features/dashboard/utils/loadSnapshotData.ts @@ -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, diff --git a/public/app/features/panel/panellinks/linkSuppliers.test.ts b/public/app/features/panel/panellinks/linkSuppliers.test.ts index 941b83b320a..d75a097d137 100644 --- a/public/app/features/panel/panellinks/linkSuppliers.test.ts +++ b/public/app/features/panel/panellinks/linkSuppliers.test.ts @@ -93,7 +93,6 @@ describe('getFieldLinksSupplier', () => { getDataSourceSettingsByUid: (val: string) => ({} as any), timeZone: 'utc', theme: getTheme(), - autoMinMax: true, })[0]; const rowIndex = 0; diff --git a/public/app/features/query/state/PanelQueryRunner.ts b/public/app/features/query/state/PanelQueryRunner.ts index cb94ce09b8e..7aa8daac174 100644 --- a/public/app/features/query/state/PanelQueryRunner.ts +++ b/public/app/features/query/state/PanelQueryRunner.ts @@ -89,7 +89,6 @@ export class PanelQueryRunner { ...processedData, series: applyFieldOverrides({ timeZone: timeZone, - autoMinMax: true, data: processedData.series, ...fieldConfig, }), diff --git a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx index 7f9e02de58e..503c255692c 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx @@ -75,7 +75,6 @@ export class BarGaugePanel extends PureComponent> { replaceVariables, theme: config.theme, data: data.series, - autoMinMax: true, timeZone, }); }; diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index 99dd84cf8be..47f3f07c1ef 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -57,7 +57,6 @@ export class GaugePanel extends PureComponent> { replaceVariables, theme: config.theme, data: data.series, - autoMinMax: true, timeZone, }); }; diff --git a/public/app/plugins/panel/stat/StatPanel.tsx b/public/app/plugins/panel/stat/StatPanel.tsx index 6eb9470e1dc..e9d5049498c 100644 --- a/public/app/plugins/panel/stat/StatPanel.tsx +++ b/public/app/plugins/panel/stat/StatPanel.tsx @@ -103,7 +103,6 @@ export class StatPanel extends PureComponent> { theme: config.theme, data: data.series, sparkline: options.graphMode !== BigValueGraphMode.None, - autoMinMax: true, timeZone, }); };