mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-18 16:07:10 +00:00
feat: highlight the searched spans and dim other spans in trace details v2 (#9032)
This commit is contained in:
parent
ba8a49929a
commit
d96073f478
@ -54,20 +54,32 @@ function Filters({
|
|||||||
startTime,
|
startTime,
|
||||||
endTime,
|
endTime,
|
||||||
traceID,
|
traceID,
|
||||||
|
onFilteredSpansChange = (): void => {},
|
||||||
}: {
|
}: {
|
||||||
startTime: number;
|
startTime: number;
|
||||||
endTime: number;
|
endTime: number;
|
||||||
traceID: string;
|
traceID: string;
|
||||||
|
onFilteredSpansChange?: (spanIds: string[], isFilterActive: boolean) => void;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const [filters, setFilters] = useState<TagFilter>(
|
const [filters, setFilters] = useState<TagFilter>(
|
||||||
BASE_FILTER_QUERY.filters || { items: [], op: 'AND' },
|
BASE_FILTER_QUERY.filters || { items: [], op: 'AND' },
|
||||||
);
|
);
|
||||||
const [noData, setNoData] = useState<boolean>(false);
|
const [noData, setNoData] = useState<boolean>(false);
|
||||||
const [filteredSpanIds, setFilteredSpanIds] = useState<string[]>([]);
|
const [filteredSpanIds, setFilteredSpanIds] = useState<string[]>([]);
|
||||||
const handleFilterChange = (value: TagFilter): void => {
|
|
||||||
setFilters(value);
|
|
||||||
};
|
|
||||||
const [currentSearchedIndex, setCurrentSearchedIndex] = useState<number>(0);
|
const [currentSearchedIndex, setCurrentSearchedIndex] = useState<number>(0);
|
||||||
|
|
||||||
|
const handleFilterChange = useCallback(
|
||||||
|
(value: TagFilter): void => {
|
||||||
|
if (value.items.length === 0) {
|
||||||
|
setFilteredSpanIds([]);
|
||||||
|
onFilteredSpansChange?.([], false);
|
||||||
|
setCurrentSearchedIndex(0);
|
||||||
|
setNoData(false);
|
||||||
|
}
|
||||||
|
setFilters(value);
|
||||||
|
},
|
||||||
|
[onFilteredSpansChange],
|
||||||
|
);
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
@ -116,6 +128,7 @@ function Filters({
|
|||||||
queryKey: [filters],
|
queryKey: [filters],
|
||||||
enabled: filters.items.length > 0,
|
enabled: filters.items.length > 0,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
|
const isFilterActive = filters.items.length > 0;
|
||||||
if (data?.payload.data.newResult.data.result[0].list) {
|
if (data?.payload.data.newResult.data.result[0].list) {
|
||||||
const uniqueSpans = uniqBy(
|
const uniqueSpans = uniqBy(
|
||||||
data?.payload.data.newResult.data.result[0].list,
|
data?.payload.data.newResult.data.result[0].list,
|
||||||
@ -124,11 +137,13 @@ function Filters({
|
|||||||
|
|
||||||
const spanIds = uniqueSpans.map((val) => val.data.spanID);
|
const spanIds = uniqueSpans.map((val) => val.data.spanID);
|
||||||
setFilteredSpanIds(spanIds);
|
setFilteredSpanIds(spanIds);
|
||||||
|
onFilteredSpansChange?.(spanIds, isFilterActive);
|
||||||
handlePrevNext(0, spanIds[0]);
|
handlePrevNext(0, spanIds[0]);
|
||||||
setNoData(false);
|
setNoData(false);
|
||||||
} else {
|
} else {
|
||||||
setNoData(true);
|
setNoData(true);
|
||||||
setFilteredSpanIds([]);
|
setFilteredSpanIds([]);
|
||||||
|
onFilteredSpansChange?.([], isFilterActive);
|
||||||
setCurrentSearchedIndex(0);
|
setCurrentSearchedIndex(0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -184,4 +199,8 @@ function Filters({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Filters.defaultProps = {
|
||||||
|
onFilteredSpansChange: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default Filters;
|
export default Filters;
|
||||||
|
|||||||
@ -315,7 +315,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.interested-span {
|
.interested-span,
|
||||||
|
.selected-non-matching-span {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: rgba(171, 189, 255, 0.06) !important;
|
background: rgba(171, 189, 255, 0.06) !important;
|
||||||
|
|
||||||
@ -323,6 +324,20 @@
|
|||||||
background: unset;
|
background: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dimmed-span {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.highlighted-span {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-non-matching-span {
|
||||||
|
.span-overview-content,
|
||||||
|
.span-line-text {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.div-td + .div-td {
|
.div-td + .div-td {
|
||||||
|
|||||||
@ -67,6 +67,8 @@ function SpanOverview({
|
|||||||
setSelectedSpan,
|
setSelectedSpan,
|
||||||
handleAddSpanToFunnel,
|
handleAddSpanToFunnel,
|
||||||
selectedSpan,
|
selectedSpan,
|
||||||
|
filteredSpanIds,
|
||||||
|
isFilterActive,
|
||||||
traceMetadata,
|
traceMetadata,
|
||||||
}: {
|
}: {
|
||||||
span: Span;
|
span: Span;
|
||||||
@ -75,6 +77,8 @@ function SpanOverview({
|
|||||||
selectedSpan: Span | undefined;
|
selectedSpan: Span | undefined;
|
||||||
setSelectedSpan: Dispatch<SetStateAction<Span | undefined>>;
|
setSelectedSpan: Dispatch<SetStateAction<Span | undefined>>;
|
||||||
handleAddSpanToFunnel: (span: Span) => void;
|
handleAddSpanToFunnel: (span: Span) => void;
|
||||||
|
filteredSpanIds: string[];
|
||||||
|
isFilterActive: boolean;
|
||||||
traceMetadata: ITraceMetadata;
|
traceMetadata: ITraceMetadata;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const isRootSpan = span.level === 0;
|
const isRootSpan = span.level === 0;
|
||||||
@ -85,13 +89,23 @@ function SpanOverview({
|
|||||||
color = `var(--bg-cherry-500)`;
|
color = `var(--bg-cherry-500)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Smart highlighting logic
|
||||||
|
const isMatching =
|
||||||
|
isFilterActive && (filteredSpanIds || []).includes(span.spanId);
|
||||||
|
const isSelected = selectedSpan?.spanId === span.spanId;
|
||||||
|
const isDimmed = isFilterActive && !isMatching && !isSelected;
|
||||||
|
const isHighlighted = isFilterActive && isMatching && !isSelected;
|
||||||
|
const isSelectedNonMatching = isSelected && isFilterActive && !isMatching;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SpanHoverCard span={span} traceMetadata={traceMetadata}>
|
<SpanHoverCard span={span} traceMetadata={traceMetadata}>
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx('span-overview', {
|
||||||
'span-overview',
|
'interested-span': isSelected && (!isFilterActive || isMatching),
|
||||||
selectedSpan?.spanId === span.spanId ? 'interested-span' : '',
|
'highlighted-span': isHighlighted,
|
||||||
)}
|
'selected-non-matching-span': isSelectedNonMatching,
|
||||||
|
'dimmed-span': isDimmed,
|
||||||
|
})}
|
||||||
style={{
|
style={{
|
||||||
paddingLeft: `${
|
paddingLeft: `${
|
||||||
isRootSpan
|
isRootSpan
|
||||||
@ -199,11 +213,15 @@ export function SpanDuration({
|
|||||||
traceMetadata,
|
traceMetadata,
|
||||||
setSelectedSpan,
|
setSelectedSpan,
|
||||||
selectedSpan,
|
selectedSpan,
|
||||||
|
filteredSpanIds,
|
||||||
|
isFilterActive,
|
||||||
}: {
|
}: {
|
||||||
span: Span;
|
span: Span;
|
||||||
traceMetadata: ITraceMetadata;
|
traceMetadata: ITraceMetadata;
|
||||||
selectedSpan: Span | undefined;
|
selectedSpan: Span | undefined;
|
||||||
setSelectedSpan: Dispatch<SetStateAction<Span | undefined>>;
|
setSelectedSpan: Dispatch<SetStateAction<Span | undefined>>;
|
||||||
|
filteredSpanIds: string[];
|
||||||
|
isFilterActive: boolean;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { time, timeUnitName } = convertTimeToRelevantUnit(
|
const { time, timeUnitName } = convertTimeToRelevantUnit(
|
||||||
span.durationNano / 1e6,
|
span.durationNano / 1e6,
|
||||||
@ -224,6 +242,13 @@ export function SpanDuration({
|
|||||||
|
|
||||||
const [hasActionButtons, setHasActionButtons] = useState(false);
|
const [hasActionButtons, setHasActionButtons] = useState(false);
|
||||||
|
|
||||||
|
const isMatching =
|
||||||
|
isFilterActive && (filteredSpanIds || []).includes(span.spanId);
|
||||||
|
const isSelected = selectedSpan?.spanId === span.spanId;
|
||||||
|
const isDimmed = isFilterActive && !isMatching && !isSelected;
|
||||||
|
const isHighlighted = isFilterActive && isMatching && !isSelected;
|
||||||
|
const isSelectedNonMatching = isSelected && isFilterActive && !isMatching;
|
||||||
|
|
||||||
const handleMouseEnter = (): void => {
|
const handleMouseEnter = (): void => {
|
||||||
setHasActionButtons(true);
|
setHasActionButtons(true);
|
||||||
};
|
};
|
||||||
@ -256,10 +281,12 @@ export function SpanDuration({
|
|||||||
return (
|
return (
|
||||||
<SpanHoverCard span={span} traceMetadata={traceMetadata}>
|
<SpanHoverCard span={span} traceMetadata={traceMetadata}>
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx('span-duration', {
|
||||||
'span-duration',
|
'interested-span': isSelected && (!isFilterActive || isMatching),
|
||||||
selectedSpan?.spanId === span.spanId ? 'interested-span' : '',
|
'highlighted-span': isHighlighted,
|
||||||
)}
|
'selected-non-matching-span': isSelectedNonMatching,
|
||||||
|
'dimmed-span': isDimmed,
|
||||||
|
})}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
@ -325,14 +352,17 @@ function getWaterfallColumns({
|
|||||||
selectedSpan,
|
selectedSpan,
|
||||||
setSelectedSpan,
|
setSelectedSpan,
|
||||||
handleAddSpanToFunnel,
|
handleAddSpanToFunnel,
|
||||||
|
filteredSpanIds,
|
||||||
|
isFilterActive,
|
||||||
}: {
|
}: {
|
||||||
handleCollapseUncollapse: (id: string, collapse: boolean) => void;
|
handleCollapseUncollapse: (id: string, collapse: boolean) => void;
|
||||||
uncollapsedNodes: string[];
|
uncollapsedNodes: string[];
|
||||||
traceMetadata: ITraceMetadata;
|
traceMetadata: ITraceMetadata;
|
||||||
selectedSpan: Span | undefined;
|
selectedSpan: Span | undefined;
|
||||||
setSelectedSpan: Dispatch<SetStateAction<Span | undefined>>;
|
setSelectedSpan: Dispatch<SetStateAction<Span | undefined>>;
|
||||||
|
|
||||||
handleAddSpanToFunnel: (span: Span) => void;
|
handleAddSpanToFunnel: (span: Span) => void;
|
||||||
|
filteredSpanIds: string[];
|
||||||
|
isFilterActive: boolean;
|
||||||
}): ColumnDef<Span, any>[] {
|
}): ColumnDef<Span, any>[] {
|
||||||
const waterfallColumns: ColumnDef<Span, any>[] = [
|
const waterfallColumns: ColumnDef<Span, any>[] = [
|
||||||
columnDefHelper.display({
|
columnDefHelper.display({
|
||||||
@ -347,6 +377,8 @@ function getWaterfallColumns({
|
|||||||
setSelectedSpan={setSelectedSpan}
|
setSelectedSpan={setSelectedSpan}
|
||||||
handleAddSpanToFunnel={handleAddSpanToFunnel}
|
handleAddSpanToFunnel={handleAddSpanToFunnel}
|
||||||
traceMetadata={traceMetadata}
|
traceMetadata={traceMetadata}
|
||||||
|
filteredSpanIds={filteredSpanIds}
|
||||||
|
isFilterActive={isFilterActive}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
size: 450,
|
size: 450,
|
||||||
@ -371,6 +403,8 @@ function getWaterfallColumns({
|
|||||||
traceMetadata={traceMetadata}
|
traceMetadata={traceMetadata}
|
||||||
selectedSpan={selectedSpan}
|
selectedSpan={selectedSpan}
|
||||||
setSelectedSpan={setSelectedSpan}
|
setSelectedSpan={setSelectedSpan}
|
||||||
|
filteredSpanIds={filteredSpanIds}
|
||||||
|
isFilterActive={isFilterActive}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
@ -390,8 +424,19 @@ function Success(props: ISuccessProps): JSX.Element {
|
|||||||
setSelectedSpan,
|
setSelectedSpan,
|
||||||
selectedSpan,
|
selectedSpan,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const [filteredSpanIds, setFilteredSpanIds] = useState<string[]>([]);
|
||||||
|
const [isFilterActive, setIsFilterActive] = useState<boolean>(false);
|
||||||
const virtualizerRef = useRef<Virtualizer<HTMLDivElement, Element>>();
|
const virtualizerRef = useRef<Virtualizer<HTMLDivElement, Element>>();
|
||||||
|
|
||||||
|
const handleFilteredSpansChange = useCallback(
|
||||||
|
(spanIds: string[], isActive: boolean) => {
|
||||||
|
setFilteredSpanIds(spanIds);
|
||||||
|
setIsFilterActive(isActive);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const handleCollapseUncollapse = useCallback(
|
const handleCollapseUncollapse = useCallback(
|
||||||
(spanId: string, collapse: boolean) => {
|
(spanId: string, collapse: boolean) => {
|
||||||
setInterestedSpanId({ spanId, isUncollapsed: !collapse });
|
setInterestedSpanId({ spanId, isUncollapsed: !collapse });
|
||||||
@ -443,6 +488,8 @@ function Success(props: ISuccessProps): JSX.Element {
|
|||||||
selectedSpan,
|
selectedSpan,
|
||||||
setSelectedSpan,
|
setSelectedSpan,
|
||||||
handleAddSpanToFunnel,
|
handleAddSpanToFunnel,
|
||||||
|
filteredSpanIds,
|
||||||
|
isFilterActive,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
handleCollapseUncollapse,
|
handleCollapseUncollapse,
|
||||||
@ -451,6 +498,8 @@ function Success(props: ISuccessProps): JSX.Element {
|
|||||||
selectedSpan,
|
selectedSpan,
|
||||||
setSelectedSpan,
|
setSelectedSpan,
|
||||||
handleAddSpanToFunnel,
|
handleAddSpanToFunnel,
|
||||||
|
filteredSpanIds,
|
||||||
|
isFilterActive,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -508,6 +557,7 @@ function Success(props: ISuccessProps): JSX.Element {
|
|||||||
startTime={traceMetadata.startTime / 1e3}
|
startTime={traceMetadata.startTime / 1e3}
|
||||||
endTime={traceMetadata.endTime / 1e3}
|
endTime={traceMetadata.endTime / 1e3}
|
||||||
traceID={traceMetadata.traceId}
|
traceID={traceMetadata.traceId}
|
||||||
|
onFilteredSpansChange={handleFilteredSpansChange}
|
||||||
/>
|
/>
|
||||||
<TableV3
|
<TableV3
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|||||||
@ -6,6 +6,14 @@ import { Span } from 'types/api/trace/getTraceV2';
|
|||||||
|
|
||||||
import { SpanDuration } from '../Success';
|
import { SpanDuration } from '../Success';
|
||||||
|
|
||||||
|
// Constants to avoid string duplication
|
||||||
|
const SPAN_DURATION_TEXT = '1.16 ms';
|
||||||
|
const SPAN_DURATION_CLASS = '.span-duration';
|
||||||
|
const INTERESTED_SPAN_CLASS = 'interested-span';
|
||||||
|
const HIGHLIGHTED_SPAN_CLASS = 'highlighted-span';
|
||||||
|
const DIMMED_SPAN_CLASS = 'dimmed-span';
|
||||||
|
const SELECTED_NON_MATCHING_SPAN_CLASS = 'selected-non-matching-span';
|
||||||
|
|
||||||
// Mock the hooks
|
// Mock the hooks
|
||||||
jest.mock('hooks/useSafeNavigate');
|
jest.mock('hooks/useSafeNavigate');
|
||||||
jest.mock('hooks/useUrlQuery');
|
jest.mock('hooks/useUrlQuery');
|
||||||
@ -87,11 +95,13 @@ describe('SpanDuration', () => {
|
|||||||
traceMetadata={mockTraceMetadata}
|
traceMetadata={mockTraceMetadata}
|
||||||
selectedSpan={undefined}
|
selectedSpan={undefined}
|
||||||
setSelectedSpan={mockSetSelectedSpan}
|
setSelectedSpan={mockSetSelectedSpan}
|
||||||
|
filteredSpanIds={[]}
|
||||||
|
isFilterActive={false}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Find and click the span duration element
|
// Find and click the span duration element
|
||||||
const spanElement = screen.getByText('1.16 ms');
|
const spanElement = screen.getByText(SPAN_DURATION_TEXT);
|
||||||
fireEvent.click(spanElement);
|
fireEvent.click(spanElement);
|
||||||
|
|
||||||
// Verify setSelectedSpan was called with the correct span
|
// Verify setSelectedSpan was called with the correct span
|
||||||
@ -113,10 +123,12 @@ describe('SpanDuration', () => {
|
|||||||
traceMetadata={mockTraceMetadata}
|
traceMetadata={mockTraceMetadata}
|
||||||
selectedSpan={undefined}
|
selectedSpan={undefined}
|
||||||
setSelectedSpan={mockSetSelectedSpan}
|
setSelectedSpan={mockSetSelectedSpan}
|
||||||
|
filteredSpanIds={[]}
|
||||||
|
isFilterActive={false}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const spanElement = screen.getByText('1.16 ms');
|
const spanElement = screen.getByText(SPAN_DURATION_TEXT);
|
||||||
|
|
||||||
// Initially, action buttons should not be visible
|
// Initially, action buttons should not be visible
|
||||||
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
||||||
@ -139,10 +151,135 @@ describe('SpanDuration', () => {
|
|||||||
traceMetadata={mockTraceMetadata}
|
traceMetadata={mockTraceMetadata}
|
||||||
selectedSpan={mockSpan}
|
selectedSpan={mockSpan}
|
||||||
setSelectedSpan={mockSetSelectedSpan}
|
setSelectedSpan={mockSetSelectedSpan}
|
||||||
|
filteredSpanIds={[]}
|
||||||
|
isFilterActive={false}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const spanElement = screen.getByText('1.16 ms').closest('.span-duration');
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||||
expect(spanElement).toHaveClass('interested-span');
|
const spanElement = screen
|
||||||
|
.getByText(SPAN_DURATION_TEXT)
|
||||||
|
.closest(SPAN_DURATION_CLASS);
|
||||||
|
expect(spanElement).toHaveClass(INTERESTED_SPAN_CLASS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies highlighted-span class when span matches filter', () => {
|
||||||
|
render(
|
||||||
|
<SpanDuration
|
||||||
|
span={mockSpan}
|
||||||
|
traceMetadata={mockTraceMetadata}
|
||||||
|
selectedSpan={undefined}
|
||||||
|
setSelectedSpan={mockSetSelectedSpan}
|
||||||
|
filteredSpanIds={[mockSpan.spanId]}
|
||||||
|
isFilterActive
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const spanElement = screen
|
||||||
|
.getByText(SPAN_DURATION_TEXT)
|
||||||
|
.closest(SPAN_DURATION_CLASS);
|
||||||
|
expect(spanElement).toHaveClass(HIGHLIGHTED_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(INTERESTED_SPAN_CLASS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies dimmed-span class when span does not match filter', () => {
|
||||||
|
render(
|
||||||
|
<SpanDuration
|
||||||
|
span={mockSpan}
|
||||||
|
traceMetadata={mockTraceMetadata}
|
||||||
|
selectedSpan={undefined}
|
||||||
|
setSelectedSpan={mockSetSelectedSpan}
|
||||||
|
filteredSpanIds={['other-span-id']}
|
||||||
|
isFilterActive
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const spanElement = screen
|
||||||
|
.getByText(SPAN_DURATION_TEXT)
|
||||||
|
.closest(SPAN_DURATION_CLASS);
|
||||||
|
expect(spanElement).toHaveClass(DIMMED_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(HIGHLIGHTED_SPAN_CLASS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prioritizes interested-span over highlighted-span when span is selected and matches filter', () => {
|
||||||
|
render(
|
||||||
|
<SpanDuration
|
||||||
|
span={mockSpan}
|
||||||
|
traceMetadata={mockTraceMetadata}
|
||||||
|
selectedSpan={mockSpan}
|
||||||
|
setSelectedSpan={mockSetSelectedSpan}
|
||||||
|
filteredSpanIds={[mockSpan.spanId]}
|
||||||
|
isFilterActive
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const spanElement = screen
|
||||||
|
.getByText(SPAN_DURATION_TEXT)
|
||||||
|
.closest(SPAN_DURATION_CLASS);
|
||||||
|
expect(spanElement).toHaveClass(INTERESTED_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(HIGHLIGHTED_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(DIMMED_SPAN_CLASS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies selected-non-matching-span class when span is selected but does not match filter', () => {
|
||||||
|
render(
|
||||||
|
<SpanDuration
|
||||||
|
span={mockSpan}
|
||||||
|
traceMetadata={mockTraceMetadata}
|
||||||
|
selectedSpan={mockSpan}
|
||||||
|
setSelectedSpan={mockSetSelectedSpan}
|
||||||
|
filteredSpanIds={['different-span-id']}
|
||||||
|
isFilterActive
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const spanElement = screen
|
||||||
|
.getByText(SPAN_DURATION_TEXT)
|
||||||
|
.closest(SPAN_DURATION_CLASS);
|
||||||
|
expect(spanElement).toHaveClass(SELECTED_NON_MATCHING_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(INTERESTED_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(HIGHLIGHTED_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(DIMMED_SPAN_CLASS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies interested-span class when span is selected and no filter is active', () => {
|
||||||
|
render(
|
||||||
|
<SpanDuration
|
||||||
|
span={mockSpan}
|
||||||
|
traceMetadata={mockTraceMetadata}
|
||||||
|
selectedSpan={mockSpan}
|
||||||
|
setSelectedSpan={mockSetSelectedSpan}
|
||||||
|
filteredSpanIds={[]}
|
||||||
|
isFilterActive={false}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const spanElement = screen
|
||||||
|
.getByText(SPAN_DURATION_TEXT)
|
||||||
|
.closest(SPAN_DURATION_CLASS);
|
||||||
|
expect(spanElement).toHaveClass(INTERESTED_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(SELECTED_NON_MATCHING_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(HIGHLIGHTED_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(DIMMED_SPAN_CLASS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dims span when filter is active but no matches found', () => {
|
||||||
|
render(
|
||||||
|
<SpanDuration
|
||||||
|
span={mockSpan}
|
||||||
|
traceMetadata={mockTraceMetadata}
|
||||||
|
selectedSpan={undefined}
|
||||||
|
setSelectedSpan={mockSetSelectedSpan}
|
||||||
|
filteredSpanIds={[]} // Empty array but filter is active
|
||||||
|
isFilterActive // This is the key difference
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const spanElement = screen
|
||||||
|
.getByText(SPAN_DURATION_TEXT)
|
||||||
|
.closest(SPAN_DURATION_CLASS);
|
||||||
|
expect(spanElement).toHaveClass(DIMMED_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(HIGHLIGHTED_SPAN_CLASS);
|
||||||
|
expect(spanElement).not.toHaveClass(INTERESTED_SPAN_CLASS);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user