From 93fef224ae5d28100a47b37338c9ff18a52d0dc9 Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Mon, 26 Feb 2024 19:41:39 -0600 Subject: [PATCH] TimeSeries: Don't re-init chart with bars style on data updates (#83355) --- .../transformers/joinDataFrames.ts | 17 +++++++---- .../app/core/components/GraphNG/utils.test.ts | 2 -- public/app/core/components/GraphNG/utils.ts | 28 ++++++++----------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts b/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts index 121a0391818..a03edb78f02 100644 --- a/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts +++ b/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts @@ -61,6 +61,11 @@ export interface JoinOptions { */ keepDisplayNames?: boolean; + /** + * @internal -- Optionally specify how to treat null values + */ + nullMode?: (field: Field) => JoinNullMode; + /** * @internal -- Optionally specify a join mode (outer or inner) */ @@ -95,6 +100,9 @@ export function joinDataFrames(options: JoinOptions): DataFrame | undefined { return; } + const nullMode = + options.nullMode ?? ((field: Field) => (field.config.custom?.spanNulls === true ? NULL_REMOVE : NULL_EXPAND)); + if (options.frames.length === 1) { let frame = options.frames[0]; let frameCopy = frame; @@ -186,8 +194,7 @@ export function joinDataFrames(options: JoinOptions): DataFrame | undefined { } // Support the standard graph span nulls field config - let spanNulls = field.config.custom?.spanNulls; - nullModesFrame.push(spanNulls === true ? NULL_REMOVE : spanNulls === -1 ? NULL_RETAIN : NULL_EXPAND); + nullModesFrame.push(nullMode(field)); let labels = field.labels ?? {}; let name = field.name; @@ -374,9 +381,9 @@ export type AlignedData = | [xValues: number[] | TypedArray, ...yValues: Array | TypedArray>]; // nullModes -const NULL_REMOVE = 0; // nulls are converted to undefined (e.g. for spanGaps: true) -const NULL_RETAIN = 1; // nulls are retained, with alignment artifacts set to undefined (default) -const NULL_EXPAND = 2; // nulls are expanded to include any adjacent alignment artifacts +export const NULL_REMOVE = 0; // nulls are converted to undefined (e.g. for spanGaps: true) +export const NULL_RETAIN = 1; // nulls are retained, with alignment artifacts set to undefined (default) +export const NULL_EXPAND = 2; // nulls are expanded to include any adjacent alignment artifacts type JoinNullMode = number; // NULL_IGNORE | NULL_RETAIN | NULL_EXPAND; diff --git a/public/app/core/components/GraphNG/utils.test.ts b/public/app/core/components/GraphNG/utils.test.ts index 9675cd7ca56..485384ab2a3 100644 --- a/public/app/core/components/GraphNG/utils.test.ts +++ b/public/app/core/components/GraphNG/utils.test.ts @@ -353,7 +353,6 @@ describe('GraphNG utils', () => { "config": { "custom": { "drawStyle": "bars", - "spanNulls": -1, }, }, "labels": { @@ -386,7 +385,6 @@ describe('GraphNG utils', () => { "config": { "custom": { "drawStyle": "bars", - "spanNulls": -1, }, }, "labels": { diff --git a/public/app/core/components/GraphNG/utils.ts b/public/app/core/components/GraphNG/utils.ts index 64cd07e1b6d..dc49d3abe3b 100644 --- a/public/app/core/components/GraphNG/utils.ts +++ b/public/app/core/components/GraphNG/utils.ts @@ -1,4 +1,5 @@ import { DataFrame, Field, FieldType, outerJoinDataFrames, TimeRange } from '@grafana/data'; +import { NULL_EXPAND, NULL_REMOVE, NULL_RETAIN } from '@grafana/data/src/transformations/transformers/joinDataFrames'; import { applyNullInsertThreshold } from '@grafana/data/src/transformations/transformers/nulls/nullInsertThreshold'; import { nullToUndefThreshold } from '@grafana/data/src/transformations/transformers/nulls/nullToUndefThreshold'; import { GraphDrawStyle } from '@grafana/schema'; @@ -68,23 +69,10 @@ export function preparePlotFrame(frames: DataFrame[], dimFields: XYFieldMatchers } }); - let numBarSeries = 0; - - frames.forEach((frame) => { - frame.fields.forEach((f) => { - if (isVisibleBarField(f)) { - // prevent minesweeper-expansion of nulls (gaps) when joining bars - // since bar width is determined from the minimum distance between non-undefined values - // (this strategy will still retain any original pre-join nulls, though) - f.config.custom = { - ...f.config.custom, - spanNulls: -1, - }; - - numBarSeries++; - } - }); - }); + let numBarSeries = frames.reduce( + (acc, frame) => acc + frame.fields.reduce((acc, field) => acc + (isVisibleBarField(field) ? 1 : 0), 0), + 0 + ); // to make bar widths of all series uniform (equal to narrowest bar series), find smallest distance between x points let minXDelta = Infinity; @@ -115,6 +103,12 @@ export function preparePlotFrame(frames: DataFrame[], dimFields: XYFieldMatchers // https://github.com/grafana/grafana/pull/31121 // https://github.com/grafana/grafana/pull/71806 keepDisplayNames: true, + + // prevent minesweeper-expansion of nulls (gaps) when joining bars + // since bar width is determined from the minimum distance between non-undefined values + // (this strategy will still retain any original pre-join nulls, though) + nullMode: (field) => + isVisibleBarField(field) ? NULL_RETAIN : field.config.custom?.spanNulls === true ? NULL_REMOVE : NULL_EXPAND, }); if (alignedFrame) {