mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Tooltips: Generate data links in TooltipPlugin2 (#97818)
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
parent
8d4e5a4e09
commit
03d176fae4
@ -40,7 +40,7 @@ export const VizTooltipFooter = ({ dataLinks, actions, annotate }: VizTooltipFoo
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
{dataLinks.length > 0 && <div className={styles.dataLinks}>{renderDataLinks(dataLinks, styles)}</div>}
|
||||
{dataLinks?.length > 0 && <div className={styles.dataLinks}>{renderDataLinks(dataLinks, styles)}</div>}
|
||||
{actions && actions.length > 0 && <div className={styles.dataLinks}>{renderActions(actions)}</div>}
|
||||
{annotate != null && (
|
||||
<div className={styles.addAnnotations}>
|
||||
|
@ -4,7 +4,7 @@ import * as React from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2, LinkModel } from '@grafana/data';
|
||||
import { DashboardCursorSync } from '@grafana/schema';
|
||||
|
||||
import { useStyles2 } from '../../../themes';
|
||||
@ -27,6 +27,8 @@ export const enum TooltipHoverMode {
|
||||
xyOne,
|
||||
}
|
||||
|
||||
type GeDataLinksCallback = (seriesIdx: number, dataIdx: number) => LinkModel[];
|
||||
|
||||
interface TooltipPlugin2Props {
|
||||
config: UPlotConfigBuilder;
|
||||
hoverMode: TooltipHoverMode;
|
||||
@ -40,6 +42,7 @@ interface TooltipPlugin2Props {
|
||||
clientZoom?: boolean;
|
||||
|
||||
onSelectRange?: OnSelectRangeCallback;
|
||||
getDataLinks?: GeDataLinksCallback;
|
||||
|
||||
render: (
|
||||
u: uPlot,
|
||||
@ -49,7 +52,8 @@ interface TooltipPlugin2Props {
|
||||
dismiss: () => void,
|
||||
// selected time range (for annotation triggering)
|
||||
timeRange: TimeRange2 | null,
|
||||
viaSync: boolean
|
||||
viaSync: boolean,
|
||||
dataLinks: LinkModel[]
|
||||
) => React.ReactNode;
|
||||
|
||||
maxWidth?: number;
|
||||
@ -102,6 +106,8 @@ const MIN_ZOOM_DIST = 5;
|
||||
|
||||
const maybeZoomAction = (e?: MouseEvent | null) => e != null && !e.ctrlKey && !e.metaKey;
|
||||
|
||||
const getDataLinksFallback: GeDataLinksCallback = () => [];
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
@ -115,6 +121,7 @@ export const TooltipPlugin2 = ({
|
||||
maxWidth,
|
||||
syncMode = DashboardCursorSync.Off,
|
||||
syncScope = 'global', // eventsScope
|
||||
getDataLinks = getDataLinksFallback,
|
||||
}: TooltipPlugin2Props) => {
|
||||
const domRef = useRef<HTMLDivElement>(null);
|
||||
const portalRoot = useRef<HTMLElement | null>(null);
|
||||
@ -131,6 +138,9 @@ export const TooltipPlugin2 = ({
|
||||
const renderRef = useRef(render);
|
||||
renderRef.current = render;
|
||||
|
||||
const getLinksRef = useRef(getDataLinks);
|
||||
getLinksRef.current = getDataLinks;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
sizeRef.current = {
|
||||
width: 0,
|
||||
@ -187,6 +197,7 @@ export const TooltipPlugin2 = ({
|
||||
let seriesIdxs: Array<number | null> = plot?.cursor.idxs!.slice()!;
|
||||
let closestSeriesIdx: number | null = null;
|
||||
let viaSync = false;
|
||||
let dataLinks: LinkModel[] = [];
|
||||
|
||||
let pendingRender = false;
|
||||
let pendingPinned = false;
|
||||
@ -242,7 +253,16 @@ export const TooltipPlugin2 = ({
|
||||
isHovering: _isHovering,
|
||||
contents:
|
||||
_isHovering || selectedRange != null
|
||||
? renderRef.current(_plot!, seriesIdxs, closestSeriesIdx, _isPinned, dismiss, selectedRange, viaSync)
|
||||
? renderRef.current(
|
||||
_plot!,
|
||||
seriesIdxs,
|
||||
closestSeriesIdx,
|
||||
_isPinned,
|
||||
dismiss,
|
||||
selectedRange,
|
||||
viaSync,
|
||||
dataLinks
|
||||
)
|
||||
: null,
|
||||
dismiss,
|
||||
};
|
||||
@ -257,6 +277,8 @@ export const TooltipPlugin2 = ({
|
||||
_isPinned = false;
|
||||
_isHovering = false;
|
||||
_plot!.setCursor({ left: -10, top: -10 });
|
||||
dataLinks = [];
|
||||
|
||||
scheduleRender(prevIsPinned);
|
||||
};
|
||||
|
||||
@ -313,6 +335,8 @@ export const TooltipPlugin2 = ({
|
||||
}
|
||||
// only pinnable tooltip is visible *and* is within proximity to series/point
|
||||
else if (_isHovering && closestSeriesIdx != null && !_isPinned) {
|
||||
dataLinks = getLinksRef.current(closestSeriesIdx!, seriesIdxs[closestSeriesIdx!]!);
|
||||
|
||||
setTimeout(() => {
|
||||
_isPinned = true;
|
||||
scheduleRender(true);
|
||||
|
@ -157,7 +157,10 @@ export const BarChartPanel = (props: PanelProps<Options>) => {
|
||||
hoverMode={
|
||||
options.tooltip.mode === TooltipDisplayMode.Single ? TooltipHoverMode.xOne : TooltipHoverMode.xAll
|
||||
}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2) => {
|
||||
getDataLinks={(seriesIdx: number, dataIdx: number) =>
|
||||
vizSeries[0].fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? []
|
||||
}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync, dataLinks) => {
|
||||
return (
|
||||
<TimeSeriesTooltip
|
||||
series={vizSeries[0]}
|
||||
@ -169,6 +172,7 @@ export const BarChartPanel = (props: PanelProps<Options>) => {
|
||||
isPinned={isPinned}
|
||||
maxHeight={options.tooltip.maxHeight}
|
||||
replaceVariables={replaceVariables}
|
||||
dataLinks={dataLinks}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -282,7 +282,10 @@ export const CandlestickPanel = ({
|
||||
clientZoom={true}
|
||||
syncMode={cursorSync}
|
||||
syncScope={eventsScope}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange2, viaSync) => {
|
||||
getDataLinks={(seriesIdx: number, dataIdx: number) =>
|
||||
alignedFrame.fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? []
|
||||
}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange2, viaSync, dataLinks) => {
|
||||
if (enableAnnotationCreation && timeRange2 != null) {
|
||||
setNewAnnotationRange(timeRange2);
|
||||
dismiss();
|
||||
@ -307,6 +310,7 @@ export const CandlestickPanel = ({
|
||||
annotate={enableAnnotationCreation ? annotate : undefined}
|
||||
maxHeight={options.tooltip.maxHeight}
|
||||
replaceVariables={replaceVariables}
|
||||
dataLinks={dataLinks}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -172,7 +172,10 @@ export const StateTimelinePanel = ({
|
||||
queryZoom={onChangeTimeRange}
|
||||
syncMode={cursorSync}
|
||||
syncScope={eventsScope}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync) => {
|
||||
getDataLinks={(seriesIdx: number, dataIdx: number) =>
|
||||
alignedFrame.fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? []
|
||||
}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync, dataLinks) => {
|
||||
if (enableAnnotationCreation && timeRange2 != null) {
|
||||
setNewAnnotationRange(timeRange2);
|
||||
dismiss();
|
||||
@ -199,6 +202,7 @@ export const StateTimelinePanel = ({
|
||||
withDuration={true}
|
||||
maxHeight={options.tooltip.maxHeight}
|
||||
replaceVariables={replaceVariables}
|
||||
dataLinks={dataLinks}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -11,7 +11,7 @@ import { VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types';
|
||||
import { getContentItems } from '@grafana/ui/src/components/VizTooltip/utils';
|
||||
import { findNextStateIndex, fmtDuration } from 'app/core/components/TimelineChart/utils';
|
||||
|
||||
import { getDataLinks, getFieldActions } from '../status-history/utils';
|
||||
import { getFieldActions } from '../status-history/utils';
|
||||
import { TimeSeriesTooltipProps } from '../timeseries/TimeSeriesTooltip';
|
||||
import { isTooltipScrollable } from '../timeseries/utils';
|
||||
|
||||
@ -32,6 +32,7 @@ export const StateTimelineTooltip2 = ({
|
||||
withDuration,
|
||||
maxHeight,
|
||||
replaceVariables,
|
||||
dataLinks,
|
||||
}: StateTimelineTooltip2Props) => {
|
||||
const xField = series.fields[0];
|
||||
|
||||
@ -70,10 +71,9 @@ export const StateTimelineTooltip2 = ({
|
||||
if (isPinned && seriesIdx != null) {
|
||||
const field = series.fields[seriesIdx];
|
||||
const dataIdx = dataIdxs[seriesIdx]!;
|
||||
const links = getDataLinks(field, dataIdx);
|
||||
const actions = getFieldActions(series, field, replaceVariables!, dataIdx);
|
||||
|
||||
footer = <VizTooltipFooter dataLinks={links} annotate={annotate} actions={actions} />;
|
||||
footer = <VizTooltipFooter dataLinks={dataLinks} annotate={annotate} actions={actions} />;
|
||||
}
|
||||
|
||||
const headerItem: VizTooltipItem = {
|
||||
|
@ -104,7 +104,10 @@ export const StatusHistoryPanel = ({
|
||||
queryZoom={onChangeTimeRange}
|
||||
syncMode={cursorSync}
|
||||
syncScope={eventsScope}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync) => {
|
||||
getDataLinks={(seriesIdx: number, dataIdx: number) =>
|
||||
alignedFrame.fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? []
|
||||
}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync, dataLinks) => {
|
||||
if (enableAnnotationCreation && timeRange2 != null) {
|
||||
setNewAnnotationRange(timeRange2);
|
||||
dismiss();
|
||||
@ -131,6 +134,7 @@ export const StatusHistoryPanel = ({
|
||||
withDuration={false}
|
||||
maxHeight={options.tooltip.maxHeight}
|
||||
replaceVariables={replaceVariables}
|
||||
dataLinks={dataLinks}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -106,7 +106,10 @@ export const TimeSeriesPanel = ({
|
||||
clientZoom={true}
|
||||
syncMode={cursorSync}
|
||||
syncScope={eventsScope}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange2, viaSync) => {
|
||||
getDataLinks={(seriesIdx: number, dataIdx: number) =>
|
||||
alignedFrame.fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? []
|
||||
}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange2, viaSync, dataLinks) => {
|
||||
if (enableAnnotationCreation && timeRange2 != null) {
|
||||
setNewAnnotationRange(timeRange2);
|
||||
dismiss();
|
||||
@ -132,6 +135,7 @@ export const TimeSeriesPanel = ({
|
||||
annotate={enableAnnotationCreation ? annotate : undefined}
|
||||
maxHeight={options.tooltip.maxHeight}
|
||||
replaceVariables={replaceVariables}
|
||||
dataLinks={dataLinks}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { DataFrame, Field, FieldType, formattedValueToString, InterpolateFunction } from '@grafana/data';
|
||||
import { DataFrame, Field, FieldType, formattedValueToString, InterpolateFunction, LinkModel } from '@grafana/data';
|
||||
import { SortOrder, TooltipDisplayMode } from '@grafana/schema/dist/esm/common/common.gen';
|
||||
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
|
||||
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
|
||||
@ -9,7 +9,7 @@ import { VizTooltipWrapper } from '@grafana/ui/src/components/VizTooltip/VizTool
|
||||
import { VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types';
|
||||
import { getContentItems } from '@grafana/ui/src/components/VizTooltip/utils';
|
||||
|
||||
import { getDataLinks, getFieldActions } from '../status-history/utils';
|
||||
import { getFieldActions } from '../status-history/utils';
|
||||
import { fmt } from '../xychart/utils';
|
||||
|
||||
import { isTooltipScrollable } from './utils';
|
||||
@ -37,6 +37,7 @@ export interface TimeSeriesTooltipProps {
|
||||
maxHeight?: number;
|
||||
|
||||
replaceVariables?: InterpolateFunction;
|
||||
dataLinks: LinkModel[];
|
||||
}
|
||||
|
||||
export const TimeSeriesTooltip = ({
|
||||
@ -50,6 +51,7 @@ export const TimeSeriesTooltip = ({
|
||||
annotate,
|
||||
maxHeight,
|
||||
replaceVariables,
|
||||
dataLinks,
|
||||
}: TimeSeriesTooltipProps) => {
|
||||
const xField = series.fields[0];
|
||||
const xVal = formattedValueToString(xField.display!(xField.values[dataIdxs[0]!]));
|
||||
@ -78,10 +80,9 @@ export const TimeSeriesTooltip = ({
|
||||
if (isPinned && seriesIdx != null) {
|
||||
const field = series.fields[seriesIdx];
|
||||
const dataIdx = dataIdxs[seriesIdx]!;
|
||||
const links = getDataLinks(field, dataIdx);
|
||||
const actions = getFieldActions(series, field, replaceVariables!, dataIdx);
|
||||
|
||||
footer = <VizTooltipFooter dataLinks={links} actions={actions} annotate={annotate} />;
|
||||
footer = <VizTooltipFooter dataLinks={dataLinks} actions={actions} annotate={annotate} />;
|
||||
}
|
||||
|
||||
const headerItem: VizTooltipItem | null = xField.config.custom?.hideFrom?.tooltip
|
||||
|
@ -119,7 +119,10 @@ export const TrendPanel = ({
|
||||
hoverMode={
|
||||
options.tooltip.mode === TooltipDisplayMode.Single ? TooltipHoverMode.xOne : TooltipHoverMode.xAll
|
||||
}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned = false) => {
|
||||
getDataLinks={(seriesIdx: number, dataIdx: number) =>
|
||||
alignedDataFrame.fields[seriesIdx]!.getLinks?.({ valueRowIndex: dataIdx }) ?? []
|
||||
}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange, viaSync, dataLinks) => {
|
||||
return (
|
||||
<TimeSeriesTooltip
|
||||
series={alignedDataFrame}
|
||||
@ -130,6 +133,7 @@ export const TrendPanel = ({
|
||||
isPinned={isPinned}
|
||||
maxHeight={options.tooltip.maxHeight}
|
||||
replaceVariables={replaceVariables}
|
||||
dataLinks={dataLinks}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
Loading…
Reference in New Issue
Block a user