import { ArrayVector, DataFrame, Field, FieldType, getDisplayProcessor, GrafanaTheme2, isBooleanUnit, } from '@grafana/data'; import { GraphFieldConfig, LineInterpolation, StackingMode } from '@grafana/schema'; /** * Returns null if there are no graphable fields */ export function prepareGraphableFields(series: DataFrame[], theme: GrafanaTheme2): DataFrame[] | null { if (!series?.length) { return null; } let copy: Field; const frames: DataFrame[] = []; for (let frame of series) { const fields: Field[] = []; let hasTimeField = false; let hasValueField = false; for (const field of frame.fields) { switch (field.type) { case FieldType.time: hasTimeField = true; fields.push(field); break; case FieldType.number: hasValueField = true; copy = { ...field, values: new ArrayVector( field.values.toArray().map((v) => { if (!(Number.isFinite(v) || v == null)) { return null; } return v; }) ), }; if (copy.config.custom?.stacking?.mode === StackingMode.Percent) { copy.config.unit = 'percentunit'; copy.display = getDisplayProcessor({ field: copy, theme }); } fields.push(copy); break; // ok case FieldType.boolean: hasValueField = true; const custom: GraphFieldConfig = field.config?.custom ?? {}; const config = { ...field.config, max: 1, min: 0, custom, }; // smooth and linear do not make sense if (custom.lineInterpolation !== LineInterpolation.StepBefore) { custom.lineInterpolation = LineInterpolation.StepAfter; } copy = { ...field, config, type: FieldType.number, values: new ArrayVector( field.values.toArray().map((v) => { if (v == null) { return v; } return Boolean(v) ? 1 : 0; }) ), }; if (!isBooleanUnit(config.unit)) { config.unit = 'bool'; copy.display = getDisplayProcessor({ field: copy, theme }); } fields.push(copy); break; } } if (hasTimeField && hasValueField) { frames.push({ ...frame, fields, }); } } if (frames.length) { return frames; } return null; }