TimeseriesPanel: Preserve string fields for data link interpolation (#58424)

* TimeseriesPanel: Preserve string fields for data link interpolation

* clean code

* Modify tests so that string fields are allowed only when a valid time/number dataframe exists

* performance mods

* fix wrong length

* remove console.log

* Check if aligned dataframe has links
This commit is contained in:
Victor Marin 2022-11-24 14:44:32 +02:00 committed by GitHub
parent 1ee52e14d2
commit 0da77201bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 12 deletions

View File

@ -23,4 +23,8 @@ export class SortedVector<T = any> implements Vector<T> {
toJSON(): T[] {
return vectorToArray(this);
}
getOrderArray(): number[] {
return this.order;
}
}

View File

@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import { Field, PanelProps, getLinksSupplier } from '@grafana/data';
import { Field, PanelProps } from '@grafana/data';
import { PanelDataErrorView } from '@grafana/runtime';
import { TooltipDisplayMode } from '@grafana/schema';
import { usePanelContext, TimeSeries, TooltipPlugin, ZoomPlugin, KeyboardPlugin } from '@grafana/ui';
@ -14,7 +14,7 @@ import { ExemplarsPlugin } from './plugins/ExemplarsPlugin';
import { OutsideRangePlugin } from './plugins/OutsideRangePlugin';
import { ThresholdControlsPlugin } from './plugins/ThresholdControlsPlugin';
import { TimeSeriesOptions } from './types';
import { getTimezones, prepareGraphableFields } from './utils';
import { getTimezones, prepareGraphableFields, regenerateLinksSupplier } from './utils';
interface TimeSeriesPanelProps extends PanelProps<TimeSeriesOptions> {}
@ -65,15 +65,11 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
options={options}
>
{(config, alignedDataFrame) => {
alignedDataFrame.fields.forEach((field) => {
field.getLinks = getLinksSupplier(
alignedDataFrame,
field,
field.state!.scopedVars!,
replaceVariables,
timeZone
);
});
if (
alignedDataFrame.fields.filter((f) => f.config.links !== undefined && f.config.links.length > 0).length > 0
) {
alignedDataFrame = regenerateLinksSupplier(alignedDataFrame, frames, replaceVariables, timeZone);
}
return (
<>

View File

@ -43,7 +43,7 @@ describe('prepare timeseries graph', () => {
const frames = prepareGraphableFields(input, createTheme());
const out = frames![0];
expect(out.fields.map((f) => f.name)).toEqual(['a', 'c', 'd']);
expect(out.fields.map((f) => f.name)).toEqual(['a', 'b', 'c', 'd']);
const field = out.fields.find((f) => f.name === 'c');
expect(field?.display).toBeDefined();

View File

@ -4,8 +4,11 @@ import {
Field,
FieldType,
getDisplayProcessor,
getLinksSupplier,
GrafanaTheme2,
InterpolateFunction,
isBooleanUnit,
SortedVector,
TimeRange,
} from '@grafana/data';
import { GraphFieldConfig, LineInterpolation } from '@grafana/schema';
@ -60,6 +63,14 @@ export function prepareGraphableFields(
),
};
fields.push(copy);
break; // ok
case FieldType.string:
copy = {
...field,
values: new ArrayVector(field.values.toArray()),
};
fields.push(copy);
break; // ok
case FieldType.boolean:
@ -123,3 +134,46 @@ export function getTimezones(timezones: string[] | undefined, defaultTimezone: s
}
return timezones.map((v) => (v?.length ? v : defaultTimezone));
}
export function regenerateLinksSupplier(
alignedDataFrame: DataFrame,
frames: DataFrame[],
replaceVariables: InterpolateFunction,
timeZone: string
): DataFrame {
alignedDataFrame.fields.forEach((field) => {
const frameIndex = field.state?.origin?.frameIndex;
if (frameIndex === undefined) {
return;
}
const frame = frames[frameIndex];
const tempFields: Field[] = [];
/* check if field has sortedVector values
if it does, sort all string fields in the original frame by the order array already used for the field
otherwise just attach the fields to the temporary frame used to get the links
*/
for (const frameField of frame.fields) {
if (frameField.type === FieldType.string) {
if (field.values instanceof SortedVector) {
const copiedField = { ...frameField };
copiedField.values = new SortedVector(frameField.values, field.values.getOrderArray());
tempFields.push(copiedField);
} else {
tempFields.push(frameField);
}
}
}
const tempFrame: DataFrame = {
fields: [...alignedDataFrame.fields, ...tempFields],
length: alignedDataFrame.fields.length + tempFields.length,
};
field.getLinks = getLinksSupplier(tempFrame, field, field.state!.scopedVars!, replaceVariables, timeZone);
});
return alignedDataFrame;
}