From 4d88e2b542c62bf287a60af01fddfa2e6de220b4 Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Thu, 3 Nov 2022 08:24:01 -0500 Subject: [PATCH] TimeSeries: more thorough detection of negative values for stacking dir (#57863) --- .../src/components/uPlot/utils.test.ts | 2 +- .../grafana-ui/src/components/uPlot/utils.ts | 48 ++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/packages/grafana-ui/src/components/uPlot/utils.test.ts b/packages/grafana-ui/src/components/uPlot/utils.test.ts index 21e9b2d873e..b09afd295fc 100644 --- a/packages/grafana-ui/src/components/uPlot/utils.test.ts +++ b/packages/grafana-ui/src/components/uPlot/utils.test.ts @@ -1061,7 +1061,7 @@ describe('auto stacking groups', () => { }, { name: 'd', - values: [-0, -10, -20], + values: [null, -0, null], config: { custom: { stacking: { mode: StackingMode.Normal } } }, }, ], diff --git a/packages/grafana-ui/src/components/uPlot/utils.ts b/packages/grafana-ui/src/components/uPlot/utils.ts index 9be91616cb8..f8100a1b7bc 100644 --- a/packages/grafana-ui/src/components/uPlot/utils.ts +++ b/packages/grafana-ui/src/components/uPlot/utils.ts @@ -116,8 +116,7 @@ export function getStackingGroups(frame: DataFrame) { // will this be stacked up or down after any transforms applied let vals = values.toArray(); let transform = custom.transform; - let firstValue = vals.find((v) => v != null); - let stackDir = getStackDirection(transform, firstValue); + let stackDir = getStackDirection(transform, vals); let drawStyle = custom.drawStyle as GraphDrawStyle; let drawStyle2 = @@ -341,13 +340,48 @@ export function findMidPointYPosition(u: uPlot, idx: number) { return y; } -function getStackDirection(transform: GraphTransform, firstValue: number) { - // Check if first value is negative zero. This can happen with a binary operation transform. - const isNegativeZero = Object.is(firstValue, -0); +function getStackDirection(transform: GraphTransform, data: unknown[]) { + const hasNegSamp = hasNegSample(data); + if (transform === GraphTransform.NegativeY) { - return !isNegativeZero && firstValue >= 0 ? StackDirection.Neg : StackDirection.Pos; + return hasNegSamp ? StackDirection.Pos : StackDirection.Neg; } - return !isNegativeZero && firstValue >= 0 ? StackDirection.Pos : StackDirection.Neg; + return hasNegSamp ? StackDirection.Neg : StackDirection.Pos; +} + +// similar to isLikelyAscendingVector() +function hasNegSample(data: unknown[], samples = 50) { + const len = data.length; + + if (len === 0) { + return false; + } + + // skip leading & trailing nullish + let firstIdx = 0; + let lastIdx = len - 1; + + while (firstIdx <= lastIdx && data[firstIdx] == null) { + firstIdx++; + } + + while (lastIdx >= firstIdx && data[lastIdx] == null) { + lastIdx--; + } + + if (lastIdx >= firstIdx) { + const stride = Math.max(1, Math.floor((lastIdx - firstIdx + 1) / samples)); + + for (let i = firstIdx; i <= lastIdx; i += stride) { + const v = data[i]; + + if (v != null && (v < 0 || Object.is(v, -0))) { + return true; + } + } + } + + return false; } // Dev helpers