Files
grafana/public/app/plugins/panel/timeseries/TimeSeriesTooltip.tsx
Jack Westbrook c9f563e576 Grafana UI: Use package.json exports for internal code (#101815)
* feat(grafana-ui): introduce development exports to prevent importing from grafana/ui/src

* refactor(theme-generation): move theme templates into scripts so themes continue to build

* refactor(frontend): replace  grafana/ui paths that use nested src with /internal or /unstable

* chore(betterer): update better results file

* feat(grafana-ui): support enterprise, remove Text component from internal

* docs(packages): update readme with exporting code conventions
2025-03-12 14:14:32 +01:00

117 lines
3.0 KiB
TypeScript

import { ReactNode } from 'react';
import { DataFrame, Field, FieldType, formattedValueToString, InterpolateFunction, LinkModel } from '@grafana/data';
import { SortOrder, TooltipDisplayMode } from '@grafana/schema/dist/esm/common/common.gen';
import {
VizTooltipContent,
VizTooltipFooter,
VizTooltipHeader,
VizTooltipWrapper,
getContentItems,
VizTooltipItem,
} from '@grafana/ui/internal';
import { getFieldActions } from '../status-history/utils';
import { fmt } from '../xychart/utils';
import { isTooltipScrollable } from './utils';
// exemplar / annotation / time region hovering?
// add annotation UI / alert dismiss UI?
export interface TimeSeriesTooltipProps {
// aligned series frame
series: DataFrame;
// aligned fields that are not series
_rest?: Field[];
// hovered points
dataIdxs: Array<number | null>;
// closest/hovered series
seriesIdx?: number | null;
mode?: TooltipDisplayMode;
sortOrder?: SortOrder;
isPinned: boolean;
annotate?: () => void;
maxHeight?: number;
replaceVariables?: InterpolateFunction;
dataLinks: LinkModel[];
hideZeros?: boolean;
}
export const TimeSeriesTooltip = ({
series,
_rest,
dataIdxs,
seriesIdx,
mode = TooltipDisplayMode.Single,
sortOrder = SortOrder.None,
isPinned,
annotate,
maxHeight,
replaceVariables = (str) => str,
dataLinks,
hideZeros,
}: TimeSeriesTooltipProps) => {
const xField = series.fields[0];
const xVal = formattedValueToString(xField.display!(xField.values[dataIdxs[0]!]));
const contentItems = getContentItems(
series.fields,
xField,
dataIdxs,
seriesIdx,
mode,
sortOrder,
(field) => field.type === FieldType.number || field.type === FieldType.enum,
hideZeros
);
_rest?.forEach((field) => {
if (!field.config.custom?.hideFrom?.tooltip) {
contentItems.push({
label: field.state?.displayName ?? field.name,
value: fmt(field, field.values[dataIdxs[0]!]),
});
}
});
let footer: ReactNode;
if (seriesIdx != null) {
const field = series.fields[seriesIdx];
const hasOneClickLink = dataLinks.some((dataLink) => dataLink.oneClick === true);
if (isPinned || hasOneClickLink) {
const dataIdx = dataIdxs[seriesIdx]!;
const actions = getFieldActions(series, field, replaceVariables, dataIdx);
footer = <VizTooltipFooter dataLinks={dataLinks} actions={actions} annotate={annotate} />;
}
}
const headerItem: VizTooltipItem | null = xField.config.custom?.hideFrom?.tooltip
? null
: {
label: xField.type === FieldType.time ? '' : (xField.state?.displayName ?? xField.name),
value: xVal,
};
return (
<VizTooltipWrapper>
{headerItem != null && <VizTooltipHeader item={headerItem} isPinned={isPinned} />}
<VizTooltipContent
items={contentItems}
isPinned={isPinned}
scrollable={isTooltipScrollable({ mode, maxHeight })}
maxHeight={maxHeight}
/>
{footer}
</VizTooltipWrapper>
);
};