diff --git a/packages/grafana-ui/src/components/GraphNG/utils.ts b/packages/grafana-ui/src/components/GraphNG/utils.ts index 427408064a0..cc7feecd4a0 100644 --- a/packages/grafana-ui/src/components/GraphNG/utils.ts +++ b/packages/grafana-ui/src/components/GraphNG/utils.ts @@ -17,13 +17,11 @@ function applySpanNullsThresholds(frame: DataFrame) { continue; } - if (field.type === FieldType.number) { - let spanNulls = field.config.custom?.spanNulls; + let spanNulls = field.config.custom?.spanNulls; - if (typeof spanNulls === 'number') { - if (spanNulls !== -1) { - field.values = new ArrayVector(nullToUndefThreshold(refValues, field.values.toArray(), spanNulls)); - } + if (typeof spanNulls === 'number') { + if (spanNulls !== -1) { + field.values = new ArrayVector(nullToUndefThreshold(refValues, field.values.toArray(), spanNulls)); } } } diff --git a/public/app/plugins/panel/state-timeline/module.tsx b/public/app/plugins/panel/state-timeline/module.tsx index e45c66fceab..2aed7c0165b 100755 --- a/public/app/plugins/panel/state-timeline/module.tsx +++ b/public/app/plugins/panel/state-timeline/module.tsx @@ -1,10 +1,17 @@ -import { FieldColorModeId, FieldConfigProperty, PanelPlugin } from '@grafana/data'; +import { + FieldColorModeId, + FieldConfigProperty, + FieldType, + identityOverrideProcessor, + PanelPlugin, +} from '@grafana/data'; import { StateTimelinePanel } from './StateTimelinePanel'; import { TimelineOptions, TimelineFieldConfig, defaultPanelOptions, defaultTimelineFieldConfig } from './types'; import { VisibilityMode } from '@grafana/schema'; import { commonOptionsBuilder } from '@grafana/ui'; import { timelinePanelChangedHandler } from './migrations'; import { StatTimelineSuggestionsSupplier } from './suggestions'; +import { SpanNullsEditor } from '../timeseries/SpanNullsEditor'; export const plugin = new PanelPlugin(StateTimelinePanel) .setPanelChangeHandler(timelinePanelChangedHandler) @@ -40,6 +47,16 @@ export const plugin = new PanelPlugin(Stat max: 100, step: 1, }, + }) + .addCustomEditor({ + id: 'spanNulls', + path: 'spanNulls', + name: 'Connect null values', + defaultValue: false, + editor: SpanNullsEditor, + override: SpanNullsEditor, + shouldApply: (f) => f.type !== FieldType.time, + process: identityOverrideProcessor, }); }, }) diff --git a/public/app/plugins/panel/state-timeline/utils.ts b/public/app/plugins/panel/state-timeline/utils.ts index 74209a04412..1b5a7ff3036 100644 --- a/public/app/plugins/panel/state-timeline/utils.ts +++ b/public/app/plugins/panel/state-timeline/utils.ts @@ -310,6 +310,15 @@ export function unsetSameFutureValues(values: any[]): any[] | undefined { return clone; } +function getSpanNulls(field: Field) { + let spanNulls = field.config.custom?.spanNulls; + + // magic value for join() to leave nulls alone instead of expanding null ranges + // should be set to -1 when spanNulls = null|undefined|false|0, which is "retain nulls, without expanding" + // Infinity is not optimal here since it causes spanNulls to be more expensive than simply removing all nulls unconditionally + return !spanNulls ? -1 : spanNulls === true ? Infinity : spanNulls; +} + /** * Merge values by the threshold */ @@ -359,8 +368,7 @@ export function mergeThresholdValues(field: Field, theme: GrafanaTheme2): Field ...field.config, custom: { ...field.config.custom, - // magic value for join() to leave nulls alone - spanNulls: -1, + spanNulls: getSpanNulls(field), }, }, type: FieldType.string, @@ -413,8 +421,7 @@ export function prepareTimelineFields( ...field.config, custom: { ...field.config.custom, - // magic value for join() to leave nulls alone - spanNulls: -1, + spanNulls: getSpanNulls(field), }, }, };