diff --git a/public/app/core/components/TimelineChart/timeline.ts b/public/app/core/components/TimelineChart/timeline.ts index 13fdd76fb4d..8cd9817921a 100644 --- a/public/app/core/components/TimelineChart/timeline.ts +++ b/public/app/core/components/TimelineChart/timeline.ts @@ -357,7 +357,6 @@ export function getConfig(opts: TimelineCoreOptions) { }; const init = (u: uPlot) => { - let over = u.over; let chars = ''; for (let i = 32; i <= 126; i++) { chars += String.fromCharCode(i); @@ -367,7 +366,6 @@ export function getConfig(opts: TimelineCoreOptions) { // be a bit more conservtive to prevent overlap pxPerChar += 2.5; - over.style.overflow = 'hidden'; u.root.querySelectorAll('.u-cursor-pt').forEach((el) => { el.style.borderRadius = '0'; }); diff --git a/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx b/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx index be421751558..67514d22153 100644 --- a/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx +++ b/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx @@ -1,9 +1,11 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { CartesianCoords2D, DashboardCursorSync, DataFrame, FieldType, PanelProps } from '@grafana/data'; +import { config } from '@grafana/runtime'; import { Portal, TooltipDisplayMode, + TooltipPlugin2, UPlotConfigBuilder, usePanelContext, useTheme2, @@ -11,6 +13,7 @@ import { ZoomPlugin, } from '@grafana/ui'; 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 { TimelineChart } from 'app/core/components/TimelineChart/TimelineChart'; import { @@ -23,6 +26,7 @@ import { OutsideRangePlugin } from '../timeseries/plugins/OutsideRangePlugin'; import { getTimezones } from '../timeseries/utils'; import { StatusHistoryTooltip } from './StatusHistoryTooltip'; +import { StatusHistoryTooltip2 } from './StatusHistoryTooltip2'; import { Options } from './panelcfg.gen'; const TOOLTIP_OFFSET = 10; @@ -198,10 +202,10 @@ export const StatusHistoryPanel = ({ {...options} mode={TimelineMode.Samples} > - {(config, alignedFrame) => { - if (oldConfig.current !== config) { + {(builder, alignedFrame) => { + if (oldConfig.current !== builder) { oldConfig.current = addTooltipSupport({ - config, + config: builder, onUPlotClick, setFocusedSeriesIdx, setFocusedPointIdx, @@ -213,13 +217,39 @@ export const StatusHistoryPanel = ({ }); } - return ( - <> - - {renderTooltip(alignedFrame)} - - - ); + if (config.featureToggles.newVizTooltips) { + return ( + <> + {options.tooltip.mode !== TooltipDisplayMode.None && ( + { + return ( + + ); + }} + /> + )} + + ); + } else { + return ( + <> + + {renderTooltip(alignedFrame)} + + + ); + } }} ); diff --git a/public/app/plugins/panel/status-history/StatusHistoryTooltip2.tsx b/public/app/plugins/panel/status-history/StatusHistoryTooltip2.tsx new file mode 100644 index 00000000000..94380a76d71 --- /dev/null +++ b/public/app/plugins/panel/status-history/StatusHistoryTooltip2.tsx @@ -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; + 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> = []; + const linkLookup = new Set(); + + 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 ( +
+ + + {isPinned && } +
+ ); +}; + +const getStyles = (theme: GrafanaTheme2) => ({ + wrapper: css({ + display: 'flex', + flexDirection: 'column', + width: '280px', + }), +});