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:
@@ -23,4 +23,8 @@ export class SortedVector<T = any> implements Vector<T> {
|
|||||||
toJSON(): T[] {
|
toJSON(): T[] {
|
||||||
return vectorToArray(this);
|
return vectorToArray(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOrderArray(): number[] {
|
||||||
|
return this.order;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
import { Field, PanelProps, getLinksSupplier } from '@grafana/data';
|
import { Field, PanelProps } from '@grafana/data';
|
||||||
import { PanelDataErrorView } from '@grafana/runtime';
|
import { PanelDataErrorView } from '@grafana/runtime';
|
||||||
import { TooltipDisplayMode } from '@grafana/schema';
|
import { TooltipDisplayMode } from '@grafana/schema';
|
||||||
import { usePanelContext, TimeSeries, TooltipPlugin, ZoomPlugin, KeyboardPlugin } from '@grafana/ui';
|
import { usePanelContext, TimeSeries, TooltipPlugin, ZoomPlugin, KeyboardPlugin } from '@grafana/ui';
|
||||||
@@ -14,7 +14,7 @@ import { ExemplarsPlugin } from './plugins/ExemplarsPlugin';
|
|||||||
import { OutsideRangePlugin } from './plugins/OutsideRangePlugin';
|
import { OutsideRangePlugin } from './plugins/OutsideRangePlugin';
|
||||||
import { ThresholdControlsPlugin } from './plugins/ThresholdControlsPlugin';
|
import { ThresholdControlsPlugin } from './plugins/ThresholdControlsPlugin';
|
||||||
import { TimeSeriesOptions } from './types';
|
import { TimeSeriesOptions } from './types';
|
||||||
import { getTimezones, prepareGraphableFields } from './utils';
|
import { getTimezones, prepareGraphableFields, regenerateLinksSupplier } from './utils';
|
||||||
|
|
||||||
interface TimeSeriesPanelProps extends PanelProps<TimeSeriesOptions> {}
|
interface TimeSeriesPanelProps extends PanelProps<TimeSeriesOptions> {}
|
||||||
|
|
||||||
@@ -65,15 +65,11 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
|
|||||||
options={options}
|
options={options}
|
||||||
>
|
>
|
||||||
{(config, alignedDataFrame) => {
|
{(config, alignedDataFrame) => {
|
||||||
alignedDataFrame.fields.forEach((field) => {
|
if (
|
||||||
field.getLinks = getLinksSupplier(
|
alignedDataFrame.fields.filter((f) => f.config.links !== undefined && f.config.links.length > 0).length > 0
|
||||||
alignedDataFrame,
|
) {
|
||||||
field,
|
alignedDataFrame = regenerateLinksSupplier(alignedDataFrame, frames, replaceVariables, timeZone);
|
||||||
field.state!.scopedVars!,
|
}
|
||||||
replaceVariables,
|
|
||||||
timeZone
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ describe('prepare timeseries graph', () => {
|
|||||||
const frames = prepareGraphableFields(input, createTheme());
|
const frames = prepareGraphableFields(input, createTheme());
|
||||||
const out = frames![0];
|
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');
|
const field = out.fields.find((f) => f.name === 'c');
|
||||||
expect(field?.display).toBeDefined();
|
expect(field?.display).toBeDefined();
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import {
|
|||||||
Field,
|
Field,
|
||||||
FieldType,
|
FieldType,
|
||||||
getDisplayProcessor,
|
getDisplayProcessor,
|
||||||
|
getLinksSupplier,
|
||||||
GrafanaTheme2,
|
GrafanaTheme2,
|
||||||
|
InterpolateFunction,
|
||||||
isBooleanUnit,
|
isBooleanUnit,
|
||||||
|
SortedVector,
|
||||||
TimeRange,
|
TimeRange,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { GraphFieldConfig, LineInterpolation } from '@grafana/schema';
|
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);
|
fields.push(copy);
|
||||||
break; // ok
|
break; // ok
|
||||||
case FieldType.boolean:
|
case FieldType.boolean:
|
||||||
@@ -123,3 +134,46 @@ export function getTimezones(timezones: string[] | undefined, defaultTimezone: s
|
|||||||
}
|
}
|
||||||
return timezones.map((v) => (v?.length ? v : defaultTimezone));
|
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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user