TimeSeries: Use graph min/max for percentage threshold (#38528)

* TimeSeries: Use graph min/max as baseis for percentage threshold calculation.

* respect hard and soft axis limits for % threshold steps

* revert state-timeline changes

* enable by-threshold coloring in histogram and barchart

* revert yMin

Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
Oscar Kilhed 2021-08-29 22:46:17 +02:00 committed by GitHub
parent 96557ecadc
commit b248c119ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 77 additions and 14 deletions

View File

@ -236,6 +236,10 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor
show: !customConfig.hideFrom?.viz,
gradientMode: customConfig.gradientMode,
thresholds: config.thresholds,
hardMin: field.config.min,
hardMax: field.config.max,
softMin: customConfig.axisSoftMin,
softMax: customConfig.axisSoftMax,
// The following properties are not used in the uPlot config, but are utilized as transport for legend config
dataFrameFieldIndex: field.state?.origin,
});
@ -249,6 +253,10 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor
thresholds: config.thresholds,
scaleKey,
theme,
hardMin: field.config.min,
hardMax: field.config.max,
softMin: customConfig.axisSoftMin,
softMax: customConfig.axisSoftMax,
});
}
}

View File

@ -26,10 +26,15 @@ export interface SeriesProps extends LineConfig, BarConfig, FillConfig, PointsCo
scaleKey: string;
pxAlign?: boolean;
gradientMode?: GraphGradientMode;
/** Used when gradientMode is set to Scheme */
thresholds?: ThresholdsConfig;
/** Used when gradientMode is set to Scheme */
colorMode?: FieldColorMode;
hardMin?: number | null;
hardMax?: number | null;
softMin?: number | null;
softMax?: number | null;
drawStyle?: GraphDrawStyle;
pathBuilder?: Series.PathBuilder;
pointsFilter?: Series.Points.Filter;
@ -138,17 +143,29 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
}
private getLineColor(): Series.Stroke {
const { lineColor, gradientMode, colorMode, thresholds, theme } = this.props;
const { lineColor, gradientMode, colorMode, thresholds, theme, hardMin, hardMax, softMin, softMax } = this.props;
if (gradientMode === GraphGradientMode.Scheme && colorMode?.id !== FieldColorModeId.Fixed) {
return getScaleGradientFn(1, theme, colorMode, thresholds);
return getScaleGradientFn(1, theme, colorMode, thresholds, hardMin, hardMax, softMin, softMax);
}
return lineColor ?? FALLBACK_COLOR;
}
private getFill(): Series.Fill | undefined {
const { lineColor, fillColor, gradientMode, fillOpacity, colorMode, thresholds, theme } = this.props;
const {
lineColor,
fillColor,
gradientMode,
fillOpacity,
colorMode,
thresholds,
theme,
hardMin,
hardMax,
softMin,
softMax,
} = this.props;
if (fillColor) {
return fillColor;
@ -164,7 +181,7 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
return getHueGradientFn((fillColor ?? lineColor)!, opacityPercent, theme);
case GraphGradientMode.Scheme:
if (colorMode?.id !== FieldColorModeId.Fixed) {
return getScaleGradientFn(opacityPercent, theme, colorMode, thresholds);
return getScaleGradientFn(opacityPercent, theme, colorMode, thresholds, hardMin, hardMax, softMin, softMax);
}
// intentional fall-through to handle Scheme with Fixed color
default:

View File

@ -1,6 +1,6 @@
import { GrafanaTheme2, ThresholdsConfig, ThresholdsMode } from '@grafana/data';
import { GraphThresholdsStyleConfig, GraphTresholdsStyleMode } from '@grafana/schema';
import { getDataRange, GradientDirection, scaleGradient } from './gradientFills';
import { getThresholdRange, GradientDirection, scaleGradient } from './gradientFills';
import tinycolor from 'tinycolor2';
export interface UPlotThresholdOptions {
@ -8,23 +8,27 @@ export interface UPlotThresholdOptions {
thresholds: ThresholdsConfig;
config: GraphThresholdsStyleConfig;
theme: GrafanaTheme2;
hardMin?: number | null;
hardMax?: number | null;
softMin?: number | null;
softMax?: number | null;
}
export function getThresholdsDrawHook(options: UPlotThresholdOptions) {
return (u: uPlot) => {
const ctx = u.ctx;
const { scaleKey, thresholds, theme, config } = options;
const { scaleKey, thresholds, theme, config, hardMin, hardMax, softMin, softMax } = options;
const { min: xMin, max: xMax } = u.scales.x;
const { min: yMin, max: yMax } = u.scales[scaleKey];
if (xMin === undefined || xMax === undefined || yMin === undefined || yMax === undefined) {
if (xMin == null || xMax == null || yMin == null || yMax == null) {
return;
}
let { steps, mode } = thresholds;
if (mode === ThresholdsMode.Percentage) {
let [min, max] = getDataRange(u, scaleKey);
let [min, max] = getThresholdRange(u, scaleKey, hardMin, hardMax, softMin, softMax);
let range = max - min;
steps = steps.map((step) => ({

View File

@ -172,11 +172,36 @@ export function getDataRange(plot: uPlot, scaleKey: string) {
return [min, max];
}
export function getThresholdRange(
u: uPlot,
scaleKey: string,
hardMin?: number | null,
hardMax?: number | null,
softMin?: number | null,
softMax?: number | null
) {
let min = hardMin ?? softMin ?? null;
let max = hardMax ?? softMax ?? null;
if (min == null || max == null) {
let [dataMin, dataMax] = getDataRange(u, scaleKey);
min = min ?? dataMin ?? 0;
max = max ?? dataMax ?? 100;
}
return [min, max];
}
export function getScaleGradientFn(
opacity: number,
theme: GrafanaTheme2,
colorMode?: FieldColorMode,
thresholds?: ThresholdsConfig
thresholds?: ThresholdsConfig,
hardMin?: number | null,
hardMax?: number | null,
softMin?: number | null,
softMax?: number | null
): (self: uPlot, seriesIdx: number) => CanvasGradient | string {
if (!colorMode) {
throw Error('Missing colorMode required for color scheme gradients');
@ -199,7 +224,7 @@ export function getScaleGradientFn(
);
gradient = scaleGradient(plot, scaleKey, GradientDirection.Up, valueStops, true);
} else {
const [min, max] = getDataRange(plot, scaleKey);
const [min, max] = getThresholdRange(plot, scaleKey, hardMin, hardMax, softMin, softMax);
const range = max - min;
const valueStops = thresholds.steps.map(
(step) =>
@ -212,7 +237,7 @@ export function getScaleGradientFn(
}
} else if (colorMode.getColors) {
const colors = colorMode.getColors(theme);
const [min, max] = getDataRange(plot, scaleKey);
const [min, max] = getThresholdRange(plot, scaleKey, hardMin, hardMax, softMin, softMax);
const range = max - min;
const valueStops = colors.map(
(color, i) =>

View File

@ -17,7 +17,7 @@ export const plugin = new PanelPlugin<BarChartOptions, BarChartFieldConfig>(BarC
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byValueSupport: false,
byValueSupport: true,
},
defaultValue: {
mode: FieldColorModeId.PaletteClassic,

View File

@ -134,6 +134,10 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<BarChartOptions> = ({
show: !customConfig.hideFrom?.viz,
gradientMode: customConfig.gradientMode,
thresholds: field.config.thresholds,
hardMin: field.config.min,
hardMax: field.config.max,
softMin: customConfig.axisSoftMin,
softMax: customConfig.axisSoftMax,
// The following properties are not used in the uPlot config, but are utilized as transport for legend config
dataFrameFieldIndex: {

View File

@ -182,6 +182,11 @@ const prepConfig = (frame: DataFrame, theme: GrafanaTheme2) => {
gradientMode: customConfig.gradientMode,
thresholds: field.config.thresholds,
hardMin: field.config.min,
hardMax: field.config.max,
softMin: customConfig.axisSoftMin,
softMax: customConfig.axisSoftMax,
// The following properties are not used in the uPlot config, but are utilized as transport for legend config
dataFrameFieldIndex: {
fieldIndex: i,

View File

@ -52,7 +52,7 @@ export const plugin = new PanelPlugin<PanelOptions, PanelFieldConfig>(HistogramP
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byValueSupport: false,
byValueSupport: true,
},
defaultValue: {
mode: FieldColorModeId.PaletteClassic,