mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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 <ismailsimsek09@gmail.com>
This commit is contained in:
parent
551a2ac9ec
commit
f37997aec0
@ -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<LinkModel<Field>> = [];
|
||||
const linkLookup = new Set<string>();
|
||||
const links: Record<string, Array<LinkModel<Field>>> = {};
|
||||
|
||||
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) => (
|
||||
<tr key={`${i}/${rowIndex}`} className={i === columnIndex ? styles.highlight : ''}>
|
||||
<th>{v[0]}:</th>
|
||||
<td>{v[2]}</td>
|
||||
<td>{renderWithLinks(v[0], v[2], links)}</td>
|
||||
</tr>
|
||||
))}
|
||||
{mode === TooltipDisplayMode.Single && columnIndex && (
|
||||
<tr key={`${columnIndex}/${rowIndex}`}>
|
||||
<th>{displayValues[columnIndex][0]}:</th>
|
||||
<td>{displayValues[columnIndex][2]}</td>
|
||||
</tr>
|
||||
)}
|
||||
{links.length > 0 && (
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
<VerticalGroup>
|
||||
{links.map((link, i) => (
|
||||
<LinkButton
|
||||
key={i}
|
||||
icon={'external-link-alt'}
|
||||
target={link.target}
|
||||
href={link.href}
|
||||
onClick={link.onClick}
|
||||
fill="text"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{link.title}
|
||||
</LinkButton>
|
||||
))}
|
||||
</VerticalGroup>
|
||||
</td>
|
||||
<td>{renderWithLinks(displayValues[columnIndex][0], displayValues[columnIndex][2], links)}</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
@ -100,6 +78,30 @@ export const DataHoverView = ({ data, rowIndex, columnIndex, sortOrder, mode }:
|
||||
);
|
||||
};
|
||||
|
||||
const renderWithLinks = (key: string, val: string, links: Record<string, Array<LinkModel<Field>>>) =>
|
||||
links[key] ? (
|
||||
<HorizontalGroup>
|
||||
<>
|
||||
{val}
|
||||
{links[key].map((link, i) => (
|
||||
<LinkButton
|
||||
key={i}
|
||||
icon={'external-link-alt'}
|
||||
target={link.target}
|
||||
href={link.href}
|
||||
onClick={link.onClick}
|
||||
fill="text"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{link.title}
|
||||
</LinkButton>
|
||||
))}
|
||||
</>
|
||||
</HorizontalGroup>
|
||||
) : (
|
||||
<>{val}</>
|
||||
);
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
infoWrap: css`
|
||||
padding: 8px;
|
||||
|
@ -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) => {
|
||||
|
@ -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<HeatmapPanelProps> = ({
|
||||
let timeRangeRef = useRef<TimeRange>(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<HeatmapPanelProps> = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<HeatmapHoverView data={info} hover={hover} showHistogram={options.tooltip.yHistogram} />
|
||||
<HeatmapHoverView
|
||||
timeRange={timeRange}
|
||||
data={info}
|
||||
hover={hover}
|
||||
showHistogram={options.tooltip.yHistogram}
|
||||
/>
|
||||
</VizTooltipContainer>
|
||||
)}
|
||||
</Portal>
|
||||
|
@ -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<LinkModel<Field>>
|
||||
): 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user