From eb90d0c4b53348bc8c0b9b5a39d127f8aef2f329 Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Thu, 8 Sep 2022 11:52:57 -0500 Subject: [PATCH] TimeSeries: Support for log scale and negative numbers (#54812) --- .../grafana-schema/src/schema/mudball.cue | 3 +- .../grafana-schema/src/schema/mudball.gen.ts | 2 + .../GraphNG/__snapshots__/utils.test.ts.snap | 1 + .../src/components/TimeSeries/utils.ts | 1 + .../uPlot/config/UPlotConfigBuilder.test.ts | 4 ++ .../uPlot/config/UPlotScaleBuilder.ts | 25 ++++++-- .../grafana-ui/src/options/builder/axis.tsx | 62 ++++++++++++------- .../barchart/__snapshots__/utils.test.ts.snap | 16 +++++ 8 files changed, 86 insertions(+), 28 deletions(-) diff --git a/packages/grafana-schema/src/schema/mudball.cue b/packages/grafana-schema/src/schema/mudball.cue index 054e8e8a44b..f4cb9d302d6 100644 --- a/packages/grafana-schema/src/schema/mudball.cue +++ b/packages/grafana-schema/src/schema/mudball.cue @@ -21,7 +21,7 @@ GraphTransform: "constant" | "negative-Y" @cue LineInterpolation: "linear" | "smooth" | "stepBefore" | "stepAfter" @cuetsy(kind="enum") // TODO docs -ScaleDistribution: "linear" | "log" | "ordinal" @cuetsy(kind="enum") +ScaleDistribution: "linear" | "log" | "ordinal" | "symlog" @cuetsy(kind="enum") // TODO docs GraphGradientMode: "none" | "opacity" | "hue" | "scheme" @cuetsy(kind="enum") @@ -83,6 +83,7 @@ PointsConfig: { ScaleDistributionConfig: { type: ScaleDistribution log?: number + linearThreshold?: number } @cuetsy(kind="interface") // TODO docs diff --git a/packages/grafana-schema/src/schema/mudball.gen.ts b/packages/grafana-schema/src/schema/mudball.gen.ts index b8f3cec5b4c..a9688bb097e 100644 --- a/packages/grafana-schema/src/schema/mudball.gen.ts +++ b/packages/grafana-schema/src/schema/mudball.gen.ts @@ -46,6 +46,7 @@ export enum ScaleDistribution { Linear = 'linear', Log = 'log', Ordinal = 'ordinal', + Symlog = 'symlog', } export enum GraphGradientMode { @@ -116,6 +117,7 @@ export interface PointsConfig { } export interface ScaleDistributionConfig { + linearThreshold?: number; log?: number; type: ScaleDistribution; } diff --git a/packages/grafana-ui/src/components/GraphNG/__snapshots__/utils.test.ts.snap b/packages/grafana-ui/src/components/GraphNG/__snapshots__/utils.test.ts.snap index 5bb603fa828..05d9b60120e 100644 --- a/packages/grafana-ui/src/components/GraphNG/__snapshots__/utils.test.ts.snap +++ b/packages/grafana-ui/src/components/GraphNG/__snapshots__/utils.test.ts.snap @@ -100,6 +100,7 @@ Object { ], "scales": Object { "__fixed/na-na/na-na/auto/linear/na": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, diff --git a/packages/grafana-ui/src/components/TimeSeries/utils.ts b/packages/grafana-ui/src/components/TimeSeries/utils.ts index 413881808e9..37914e6097b 100644 --- a/packages/grafana-ui/src/components/TimeSeries/utils.ts +++ b/packages/grafana-ui/src/components/TimeSeries/utils.ts @@ -217,6 +217,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ direction: ScaleDirection.Up, distribution: customConfig.scaleDistribution?.type, log: customConfig.scaleDistribution?.log, + linearThreshold: customConfig.scaleDistribution?.linearThreshold, min: field.config.min, max: field.config.max, softMin: customConfig.axisSoftMin, 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 ee182713179..a32fadd34d7 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts @@ -124,6 +124,7 @@ describe('UPlotConfigBuilder', () => { "time": true, }, "scale-y": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -209,6 +210,7 @@ describe('UPlotConfigBuilder', () => { ], "scales": Object { "scale-y": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -274,6 +276,7 @@ describe('UPlotConfigBuilder', () => { ], "scales": Object { "scale-y": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -340,6 +343,7 @@ describe('UPlotConfigBuilder', () => { ], "scales": Object { "scale-y": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts index e7d4077ce6e..9527e8b69b2 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts @@ -17,6 +17,7 @@ export interface ScaleProps { orientation: ScaleOrientation; direction: ScaleDirection; log?: number; + linearThreshold?: number; centeredZero?: boolean; decimals?: DecimalCount; } @@ -41,15 +42,21 @@ export class UPlotScaleBuilder extends PlotConfigBuilder { centeredZero, decimals, } = this.props; + + const distr = this.props.distribution; + const distribution = !isTime ? { distr: - this.props.distribution === ScaleDistribution.Log + distr === ScaleDistribution.Symlog + ? 4 + : distr === ScaleDistribution.Log ? 3 - : this.props.distribution === ScaleDistribution.Ordinal + : distr === ScaleDistribution.Ordinal ? 2 : 1, - log: this.props.distribution === ScaleDistribution.Log ? this.props.log || 2 : undefined, + log: distr === ScaleDistribution.Log || distr === ScaleDistribution.Symlog ? this.props.log ?? 2 : undefined, + asinh: distr === ScaleDistribution.Symlog ? this.props.linearThreshold ?? 1 : undefined, } : {}; @@ -91,7 +98,7 @@ export class UPlotScaleBuilder extends PlotConfigBuilder { return minMax; } - if (scale.distr === 1 || scale.distr === 2) { + if (scale.distr === 1 || scale.distr === 2 || scale.distr === 4) { if (centeredZero) { let absMin = Math.abs(dataMin!); let absMax = Math.abs(dataMax!); @@ -100,8 +107,14 @@ export class UPlotScaleBuilder extends PlotConfigBuilder { dataMax = max; } - // @ts-ignore here we may use hardMin / hardMax to make sure any extra padding is computed from a more accurate delta - minMax = uPlot.rangeNum(hardMinOnly ? hardMin : dataMin, hardMaxOnly ? hardMax : dataMax, rangeConfig); + if (scale.distr === 4) { + // TODO: switch to `, true)` after updating uPlot to 1.6.23+ + // see https://github.com/leeoniya/uPlot/issues/749 + minMax = uPlot.rangeAsinh(dataMin!, dataMax!, scale.log ?? 10, false); + } else { + // @ts-ignore here we may use hardMin / hardMax to make sure any extra padding is computed from a more accurate delta + minMax = uPlot.rangeNum(hardMinOnly ? hardMin : dataMin, hardMaxOnly ? hardMax : dataMax, rangeConfig); + } } else if (scale.distr === 3) { minMax = uPlot.rangeLog(dataMin!, dataMax!, scale.log ?? 10, true); } diff --git a/packages/grafana-ui/src/options/builder/axis.tsx b/packages/grafana-ui/src/options/builder/axis.tsx index 117c74f1b4f..6b940e0736c 100644 --- a/packages/grafana-ui/src/options/builder/axis.tsx +++ b/packages/grafana-ui/src/options/builder/axis.tsx @@ -9,7 +9,7 @@ import { } from '@grafana/data'; import { AxisColorMode, AxisConfig, AxisPlacement, ScaleDistribution, ScaleDistributionConfig } from '@grafana/schema'; -import { graphFieldOptions, Select, HorizontalGroup, RadioButtonGroup } from '../../index'; +import { graphFieldOptions, Select, RadioButtonGroup, Input, Field } from '../../index'; /** * @alpha @@ -128,6 +128,10 @@ const DISTRIBUTION_OPTIONS: Array> = [ label: 'Logarithmic', value: ScaleDistribution.Log, }, + { + label: 'Symlog', + value: ScaleDistribution.Symlog, + }, ]; const LOG_DISTRIBUTION_OPTIONS: Array> = [ @@ -147,32 +151,48 @@ const LOG_DISTRIBUTION_OPTIONS: Array> = [ export const ScaleDistributionEditor = ({ value, onChange }: StandardEditorProps) => { const type = value?.type ?? ScaleDistribution.Linear; return ( - - { - onChange({ - ...value, - type: v!, - log: v === ScaleDistribution.Linear ? undefined : 2, - }); - }} - /> - {type === ScaleDistribution.Log && ( - { + onChange({ + ...value, + log: v.value!, + }); + }} + /> + )} - + {type === ScaleDistribution.Symlog && ( + + { + onChange({ + ...value, + linearThreshold: Number(v.currentTarget.value), + }); + }} + /> + + )} + ); }; diff --git a/public/app/plugins/panel/barchart/__snapshots__/utils.test.ts.snap b/public/app/plugins/panel/barchart/__snapshots__/utils.test.ts.snap index b02536f380b..eb7fa961463 100644 --- a/public/app/plugins/panel/barchart/__snapshots__/utils.test.ts.snap +++ b/public/app/plugins/panel/barchart/__snapshots__/utils.test.ts.snap @@ -105,6 +105,7 @@ Object { ], "scales": Object { "m/s": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -114,6 +115,7 @@ Object { "time": undefined, }, "x": Object { + "asinh": undefined, "auto": true, "dir": -1, "distr": 2, @@ -256,6 +258,7 @@ Object { ], "scales": Object { "m/s": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -265,6 +268,7 @@ Object { "time": undefined, }, "x": Object { + "asinh": undefined, "auto": true, "dir": -1, "distr": 2, @@ -407,6 +411,7 @@ Object { ], "scales": Object { "m/s": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -416,6 +421,7 @@ Object { "time": undefined, }, "x": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 2, @@ -558,6 +564,7 @@ Object { ], "scales": Object { "m/s": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -567,6 +574,7 @@ Object { "time": undefined, }, "x": Object { + "asinh": undefined, "auto": true, "dir": -1, "distr": 2, @@ -709,6 +717,7 @@ Object { ], "scales": Object { "m/s": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -718,6 +727,7 @@ Object { "time": undefined, }, "x": Object { + "asinh": undefined, "auto": true, "dir": -1, "distr": 2, @@ -860,6 +870,7 @@ Object { ], "scales": Object { "m/s": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -869,6 +880,7 @@ Object { "time": undefined, }, "x": Object { + "asinh": undefined, "auto": true, "dir": -1, "distr": 2, @@ -1011,6 +1023,7 @@ Object { ], "scales": Object { "m/s": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -1020,6 +1033,7 @@ Object { "time": undefined, }, "x": Object { + "asinh": undefined, "auto": true, "dir": -1, "distr": 2, @@ -1162,6 +1176,7 @@ Object { ], "scales": Object { "m/s": Object { + "asinh": undefined, "auto": true, "dir": 1, "distr": 1, @@ -1171,6 +1186,7 @@ Object { "time": undefined, }, "x": Object { + "asinh": undefined, "auto": true, "dir": -1, "distr": 2,