StateTimeline: Add tooltip multi mode (#79944)
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
co-authored by
Leon Sorokin
parent
7d5432e10a
commit
552fa78564
@@ -210,7 +210,10 @@ export const StateTimelinePanel = ({
|
||||
alignedData={alignedFrame}
|
||||
seriesIdx={seriesIdx}
|
||||
timeZone={timeZone}
|
||||
mode={options.tooltip.mode}
|
||||
sortOrder={options.tooltip.sort}
|
||||
isPinned={isPinned}
|
||||
timeRange={timeRange}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
arrayUtils,
|
||||
DataFrame,
|
||||
Field,
|
||||
FieldType,
|
||||
@@ -9,9 +10,11 @@ import {
|
||||
getFieldDisplayName,
|
||||
GrafanaTheme2,
|
||||
LinkModel,
|
||||
TimeRange,
|
||||
TimeZone,
|
||||
} from '@grafana/data';
|
||||
import { useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { SortOrder } from '@grafana/schema/dist/esm/common/common.gen';
|
||||
import { TooltipDisplayMode, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
|
||||
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
|
||||
import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader';
|
||||
@@ -28,6 +31,9 @@ interface StateTimelineTooltip2Props {
|
||||
seriesIdx: number | null | undefined;
|
||||
isPinned: boolean;
|
||||
timeZone?: TimeZone;
|
||||
timeRange: TimeRange;
|
||||
mode?: TooltipDisplayMode;
|
||||
sortOrder?: SortOrder;
|
||||
}
|
||||
|
||||
export const StateTimelineTooltip2 = ({
|
||||
@@ -36,6 +42,9 @@ export const StateTimelineTooltip2 = ({
|
||||
dataIdxs,
|
||||
seriesIdx,
|
||||
timeZone,
|
||||
timeRange,
|
||||
mode = TooltipDisplayMode.Single,
|
||||
sortOrder = SortOrder.None,
|
||||
isPinned,
|
||||
}: StateTimelineTooltip2Props) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
@@ -65,54 +74,45 @@ export const StateTimelineTooltip2 = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const field = alignedData.fields[seriesIdx!];
|
||||
|
||||
const links: Array<LinkModel<Field>> = getDataLinks(field, datapointIdx);
|
||||
let contentLabelValue: LabelValue[] = [];
|
||||
|
||||
const xField = alignedData.fields[0];
|
||||
const xFieldFmt = xField.display || getDisplayProcessor({ field: xField, timeZone, theme });
|
||||
|
||||
const dataFrameFieldIndex = field.state?.origin;
|
||||
const fieldFmt = field.display || getDisplayProcessor({ field, timeZone, theme });
|
||||
const value = field.values[datapointIdx!];
|
||||
const display = fieldFmt(value);
|
||||
const fieldDisplayName = dataFrameFieldIndex
|
||||
? getFieldDisplayName(
|
||||
data[dataFrameFieldIndex.frameIndex].fields[dataFrameFieldIndex.fieldIndex],
|
||||
data[dataFrameFieldIndex.frameIndex],
|
||||
data
|
||||
)
|
||||
: null;
|
||||
|
||||
const nextStateIdx = findNextStateIndex(field, datapointIdx!);
|
||||
let nextStateTs;
|
||||
if (nextStateIdx) {
|
||||
nextStateTs = xField.values[nextStateIdx!];
|
||||
}
|
||||
|
||||
const stateTs = xField.values[datapointIdx!];
|
||||
let duration = nextStateTs && fmtDuration(nextStateTs - stateTs);
|
||||
|
||||
if (nextStateTs) {
|
||||
duration = nextStateTs && fmtDuration(nextStateTs - stateTs);
|
||||
}
|
||||
let links: Array<LinkModel<Field>> = [];
|
||||
|
||||
const from = xFieldFmt(xField.values[datapointIdx!]).text;
|
||||
const to = xFieldFmt(xField.values[nextStateIdx!]).text;
|
||||
|
||||
const getHeaderLabel = (): LabelValue => {
|
||||
return {
|
||||
label: '',
|
||||
value: Boolean(to) ? to : from,
|
||||
};
|
||||
};
|
||||
// Single mode
|
||||
if (mode === TooltipDisplayMode.Single || isPinned) {
|
||||
const field = alignedData.fields[seriesIdx!];
|
||||
links = getDataLinks(field, datapointIdx);
|
||||
|
||||
const fieldFmt = field.display || getDisplayProcessor({ field, timeZone, theme });
|
||||
const value = field.values[datapointIdx!];
|
||||
const display = fieldFmt(value);
|
||||
|
||||
const nextStateIdx = findNextStateIndex(field, datapointIdx!);
|
||||
let nextStateTs;
|
||||
if (nextStateIdx) {
|
||||
nextStateTs = xField.values[nextStateIdx!];
|
||||
}
|
||||
|
||||
const stateTs = xField.values[datapointIdx!];
|
||||
let duration: string;
|
||||
|
||||
if (nextStateTs) {
|
||||
duration = nextStateTs && fmtDuration(nextStateTs - stateTs);
|
||||
} else {
|
||||
const to = timeRange.to.valueOf();
|
||||
duration = fmtDuration(to - stateTs);
|
||||
}
|
||||
|
||||
const getContentLabelValue = (): LabelValue[] => {
|
||||
const durationEntry: LabelValue[] = duration ? [{ label: 'Duration', value: duration }] : [];
|
||||
|
||||
return [
|
||||
contentLabelValue = [
|
||||
{
|
||||
label: fieldDisplayName ?? '',
|
||||
label: getFieldDisplayName(field),
|
||||
value: display.text,
|
||||
color: display.color,
|
||||
colorIndicator: ColorIndicator.value,
|
||||
@@ -120,6 +120,62 @@ export const StateTimelineTooltip2 = ({
|
||||
},
|
||||
...durationEntry,
|
||||
];
|
||||
}
|
||||
|
||||
if (mode === TooltipDisplayMode.Multi && !isPinned) {
|
||||
const fields = alignedData.fields;
|
||||
const sortIdx: unknown[] = [];
|
||||
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
const field = fields[i];
|
||||
if (
|
||||
!field ||
|
||||
field === xField ||
|
||||
field.type === FieldType.time ||
|
||||
field.config.custom?.hideFrom?.tooltip ||
|
||||
field.config.custom?.hideFrom?.viz
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fieldFmt = field.display || getDisplayProcessor({ field, timeZone, theme });
|
||||
const v = field.values[dataIdxs[i]!];
|
||||
const display = fieldFmt(v);
|
||||
|
||||
sortIdx.push(v);
|
||||
contentLabelValue.push({
|
||||
label: getFieldDisplayName(field),
|
||||
value: display.text,
|
||||
color: display.color,
|
||||
colorIndicator: ColorIndicator.value,
|
||||
colorPlacement: ColorPlacement.trailing,
|
||||
isActive: seriesIdx === i,
|
||||
});
|
||||
}
|
||||
|
||||
if (sortOrder !== SortOrder.None) {
|
||||
// create sort reference series array, as Array.sort() mutates the original array
|
||||
const sortRef = [...contentLabelValue];
|
||||
const sortFn = arrayUtils.sortValues(sortOrder);
|
||||
|
||||
contentLabelValue.sort((a, b) => {
|
||||
// get compared values indices to retrieve raw values from sortIdx
|
||||
const aIdx = sortRef.indexOf(a);
|
||||
const bIdx = sortRef.indexOf(b);
|
||||
return sortFn(sortIdx[aIdx], sortIdx[bIdx]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const getHeaderLabel = (): LabelValue => {
|
||||
return {
|
||||
label: '',
|
||||
value: from,
|
||||
};
|
||||
};
|
||||
|
||||
const getContentLabelValue = (): LabelValue[] => {
|
||||
return contentLabelValue;
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
identityOverrideProcessor,
|
||||
PanelPlugin,
|
||||
} from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { VisibilityMode } from '@grafana/schema';
|
||||
import { commonOptionsBuilder } from '@grafana/ui';
|
||||
|
||||
@@ -14,7 +15,7 @@ import { NullEditorSettings } from '../timeseries/config';
|
||||
|
||||
import { StateTimelinePanel } from './StateTimelinePanel';
|
||||
import { timelinePanelChangedHandler } from './migrations';
|
||||
import { Options, FieldConfig, defaultOptions, defaultFieldConfig } from './panelcfg.gen';
|
||||
import { defaultFieldConfig, defaultOptions, FieldConfig, Options } from './panelcfg.gen';
|
||||
import { StatTimelineSuggestionsSupplier } from './suggestions';
|
||||
|
||||
export const plugin = new PanelPlugin<Options, FieldConfig>(StateTimelinePanel)
|
||||
@@ -121,7 +122,7 @@ export const plugin = new PanelPlugin<Options, FieldConfig>(StateTimelinePanel)
|
||||
});
|
||||
|
||||
commonOptionsBuilder.addLegendOptions(builder, false);
|
||||
commonOptionsBuilder.addTooltipOptions(builder, true);
|
||||
commonOptionsBuilder.addTooltipOptions(builder, !config.featureToggles.newVizTooltips);
|
||||
})
|
||||
.setSuggestionsSupplier(new StatTimelineSuggestionsSupplier())
|
||||
.setDataSupport({ annotations: true });
|
||||
|
||||
Reference in New Issue
Block a user