Compare commits

...

5 Commits

Author SHA1 Message Date
Abhi Kumar
169c10a8fb Merge branch 'main' of https://github.com/SigNoz/signoz into chore/tooltip-events 2026-04-24 16:27:50 +05:30
Abhi Kumar
a2ab816ebb chore: added todo for event removal 2026-04-24 16:27:40 +05:30
Abhi kumar
51e143bb7c Merge branch 'main' into chore/tooltip-events 2026-04-24 16:23:20 +05:30
Abhi Kumar
5dc7c83359 chore: pr review comments 2026-04-24 16:22:18 +05:30
Abhi Kumar
a2b9cec8e4 chore: added tooltip events + minor ui fixes 2026-04-24 15:49:29 +05:30
7 changed files with 87 additions and 22 deletions

View File

@@ -3,6 +3,9 @@ export enum Events {
UPDATE_GRAPH_MANAGER_TABLE = 'UPDATE_GRAPH_MANAGER_TABLE',
TABLE_COLUMNS_DATA = 'TABLE_COLUMNS_DATA',
SLOW_API_WARNING = 'SLOW_API_WARNING',
TOOLTIP_PINNED = 'TOOLTIP_PINNED',
TOOLTIP_UNPINNED = 'TOOLTIP_UNPINNED',
TOOLTIP_CONTENT_SCROLLED = 'TOOLTIP_CONTENT_SCROLLED',
}
export enum InfraMonitoringEvents {

View File

@@ -14,7 +14,7 @@ import uPlot from 'uplot';
import { ChartProps } from '../types';
const TOOLTIP_WIDTH_PADDING = 120;
const TOOLTIP_MIN_WIDTH = 200;
const TOOLTIP_MIN_WIDTH = 300;
export default function ChartWrapper({
legendConfig = { position: LegendPosition.BOTTOM },

View File

@@ -4,15 +4,38 @@
justify-content: space-between;
gap: 10px;
padding: 7px 12px;
border-top: 1px solid var(--l2-border);
background: var(--l2-background);
border-top: 1px dashed var(--l2-border);
background: var(--l1-background);
border-radius: 0 0 6px 6px;
}
.hintList {
display: flex;
flex-direction: column;
gap: var(--spacing-4);
padding: var(--spacing-2) var(--spacing-8);
margin: 0;
}
.hint {
display: flex;
align-items: center;
gap: 5px;
font-size: 11px;
color: var(--l2-foreground);
position: relative;
&[data-active='false']::before {
content: '';
position: absolute;
top: 50%;
left: -12px;
transform: translateY(-50%);
width: 4px;
height: 4px;
border-radius: 50%;
background: var(--l2-foreground);
opacity: 0.5;
}
}

View File

@@ -4,6 +4,10 @@ import { DEFAULT_PIN_TOOLTIP_KEY } from 'lib/uPlotV2/plugins/TooltipPlugin/types
import { X } from 'lucide-react';
import Styles from './TooltipFooter.module.scss';
import { MousePointerClick } from '@signozhq/icons';
import logEvent from 'api/common/logEvent';
import { Events } from 'constants/events';
import { getAbsoluteUrl } from 'utils/basePath';
interface TooltipFooterProps {
pinKey?: string;
@@ -16,27 +20,41 @@ export default function TooltipFooter({
isPinned,
dismiss,
}: TooltipFooterProps): JSX.Element {
const handleUnpinClick = (): void => {
logEvent(Events.TOOLTIP_UNPINNED, {
path: getAbsoluteUrl(window.location.pathname),
});
dismiss();
};
return (
<div
className={Styles.footer}
role="status"
data-testid="uplot-tooltip-footer"
>
<div className={Styles.hint}>
<div>
{isPinned ? (
<>
<div className={Styles.hint}>
<span>Press</span>
<Kbd active>{pinKey.toUpperCase()}</Kbd>
<span>or</span>
<Kbd active>Esc</Kbd>
<span>to unpin</span>
</>
</div>
) : (
<>
<span>Press</span>
<Kbd>{pinKey.toUpperCase()}</Kbd>
<span>to pin the tooltip</span>
</>
<div className={Styles.hintList}>
<div className={Styles.hint} data-active="false">
<Kbd>
<MousePointerClick size={12} />
</Kbd>
<span>Click to drilldown</span>
</div>
<div className={Styles.hint} data-active="false">
<span>Press</span>
<Kbd>{pinKey.toUpperCase()}</Kbd>
<span>to pin the tooltip</span>
</div>
</div>
)}
</div>
@@ -45,7 +63,7 @@ export default function TooltipFooter({
variant="outlined"
color="secondary"
size="sm"
onClick={dismiss}
onClick={handleUnpinClick}
aria-label="Unpin tooltip"
data-testid="uplot-tooltip-unpin"
>

View File

@@ -3,7 +3,6 @@
align-items: center;
gap: var(--spacing-2);
padding: 6px;
cursor: pointer;
opacity: 0.7;
font-weight: 400;
@@ -12,11 +11,6 @@
font-weight: 700;
}
&:hover {
opacity: 1 !important;
background-color: var(--l2-border);
border-radius: 4px;
}
}
.uplotTooltipItemMarker {

View File

@@ -1,12 +1,15 @@
import { useMemo, useState } from 'react';
import { useCallback, useMemo, useRef, useState } from 'react';
import { Virtuoso } from 'react-virtuoso';
import cx from 'classnames';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { TooltipContentItem } from '../../../types';
import TooltipItem from '../TooltipItem/TooltipItem';
import logEvent from 'api/common/logEvent';
import { Events } from 'constants/events';
import Styles from './TooltipList.module.scss';
import { getAbsoluteUrl } from 'utils/basePath';
// Fallback per-item height before Virtuoso reports the real total.
const TOOLTIP_ITEM_HEIGHT = 38;
@@ -20,6 +23,7 @@ export default function TooltipList({
content,
}: TooltipListProps): JSX.Element {
const isDarkMode = useIsDarkMode();
const isScrollEventTriggered = useRef(false);
const [totalListHeight, setTotalListHeight] = useState(0);
// Use the measured height from Virtuoso when available; fall back to a
@@ -33,11 +37,22 @@ export default function TooltipList({
[totalListHeight, content.length],
);
const handleScroll = useCallback(() => {
if (!isScrollEventTriggered.current) {
// TODO: remove event in July 2026
logEvent(Events.TOOLTIP_CONTENT_SCROLLED, {
path: getAbsoluteUrl(window.location.pathname),
});
isScrollEventTriggered.current = true;
}
}, []);
return (
<Virtuoso
className={cx(Styles.list, !isDarkMode && Styles.listLightMode)}
data-testid="uplot-tooltip-list"
data={content}
onScroll={handleScroll}
style={{ height }}
totalListHeightChanged={setTotalListHeight}
itemContent={(_, item): JSX.Element => (

View File

@@ -2,6 +2,7 @@ import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import cx from 'classnames';
import uPlot from 'uplot';
import logEvent from 'api/common/logEvent';
import { syncCursorRegistry } from './syncCursorRegistry';
import {
@@ -28,8 +29,10 @@ import {
createInitialViewState,
createLayoutObserver,
} from './utils';
import { Events } from 'constants/events';
import Styles from './TooltipPlugin.module.scss';
import { getAbsoluteUrl } from 'utils/basePath';
// Delay before hiding an unpinned tooltip when the cursor briefly leaves
// the plot this avoids flicker when moving between nearby points.
@@ -156,7 +159,7 @@ export default function TooltipPlugin({
function updateCursorLock(): void {
const plot = getPlot(controller);
if (plot) {
// @ts-ignore uPlot cursor lock is not working as expected
// @ts-expect-error uPlot cursor lock is not working as expected
plot.cursor._lock = controller.pinned;
}
}
@@ -296,6 +299,9 @@ export default function TooltipPlugin({
// Escape: release-only (never toggles on).
if (event.key === 'Escape') {
if (controller.pinned) {
logEvent(Events.TOOLTIP_UNPINNED, {
path: getAbsoluteUrl(window.location.pathname),
});
dismissTooltip();
}
return;
@@ -307,6 +313,9 @@ export default function TooltipPlugin({
// Toggle off: P pressed while already pinned.
if (controller.pinned) {
logEvent(Events.TOOLTIP_UNPINNED, {
path: getAbsoluteUrl(window.location.pathname),
});
dismissTooltip();
return;
}
@@ -328,16 +337,19 @@ export default function TooltipPlugin({
}
const plotRect = plot.over.getBoundingClientRect();
const syntheticEvent = ({
const syntheticEvent = {
clientX: plotRect.left + cursorLeft,
clientY: plotRect.top + cursorTop,
target: plot.over,
offsetX: cursorLeft,
offsetY: cursorTop,
} as unknown) as MouseEvent;
} as unknown as MouseEvent;
controller.clickData = buildClickData(syntheticEvent, plot);
controller.pinned = true;
logEvent(Events.TOOLTIP_PINNED, {
path: getAbsoluteUrl(window.location.pathname),
});
scheduleRender(true);
};