mirror of
https://github.com/SigNoz/signoz.git
synced 2026-04-24 12:50:28 +01:00
Compare commits
5 Commits
main
...
chore/tool
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
169c10a8fb | ||
|
|
a2ab816ebb | ||
|
|
51e143bb7c | ||
|
|
5dc7c83359 | ||
|
|
a2b9cec8e4 |
@@ -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 {
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 => (
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user