feat: added copy span link support alongside span click expand in waterfall graph

This commit is contained in:
sawhil 2025-04-07 16:20:14 +05:30 committed by Sahil Khan
parent 9338efcefc
commit 0944af3d31
4 changed files with 140 additions and 0 deletions

View File

@ -0,0 +1,42 @@
.span-line-action-buttons {
display: flex;
position: absolute;
transform: translate(-50%, -50%);
top: 50%;
right: 0;
cursor: pointer;
border-radius: 2px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-400);
.ant-btn-default {
border: none;
box-shadow: none;
padding: 9px;
justify-content: center;
align-items: center;
display: flex;
&.active-tab {
background-color: var(--bg-slate-400);
}
}
.copy-span-btn {
border-color: var(--bg-slate-400) !important;
}
}
.lightMode {
.span-line-action-buttons {
border: 1px solid var(--bg-vanilla-400);
background: var(--bg-vanilla-400);
.ant-btn-default {
}
.copy-span-btn {
border-color: var(--bg-vanilla-400) !important;
}
}
}

View File

@ -0,0 +1,34 @@
import './SpanLineActionButtons.styles.scss';
import { LinkOutlined } from '@ant-design/icons';
import { Button, Tooltip } from 'antd';
import { useCopySpanLink } from 'hooks/trace/useCopySpanLink';
import { Span } from 'types/api/trace/getTraceV2';
export interface SpanLineActionButtonsProps {
span: Span;
customClassName?: string;
}
export default function SpanLineActionButtons({
span,
customClassName = '',
}: SpanLineActionButtonsProps): JSX.Element {
const { onSpanCopy } = useCopySpanLink(span);
return (
<div className={`span-line-action-buttons ${customClassName}`}>
<Tooltip title="Copy Span Link">
<Button
size="small"
icon={<LinkOutlined size={14} />}
onClick={onSpanCopy}
className="copy-span-btn"
/>
</Tooltip>
</div>
);
}
SpanLineActionButtons.defaultProps = {
customClassName: '',
};

View File

@ -9,6 +9,7 @@ import cx from 'classnames';
import { TableV3 } from 'components/TableV3/TableV3';
import { themeColors } from 'constants/theme';
import { convertTimeToRelevantUnit } from 'container/TraceDetail/utils';
import SpanLineActionButtons from 'container/TraceWaterfall/SpanLineActionButtons';
import { IInterestedSpan } from 'container/TraceWaterfall/TraceWaterfall';
import { generateColor } from 'lib/uPlotLib/utils/generateColor';
import {
@ -25,7 +26,9 @@ import {
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { Span } from 'types/api/trace/getTraceV2';
import { toFixed } from 'utils/toFixed';
@ -166,20 +169,38 @@ function SpanDuration({
const leftOffset = ((span.timestamp - traceMetadata.startTime) * 1e2) / spread;
const width = (span.durationNano * 1e2) / (spread * 1e6);
const { search } = useLocation();
const history = useHistory();
const searchParams = new URLSearchParams(search);
let color = generateColor(span.serviceName, themeColors.traceDetailColors);
if (span.hasError) {
color = `var(--bg-cherry-500)`;
}
const [hasActionButtons, setHasActionButtons] = useState(false);
const handleMouseEnter = (): void => {
setHasActionButtons(true);
};
const handleMouseLeave = (): void => {
setHasActionButtons(false);
};
return (
<div
className={cx(
'span-duration',
selectedSpan?.spanId === span.spanId ? 'interested-span' : '',
)}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onClick={(): void => {
setSelectedSpan(span);
searchParams.set('spanId', span.spanId);
history.replace({ search: searchParams.toString() });
}}
>
<div
@ -190,6 +211,7 @@ function SpanDuration({
backgroundColor: color,
}}
/>
{hasActionButtons && <SpanLineActionButtons span={span} />}
<Tooltip title={`${toFixed(time, 2)} ${timeUnitName}`}>
<Typography.Text
className="span-line-text"

View File

@ -0,0 +1,42 @@
import { useNotifications } from 'hooks/useNotifications';
import useUrlQuery from 'hooks/useUrlQuery';
import { MouseEventHandler, useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import { useCopyToClipboard } from 'react-use';
import { Span } from 'types/api/trace/getTraceV2';
export const useCopySpanLink = (
span?: Span,
): { onSpanCopy: MouseEventHandler<HTMLElement> } => {
const urlQuery = useUrlQuery();
const { pathname } = useLocation();
const [, setCopy] = useCopyToClipboard();
const { notifications } = useNotifications();
const onSpanCopy: MouseEventHandler<HTMLElement> = useCallback(
(event) => {
if (!span) return;
event.preventDefault();
event.stopPropagation();
urlQuery.delete('spanId');
if (span.spanId) {
urlQuery.set('spanId', span?.spanId);
}
const link = `${window.location.origin}${pathname}?${urlQuery.toString()}`;
setCopy(link);
notifications.success({
message: 'Copied to clipboard',
});
},
[span, urlQuery, pathname, setCopy, notifications],
);
return {
onSpanCopy,
};
};