mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Status History: Improve tooltip (#76647)
* feat(state-timeline-tooltip): improve status history tooltip Co-authored-by: Adela Almasan <adela.almasan@grafana.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
parent
b7a332276a
commit
bb316a7c24
@ -357,7 +357,6 @@ export function getConfig(opts: TimelineCoreOptions) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const init = (u: uPlot) => {
|
const init = (u: uPlot) => {
|
||||||
let over = u.over;
|
|
||||||
let chars = '';
|
let chars = '';
|
||||||
for (let i = 32; i <= 126; i++) {
|
for (let i = 32; i <= 126; i++) {
|
||||||
chars += String.fromCharCode(i);
|
chars += String.fromCharCode(i);
|
||||||
@ -367,7 +366,6 @@ export function getConfig(opts: TimelineCoreOptions) {
|
|||||||
// be a bit more conservtive to prevent overlap
|
// be a bit more conservtive to prevent overlap
|
||||||
pxPerChar += 2.5;
|
pxPerChar += 2.5;
|
||||||
|
|
||||||
over.style.overflow = 'hidden';
|
|
||||||
u.root.querySelectorAll<HTMLDivElement>('.u-cursor-pt').forEach((el) => {
|
u.root.querySelectorAll<HTMLDivElement>('.u-cursor-pt').forEach((el) => {
|
||||||
el.style.borderRadius = '0';
|
el.style.borderRadius = '0';
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { CartesianCoords2D, DashboardCursorSync, DataFrame, FieldType, PanelProps } from '@grafana/data';
|
import { CartesianCoords2D, DashboardCursorSync, DataFrame, FieldType, PanelProps } from '@grafana/data';
|
||||||
|
import { config } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
Portal,
|
Portal,
|
||||||
TooltipDisplayMode,
|
TooltipDisplayMode,
|
||||||
|
TooltipPlugin2,
|
||||||
UPlotConfigBuilder,
|
UPlotConfigBuilder,
|
||||||
usePanelContext,
|
usePanelContext,
|
||||||
useTheme2,
|
useTheme2,
|
||||||
@ -11,6 +13,7 @@ import {
|
|||||||
ZoomPlugin,
|
ZoomPlugin,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport';
|
import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport';
|
||||||
|
import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2';
|
||||||
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
|
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
|
||||||
import { TimelineChart } from 'app/core/components/TimelineChart/TimelineChart';
|
import { TimelineChart } from 'app/core/components/TimelineChart/TimelineChart';
|
||||||
import {
|
import {
|
||||||
@ -23,6 +26,7 @@ import { OutsideRangePlugin } from '../timeseries/plugins/OutsideRangePlugin';
|
|||||||
import { getTimezones } from '../timeseries/utils';
|
import { getTimezones } from '../timeseries/utils';
|
||||||
|
|
||||||
import { StatusHistoryTooltip } from './StatusHistoryTooltip';
|
import { StatusHistoryTooltip } from './StatusHistoryTooltip';
|
||||||
|
import { StatusHistoryTooltip2 } from './StatusHistoryTooltip2';
|
||||||
import { Options } from './panelcfg.gen';
|
import { Options } from './panelcfg.gen';
|
||||||
|
|
||||||
const TOOLTIP_OFFSET = 10;
|
const TOOLTIP_OFFSET = 10;
|
||||||
@ -198,10 +202,10 @@ export const StatusHistoryPanel = ({
|
|||||||
{...options}
|
{...options}
|
||||||
mode={TimelineMode.Samples}
|
mode={TimelineMode.Samples}
|
||||||
>
|
>
|
||||||
{(config, alignedFrame) => {
|
{(builder, alignedFrame) => {
|
||||||
if (oldConfig.current !== config) {
|
if (oldConfig.current !== builder) {
|
||||||
oldConfig.current = addTooltipSupport({
|
oldConfig.current = addTooltipSupport({
|
||||||
config,
|
config: builder,
|
||||||
onUPlotClick,
|
onUPlotClick,
|
||||||
setFocusedSeriesIdx,
|
setFocusedSeriesIdx,
|
||||||
setFocusedPointIdx,
|
setFocusedPointIdx,
|
||||||
@ -213,13 +217,39 @@ export const StatusHistoryPanel = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.featureToggles.newVizTooltips) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ZoomPlugin config={config} onZoom={onChangeTimeRange} />
|
{options.tooltip.mode !== TooltipDisplayMode.None && (
|
||||||
{renderTooltip(alignedFrame)}
|
<TooltipPlugin2
|
||||||
<OutsideRangePlugin config={config} onChangeTimeRange={onChangeTimeRange} />
|
config={builder}
|
||||||
|
hoverMode={TooltipHoverMode.xyOne}
|
||||||
|
queryZoom={onChangeTimeRange}
|
||||||
|
render={(u, dataIdxs, seriesIdx, isPinned, dismiss) => {
|
||||||
|
return (
|
||||||
|
<StatusHistoryTooltip2
|
||||||
|
data={frames ?? []}
|
||||||
|
dataIdxs={dataIdxs}
|
||||||
|
alignedData={alignedFrame}
|
||||||
|
seriesIdx={seriesIdx}
|
||||||
|
timeZone={timeZone}
|
||||||
|
isPinned={isPinned}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ZoomPlugin config={builder} onZoom={onChangeTimeRange} />
|
||||||
|
{renderTooltip(alignedFrame)}
|
||||||
|
<OutsideRangePlugin config={builder} onChangeTimeRange={onChangeTimeRange} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
</TimelineChart>
|
</TimelineChart>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
DataFrame,
|
||||||
|
Field,
|
||||||
|
formattedValueToString,
|
||||||
|
getDisplayProcessor,
|
||||||
|
getFieldDisplayName,
|
||||||
|
GrafanaTheme2,
|
||||||
|
TimeZone,
|
||||||
|
LinkModel,
|
||||||
|
} from '@grafana/data';
|
||||||
|
import { useStyles2 } from '@grafana/ui';
|
||||||
|
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
|
||||||
|
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
|
||||||
|
import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader';
|
||||||
|
import { LabelValue } from '@grafana/ui/src/components/VizTooltip/types';
|
||||||
|
|
||||||
|
interface StatusHistoryTooltipProps {
|
||||||
|
data: DataFrame[];
|
||||||
|
dataIdxs: Array<number | null>;
|
||||||
|
alignedData: DataFrame;
|
||||||
|
seriesIdx: number | null | undefined;
|
||||||
|
timeZone: TimeZone;
|
||||||
|
isPinned: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmt(field: Field, val: number): string {
|
||||||
|
if (field.display) {
|
||||||
|
return formattedValueToString(field.display(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${val}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StatusHistoryTooltip2 = ({
|
||||||
|
data,
|
||||||
|
dataIdxs,
|
||||||
|
alignedData,
|
||||||
|
seriesIdx,
|
||||||
|
timeZone,
|
||||||
|
isPinned,
|
||||||
|
}: StatusHistoryTooltipProps) => {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
|
// @todo: check other dataIdx, it can be undefined or null in array
|
||||||
|
const datapointIdx = dataIdxs.find((idx) => idx !== undefined);
|
||||||
|
|
||||||
|
if (!data || datapointIdx == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const xField = alignedData.fields[0];
|
||||||
|
const field = alignedData.fields[seriesIdx!];
|
||||||
|
|
||||||
|
const links: Array<LinkModel<Field>> = [];
|
||||||
|
const linkLookup = new Set<string>();
|
||||||
|
|
||||||
|
if (field.getLinks) {
|
||||||
|
const v = field.values[datapointIdx];
|
||||||
|
const disp = field.display ? field.display(v) : { text: `${v}`, numeric: +v };
|
||||||
|
field.getLinks({ calculatedValue: disp, valueRowIndex: datapointIdx }).forEach((link) => {
|
||||||
|
const key = `${link.title}/${link.href}`;
|
||||||
|
if (!linkLookup.has(key)) {
|
||||||
|
links.push(link);
|
||||||
|
linkLookup.add(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldFmt = field.display || getDisplayProcessor();
|
||||||
|
const value = field.values[datapointIdx!];
|
||||||
|
const display = fieldFmt(value);
|
||||||
|
|
||||||
|
const getHeaderLabel = (): LabelValue => {
|
||||||
|
return {
|
||||||
|
label: '',
|
||||||
|
value: fmt(xField, xField.values[datapointIdx]),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getContentLabelValue = () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: getFieldDisplayName(field),
|
||||||
|
value: fmt(field, field.values[datapointIdx]),
|
||||||
|
color: display.color,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
<VizTooltipHeader headerLabel={getHeaderLabel()} />
|
||||||
|
<VizTooltipContent contentLabelValue={getContentLabelValue()} />
|
||||||
|
{isPinned && <VizTooltipFooter dataLinks={links} canAnnotate={false} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
wrapper: css({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
width: '280px',
|
||||||
|
}),
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user