diff --git a/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx b/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx
index b41fb29c944..05b05f7518d 100644
--- a/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx
+++ b/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx
@@ -40,7 +40,7 @@ export const VizTooltipFooter = ({ dataLinks, actions, annotate }: VizTooltipFoo
return (
- {dataLinks.length > 0 &&
{renderDataLinks(dataLinks, styles)}
}
+ {dataLinks?.length > 0 &&
{renderDataLinks(dataLinks, styles)}
}
{actions && actions.length > 0 &&
{renderActions(actions)}
}
{annotate != null && (
diff --git a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx
index 54b6648efef..04756f2f924 100644
--- a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx
+++ b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx
@@ -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(null);
const portalRoot = useRef(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 = 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);
diff --git a/public/app/plugins/panel/barchart/BarChartPanel.tsx b/public/app/plugins/panel/barchart/BarChartPanel.tsx
index ce83432e202..5b03f0fbc50 100644
--- a/public/app/plugins/panel/barchart/BarChartPanel.tsx
+++ b/public/app/plugins/panel/barchart/BarChartPanel.tsx
@@ -157,7 +157,10 @@ export const BarChartPanel = (props: PanelProps) => {
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 (
) => {
isPinned={isPinned}
maxHeight={options.tooltip.maxHeight}
replaceVariables={replaceVariables}
+ dataLinks={dataLinks}
/>
);
}}
diff --git a/public/app/plugins/panel/candlestick/CandlestickPanel.tsx b/public/app/plugins/panel/candlestick/CandlestickPanel.tsx
index acddd0a5727..4ed97d59456 100644
--- a/public/app/plugins/panel/candlestick/CandlestickPanel.tsx
+++ b/public/app/plugins/panel/candlestick/CandlestickPanel.tsx
@@ -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}
/>
);
}}
diff --git a/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx b/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx
index df2df2c811c..81e272a6b98 100644
--- a/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx
+++ b/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx
@@ -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}
/>
);
}}
diff --git a/public/app/plugins/panel/state-timeline/StateTimelineTooltip2.tsx b/public/app/plugins/panel/state-timeline/StateTimelineTooltip2.tsx
index d4df7d88187..a2f2665a11d 100644
--- a/public/app/plugins/panel/state-timeline/StateTimelineTooltip2.tsx
+++ b/public/app/plugins/panel/state-timeline/StateTimelineTooltip2.tsx
@@ -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 = ;
+ footer = ;
}
const headerItem: VizTooltipItem = {
diff --git a/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx b/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx
index 5f09c96ca52..fd1164c85f3 100644
--- a/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx
+++ b/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx
@@ -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}
/>
);
}}
diff --git a/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx b/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx
index fb9076f8a01..9cd2ccf97a3 100644
--- a/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx
+++ b/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx
@@ -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}
/>
);
}}
diff --git a/public/app/plugins/panel/timeseries/TimeSeriesTooltip.tsx b/public/app/plugins/panel/timeseries/TimeSeriesTooltip.tsx
index 5ea7e95fe31..43efd330f17 100644
--- a/public/app/plugins/panel/timeseries/TimeSeriesTooltip.tsx
+++ b/public/app/plugins/panel/timeseries/TimeSeriesTooltip.tsx
@@ -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 = ;
+ footer = ;
}
const headerItem: VizTooltipItem | null = xField.config.custom?.hideFrom?.tooltip
diff --git a/public/app/plugins/panel/trend/TrendPanel.tsx b/public/app/plugins/panel/trend/TrendPanel.tsx
index b787f799d92..c9175dfb246 100644
--- a/public/app/plugins/panel/trend/TrendPanel.tsx
+++ b/public/app/plugins/panel/trend/TrendPanel.tsx
@@ -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 (
);
}}