mirror of
https://github.com/SigNoz/signoz.git
synced 2026-08-05 20:50:45 +01:00
Compare commits
1 Commits
v0.136.1
...
feat/enabl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaff028e8a |
@@ -37,6 +37,7 @@ export default function ChartWrapper({
|
||||
groupByPerQuery,
|
||||
customTooltip,
|
||||
pinnedTooltipElement,
|
||||
tooltipPortalRoot,
|
||||
'data-testid': testId,
|
||||
}: ChartProps): JSX.Element {
|
||||
const plotInstanceRef = useRef<uPlot | null>(null);
|
||||
@@ -118,6 +119,7 @@ export default function ChartWrapper({
|
||||
syncMetadata={syncMetadata}
|
||||
render={renderTooltipCallback}
|
||||
pinnedTooltipElement={pinnedTooltipElement}
|
||||
portalRoot={tooltipPortalRoot}
|
||||
/>
|
||||
)}
|
||||
</UPlotChart>
|
||||
|
||||
@@ -30,6 +30,7 @@ interface BaseChartProps {
|
||||
pinnedTooltipElement?: (clickData: ChartClickData) => React.ReactNode;
|
||||
renderTooltipFooter?: (args: IRenderTooltipFooterArgs) => React.ReactNode;
|
||||
customTooltip?: (props: TooltipRenderArgs) => React.ReactNode;
|
||||
tooltipPortalRoot?: HTMLElement | null;
|
||||
'data-testid'?: string;
|
||||
}
|
||||
interface UPlotBasedChartProps {
|
||||
|
||||
@@ -40,6 +40,9 @@ export type {
|
||||
|
||||
// TODO(H4ad): Improve this on component level
|
||||
const DRAWER_TRANSITION = { duration: 0.3, ease: [0.25, 0.1, 0.25, 1] };
|
||||
// Be careful when changing these props, this must be animated with transform but later
|
||||
// replaced with none, otherwise, the tooltip of the chart will be not positioned correctly
|
||||
// due to how the tooltip positioning calculation works
|
||||
const DRAWER_MOTION_PROPS = {
|
||||
onOpenAutoFocus: (e: Event): void => e.preventDefault(),
|
||||
initial: { opacity: 0, transform: 'translateX(100%)' },
|
||||
@@ -47,6 +50,7 @@ const DRAWER_MOTION_PROPS = {
|
||||
opacity: 1,
|
||||
transform: 'translateX(0%)',
|
||||
transition: DRAWER_TRANSITION,
|
||||
transitionEnd: { transform: 'none' },
|
||||
},
|
||||
exit: {
|
||||
opacity: 0,
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--spacing-5);
|
||||
padding: var(--spacing-4) var(--spacing-6);
|
||||
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 {
|
||||
--typography-text-display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-3);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { MousePointerClick, X } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Kbd } from '@signozhq/ui/kbd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { Events } from 'constants/events';
|
||||
import { DEFAULT_PIN_TOOLTIP_KEY } from 'lib/uPlotV2/plugins/TooltipPlugin/types';
|
||||
|
||||
import styles from './ChartTooltipFooter.module.scss';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
|
||||
interface ChartTooltipFooterProps {
|
||||
id: string;
|
||||
pinKey?: string;
|
||||
isPinned: boolean;
|
||||
canSelectTimeRange?: boolean;
|
||||
dismiss: () => void;
|
||||
}
|
||||
|
||||
export default function ChartTooltipFooter({
|
||||
id,
|
||||
pinKey = DEFAULT_PIN_TOOLTIP_KEY,
|
||||
isPinned,
|
||||
canSelectTimeRange = true,
|
||||
dismiss,
|
||||
}: ChartTooltipFooterProps): JSX.Element {
|
||||
const handleUnpinClick = (): void => {
|
||||
void logEvent(Events.TOOLTIP_UNPINNED, {
|
||||
id,
|
||||
});
|
||||
dismiss();
|
||||
};
|
||||
|
||||
return (
|
||||
<output className={styles.footer} data-testid="entity-chart-tooltip-footer">
|
||||
<div>
|
||||
{isPinned ? (
|
||||
<Typography.Text className={styles.hint} size="small">
|
||||
<span>Press</span>
|
||||
<Kbd active>{pinKey.toUpperCase()}</Kbd>
|
||||
<span>or</span>
|
||||
<Kbd active>Esc</Kbd>
|
||||
<span>to unpin</span>
|
||||
</Typography.Text>
|
||||
) : (
|
||||
<div className={styles.hintList}>
|
||||
{canSelectTimeRange && (
|
||||
<Typography.Text
|
||||
className={styles.hint}
|
||||
size="small"
|
||||
data-active="false"
|
||||
>
|
||||
<Kbd>
|
||||
<MousePointerClick size={12} />
|
||||
</Kbd>
|
||||
<span>Click and drag to zoom into a time range</span>
|
||||
</Typography.Text>
|
||||
)}
|
||||
<Typography.Text className={styles.hint} size="small" data-active="false">
|
||||
<span>Press</span>
|
||||
<Kbd>{pinKey.toUpperCase()}</Kbd>
|
||||
<span>to pin the tooltip</span>
|
||||
</Typography.Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isPinned && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
onClick={handleUnpinClick}
|
||||
aria-label="Unpin tooltip"
|
||||
data-testid="entity-chart-tooltip-unpin"
|
||||
>
|
||||
<X size={10} />
|
||||
<span>Unpin</span>
|
||||
</Button>
|
||||
)}
|
||||
</output>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { Skeleton } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import { InfraMonitoringEvents } from 'constants/events';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import TimeSeries from 'container/DashboardContainer/visualization/charts/TimeSeries/TimeSeries';
|
||||
import { LegendPosition } from 'lib/uPlotV2/components/types';
|
||||
import {
|
||||
IRenderTooltipFooterArgs,
|
||||
LegendPosition,
|
||||
} from 'lib/uPlotV2/components/types';
|
||||
import {
|
||||
InfraMonitoringEntity,
|
||||
VIEW_TYPES,
|
||||
@@ -33,6 +36,7 @@ import { isKeyNotFoundError } from '../utils';
|
||||
import styles from './EntityMetrics.module.scss';
|
||||
import { MetricsTable } from './MetricsTable';
|
||||
import { logInfraExplorerNavigatedEvent } from 'container/InfraMonitoringK8sV2/Base/events';
|
||||
import ChartTooltipFooter from './ChartTooltipFooter';
|
||||
|
||||
interface EntityMetricsProps<T> {
|
||||
entity: T;
|
||||
@@ -78,6 +82,10 @@ function EntityMetrics<T>({
|
||||
category,
|
||||
});
|
||||
|
||||
const [tooltipPortalEl, setTooltipPortalEl] = useState<HTMLDivElement | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const { timezone } = useTimezone();
|
||||
const graphRef = useRef<HTMLDivElement>(null);
|
||||
@@ -171,6 +179,18 @@ function EntityMetrics<T>({
|
||||
height={dimensions.height}
|
||||
timezone={timezone}
|
||||
yAxisUnit={entityWidgetInfo[idx].yAxisUnit}
|
||||
canPinTooltip
|
||||
tooltipPortalRoot={tooltipPortalEl ?? undefined}
|
||||
renderTooltipFooter={({
|
||||
isPinned,
|
||||
dismiss,
|
||||
}: IRenderTooltipFooterArgs) => (
|
||||
<ChartTooltipFooter
|
||||
id={configs[idx]?.getId() || idx.toString()}
|
||||
isPinned={isPinned}
|
||||
dismiss={dismiss}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
@@ -180,6 +200,8 @@ function EntityMetrics<T>({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={setTooltipPortalEl} style={{ display: 'contents' }} />
|
||||
|
||||
<div className={styles.metricsHeader}>
|
||||
<EntityDateTimeSelector
|
||||
eventEntity={eventEntity}
|
||||
|
||||
@@ -51,6 +51,7 @@ export default function TooltipPlugin({
|
||||
canPinTooltip = false,
|
||||
pinKey = DEFAULT_PIN_TOOLTIP_KEY,
|
||||
onClick,
|
||||
portalRoot: portalRootProp,
|
||||
}: TooltipPluginProps): JSX.Element | null {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const rafId = useRef<number | null>(null);
|
||||
@@ -59,7 +60,9 @@ export default function TooltipPlugin({
|
||||
const renderRef = useRef(render);
|
||||
renderRef.current = render;
|
||||
const [portalRoot, setPortalRoot] = useState<HTMLElement>(
|
||||
(document.fullscreenElement as HTMLElement) ?? document.body,
|
||||
portalRootProp ??
|
||||
(document.fullscreenElement as HTMLElement) ??
|
||||
document.body,
|
||||
);
|
||||
|
||||
// React-managed snapshot of what should be rendered. The controller
|
||||
@@ -464,8 +467,12 @@ export default function TooltipPlugin({
|
||||
}, [config]);
|
||||
|
||||
const resolvePortalRoot = useCallback((): void => {
|
||||
setPortalRoot((document.fullscreenElement as HTMLElement) ?? document.body);
|
||||
}, []);
|
||||
setPortalRoot(
|
||||
portalRootProp ??
|
||||
(document.fullscreenElement as HTMLElement) ??
|
||||
document.body,
|
||||
);
|
||||
}, [portalRootProp]);
|
||||
|
||||
useLayoutEffect((): (() => void) => {
|
||||
resolvePortalRoot();
|
||||
|
||||
@@ -67,6 +67,7 @@ export interface TooltipPluginProps {
|
||||
pinnedTooltipElement?: (clickData: ChartClickData) => ReactNode;
|
||||
maxWidth?: number;
|
||||
maxHeight?: number;
|
||||
portalRoot?: HTMLElement | null;
|
||||
}
|
||||
|
||||
export interface ChartClickData {
|
||||
|
||||
Reference in New Issue
Block a user