StateTimeline: Share cursor with rest of the panels (#41038)

* First working version of shared cursor for state timeline

* Only publish x value for time series

* Don't send legacy graph event

* Don't add y scale to cursor sync

* Snap cursor to the bottom of the canvas when sync is out of bounds

* Fix snapshot
This commit is contained in:
Zoltán Bedi 2021-11-05 18:52:40 +01:00 committed by GitHub
parent d69ffe0e44
commit af61839a26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 63 additions and 7 deletions

View File

@ -95,7 +95,7 @@ Object {
],
"scales": Array [
"x",
"__fixed",
null,
],
},
},

View File

@ -363,7 +363,8 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor
},
},
// ??? setSeries: syncMode === DashboardCursorSync.Tooltip,
scales: builder.scaleKeys,
//TODO: remove any once https://github.com/leeoniya/uPlot/pull/611 got merged or the typing is fixed
scales: [xScaleKey, null as any],
match: [() => true, () => true],
};
}

View File

@ -181,6 +181,11 @@ export function findMidPointYPosition(u: uPlot, idx: number) {
y = u.valToPos((min || max)!, u.series[(sMaxIdx || sMinIdx)!].scale!);
}
// if y is out of canvas bounds, snap it to the bottom
if (y !== undefined && y < 0) {
y = u.bbox.height / devicePixelRatio;
}
return y;
}

View File

@ -1,6 +1,6 @@
import React, { useCallback, useMemo } from 'react';
import { DataFrame, PanelProps } from '@grafana/data';
import { TooltipPlugin, useTheme2, ZoomPlugin } from '@grafana/ui';
import { TooltipPlugin, useTheme2, ZoomPlugin, usePanelContext } from '@grafana/ui';
import { TimelineMode, TimelineOptions } from './types';
import { TimelineChart } from './TimelineChart';
import { prepareTimelineFields, prepareTimelineLegendItems } from './utils';
@ -22,6 +22,7 @@ export const StateTimelinePanel: React.FC<TimelinePanelProps> = ({
onChangeTimeRange,
}) => {
const theme = useTheme2();
const { sync } = usePanelContext();
const { frames, warn } = useMemo(() => prepareTimelineFields(data?.series, options.mergeValues ?? true, theme), [
data,
@ -103,6 +104,7 @@ export const StateTimelinePanel: React.FC<TimelinePanelProps> = ({
<ZoomPlugin config={config} onZoom={onChangeTimeRange} />
<TooltipPlugin
data={alignedFrame}
sync={sync}
config={config}
mode={options.tooltip.mode}
timeZone={timeZone}

View File

@ -36,12 +36,13 @@ export class TimelineChart extends React.Component<TimelineProps> {
prepConfig = (alignedFrame: DataFrame, allFrames: DataFrame[], getTimeRange: () => TimeRange) => {
this.panelContext = this.context as PanelContext;
const { eventBus } = this.panelContext;
const { eventBus, sync } = this.panelContext;
return preparePlotConfigBuilder({
frame: alignedFrame,
getTimeRange,
eventBus,
sync,
allFrames: this.props.frames,
...this.props,

View File

@ -1,4 +1,5 @@
import { OptionsWithTooltip, OptionsWithLegend, HideableFieldConfig, VisibilityMode } from '@grafana/schema';
import { DashboardCursorSync } from '@grafana/data';
import { HideableFieldConfig, OptionsWithLegend, OptionsWithTooltip, VisibilityMode } from '@grafana/schema';
/**
* @alpha
@ -15,6 +16,8 @@ export interface TimelineOptions extends OptionsWithLegend, OptionsWithTooltip {
mergeValues?: boolean;
// only used in "changes" mode (state-timeline)
alignValue?: TimelineValueAlignment;
sync?: DashboardCursorSync;
}
export type TimelineValueAlignment = 'center' | 'left' | 'right';

View File

@ -3,6 +3,10 @@ import { XYFieldMatchers } from '@grafana/ui/src/components/GraphNG/types';
import {
ArrayVector,
DataFrame,
DashboardCursorSync,
DataHoverPayload,
DataHoverEvent,
DataHoverClearEvent,
FALLBACK_COLOR,
Field,
FieldColorModeId,
@ -58,6 +62,8 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
timeZone,
getTimeRange,
mode,
eventBus,
sync,
rowHeight,
colWidth,
showValue,
@ -65,6 +71,9 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
}) => {
const builder = new UPlotConfigBuilder(timeZone);
const xScaleUnit = 'time';
const xScaleKey = 'x';
const isDiscrete = (field: Field) => {
const mode = field.config?.color?.mode;
return !(mode && field.display && mode.startsWith('continuous-'));
@ -116,6 +125,13 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
let hoveredDataIdx: number | null = null;
const coreConfig = getConfig(opts);
const payload: DataHoverPayload = {
point: {
[xScaleUnit]: null,
[FIXED_UNIT]: null,
},
data: frame,
};
builder.addHook('init', coreConfig.init);
builder.addHook('drawClear', coreConfig.drawClear);
@ -148,7 +164,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
builder.setCursor(coreConfig.cursor);
builder.addScale({
scaleKey: 'x',
scaleKey: xScaleKey,
isTime: true,
orientation: ScaleOrientation.Horizontal,
direction: ScaleDirection.Right,
@ -164,7 +180,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
});
builder.addAxis({
scaleKey: 'x',
scaleKey: xScaleKey,
isTime: true,
splits: coreConfig.xSplits!,
placement: AxisPlacement.Bottom,
@ -219,6 +235,34 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
});
}
if (sync !== DashboardCursorSync.Off) {
let cursor: Partial<uPlot.Cursor> = {};
cursor.sync = {
key: '__global_',
filters: {
pub: (type: string, src: uPlot, x: number, y: number, w: number, h: number, dataIdx: number) => {
payload.rowIndex = dataIdx;
if (x < 0 && y < 0) {
payload.point[xScaleUnit] = null;
payload.point[FIXED_UNIT] = null;
eventBus.publish(new DataHoverClearEvent());
} else {
payload.point[xScaleUnit] = src.posToVal(x, xScaleKey);
payload.point.panelRelY = y > 0 ? y / h : 1; // used for old graph panel to position tooltip
payload.down = undefined;
eventBus.publish(new DataHoverEvent(payload));
}
return true;
},
},
//TODO: remove any once https://github.com/leeoniya/uPlot/pull/611 got merged or the typing is fixed
scales: [xScaleKey, null as any],
};
builder.setSync();
builder.setCursor(cursor);
}
return builder;
};