mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 16:15:42 -06:00
MinMax: keep global min/main in field state (#29406)
This commit is contained in:
parent
ea64dda1e3
commit
097dcc456a
@ -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', () => {
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 [
|
||||
|
@ -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<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,
|
||||
});
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user