From f37997aec04a715190c48244a0ba4f53d4c83070 Mon Sep 17 00:00:00 2001 From: Galen Kistler <109082771+gtk-grafana@users.noreply.github.com> Date: Thu, 8 Dec 2022 12:02:17 -0600 Subject: [PATCH] Prometheus: Add traceID link to heatmap exemplar popover (#60039) * Add traceID link to heatmap exemplar popover * Render links for each field individually Co-authored-by: ismail simsek --- .../panel/geomap/components/DataHoverView.tsx | 62 ++++++++++--------- .../panel/heatmap/HeatmapHoverView.tsx | 11 +++- .../plugins/panel/heatmap/HeatmapPanel.tsx | 20 ++++-- public/app/plugins/panel/heatmap/fields.ts | 15 ++++- 4 files changed, 72 insertions(+), 36 deletions(-) diff --git a/public/app/plugins/panel/geomap/components/DataHoverView.tsx b/public/app/plugins/panel/geomap/components/DataHoverView.tsx index e2506bb1e5d..23accd4617b 100644 --- a/public/app/plugins/panel/geomap/components/DataHoverView.tsx +++ b/public/app/plugins/panel/geomap/components/DataHoverView.tsx @@ -11,7 +11,7 @@ import { LinkModel, } from '@grafana/data'; import { SortOrder, TooltipDisplayMode } from '@grafana/schema'; -import { LinkButton, useStyles2, VerticalGroup } from '@grafana/ui'; +import { HorizontalGroup, LinkButton, useStyles2 } from '@grafana/ui'; export interface Props { data?: DataFrame; // source data @@ -35,19 +35,18 @@ export const DataHoverView = ({ data, rowIndex, columnIndex, sortOrder, mode }: } const displayValues: Array<[string, unknown, string]> = []; - const links: Array> = []; - const linkLookup = new Set(); + const links: Record>> = {}; for (const f of visibleFields) { const v = f.values.get(rowIndex); const disp = f.display ? f.display(v) : { text: `${v}`, numeric: +v }; if (f.getLinks) { f.getLinks({ calculatedValue: disp, valueRowIndex: rowIndex }).forEach((link) => { - const key = `${link.title}/${link.href}`; - if (!linkLookup.has(key)) { - links.push(link); - linkLookup.add(key); + const key = getFieldDisplayName(f, data); + if (!links[key]) { + links[key] = []; } + links[key].push(link); }); } @@ -65,34 +64,13 @@ export const DataHoverView = ({ data, rowIndex, columnIndex, sortOrder, mode }: displayValues.map((v, i) => ( {v[0]}: - {v[2]} + {renderWithLinks(v[0], v[2], links)} ))} {mode === TooltipDisplayMode.Single && columnIndex && ( {displayValues[columnIndex][0]}: - {displayValues[columnIndex][2]} - - )} - {links.length > 0 && ( - - - - {links.map((link, i) => ( - - {link.title} - - ))} - - + {renderWithLinks(displayValues[columnIndex][0], displayValues[columnIndex][2], links)} )} @@ -100,6 +78,30 @@ export const DataHoverView = ({ data, rowIndex, columnIndex, sortOrder, mode }: ); }; +const renderWithLinks = (key: string, val: string, links: Record>>) => + links[key] ? ( + + <> + {val} + {links[key].map((link, i) => ( + + {link.title} + + ))} + + + ) : ( + <>{val} + ); + const getStyles = (theme: GrafanaTheme2) => ({ infoWrap: css` padding: 8px; diff --git a/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx b/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx index a615bc6d8f4..79733b54a1f 100644 --- a/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx +++ b/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx @@ -1,6 +1,14 @@ import React, { useEffect, useRef, useState } from 'react'; -import { DataFrameType, Field, FieldType, formattedValueToString, getFieldDisplayName, LinkModel } from '@grafana/data'; +import { + DataFrameType, + Field, + FieldType, + formattedValueToString, + getFieldDisplayName, + LinkModel, + TimeRange, +} from '@grafana/data'; import { LinkButton, VerticalGroup } from '@grafana/ui'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { isHeatmapCellsDense, readHeatmapRowsCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap'; @@ -15,6 +23,7 @@ type Props = { data: HeatmapData; hover: HeatmapHoverEvent; showHistogram?: boolean; + timeRange: TimeRange; }; export const HeatmapHoverView = (props: Props) => { diff --git a/public/app/plugins/panel/heatmap/HeatmapPanel.tsx b/public/app/plugins/panel/heatmap/HeatmapPanel.tsx index f46a42591e8..73f3f9eb0b3 100644 --- a/public/app/plugins/panel/heatmap/HeatmapPanel.tsx +++ b/public/app/plugins/panel/heatmap/HeatmapPanel.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/css'; import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { DataFrameType, GrafanaTheme2, PanelProps, TimeRange } from '@grafana/data'; +import { DataFrame, DataFrameType, Field, getLinksSupplier, GrafanaTheme2, PanelProps, TimeRange } from '@grafana/data'; import { PanelDataErrorView } from '@grafana/runtime'; import { ScaleDistributionConfig } from '@grafana/schema'; import { @@ -47,13 +47,20 @@ export const HeatmapPanel: React.FC = ({ let timeRangeRef = useRef(timeRange); timeRangeRef.current = timeRange; + const getFieldLinksSupplier = useCallback( + (exemplars: DataFrame, field: Field) => { + return getLinksSupplier(exemplars, field, field.state?.scopedVars ?? {}, replaceVariables); + }, + [replaceVariables] + ); + const info = useMemo(() => { try { - return prepareHeatmapData(data, options, theme); + return prepareHeatmapData(data, options, theme, getFieldLinksSupplier); } catch (ex) { return { warning: `${ex}` }; } - }, [data, options, theme]); + }, [data, options, theme, getFieldLinksSupplier]); const facets = useMemo(() => { let exemplarsXFacet: number[] = []; // "Time" field @@ -218,7 +225,12 @@ export const HeatmapPanel: React.FC = ({ /> )} - + )} diff --git a/public/app/plugins/panel/heatmap/fields.ts b/public/app/plugins/panel/heatmap/fields.ts index d6491a31161..74f0bf429c0 100644 --- a/public/app/plugins/panel/heatmap/fields.ts +++ b/public/app/plugins/panel/heatmap/fields.ts @@ -6,9 +6,11 @@ import { formattedValueToString, getDisplayProcessor, GrafanaTheme2, + LinkModel, outerJoinDataFrames, PanelData, ValueFormatter, + ValueLinkConfig, } from '@grafana/data'; import { calculateHeatmapFromData, @@ -52,7 +54,12 @@ export interface HeatmapData { warning?: string; } -export function prepareHeatmapData(data: PanelData, options: PanelOptions, theme: GrafanaTheme2): HeatmapData { +export function prepareHeatmapData( + data: PanelData, + options: PanelOptions, + theme: GrafanaTheme2, + getFieldLinks?: (exemplars: DataFrame, field: Field) => (config: ValueLinkConfig) => Array> +): HeatmapData { let frames = data.series; if (!frames?.length) { return {}; @@ -60,6 +67,12 @@ export function prepareHeatmapData(data: PanelData, options: PanelOptions, theme const exemplars = data.annotations?.find((f) => f.name === 'exemplar'); + if (getFieldLinks) { + exemplars?.fields.forEach((field, index) => { + exemplars.fields[index].getLinks = getFieldLinks(exemplars, field); + }); + } + if (options.calculate) { return getDenseHeatmapData(calculateHeatmapFromData(frames, options.calculation ?? {}), exemplars, options, theme); }