diff --git a/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.test.js b/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.test.js index 73dea7818bc..5c4c0e13f1c 100644 --- a/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.test.js +++ b/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.test.js @@ -65,7 +65,7 @@ describe('', () => { const spans = [ trace.spans[0], // this span is condidered to have collapsed children - { spanID: newSpanID, depth: 1 }, + { spanID: newSpanID, depth: 1, traceID: trace.traceID }, // these two "spans" are children and should be hidden { depth: 2 }, { depth: 3 }, @@ -227,20 +227,20 @@ describe('', () => { } it('works when nothing is expanded or collapsed', () => { - verify(0, `${trace.spans[0].spanID}--bar`); + verify(0, `${trace.spans[0].traceID}--${trace.spans[0].spanID}--bar`); }); it('works when rows are expanded', () => { expandRow(1); - verify(1, `${trace.spans[1].spanID}--bar`); - verify(2, `${trace.spans[1].spanID}--detail`); - verify(3, `${trace.spans[2].spanID}--bar`); + verify(1, `${trace.spans[1].traceID}--${trace.spans[1].spanID}--bar`); + verify(2, `${trace.spans[1].traceID}--${trace.spans[1].spanID}--detail`); + verify(3, `${trace.spans[2].traceID}--${trace.spans[2].spanID}--bar`); }); it('works when a parent span is collapsed', () => { const spans = addSpansAndCollapseTheirParent(); - verify(1, `${spans[1].spanID}--bar`); - verify(2, `${spans[4].spanID}--bar`); + verify(1, `${spans[1].traceID}--${spans[1].spanID}--bar`); + verify(2, `${spans[4].traceID}--${spans[4].spanID}--bar`); }); }); @@ -250,20 +250,20 @@ describe('', () => { } it('works when nothing is expanded or collapsed', () => { - verify(`${trace.spans[0].spanID}--bar`, 0); + verify(`${trace.traceID}--${trace.spans[0].spanID}--bar`, 0); }); it('works when rows are expanded', () => { expandRow(1); - verify(`${trace.spans[1].spanID}--bar`, 1); - verify(`${trace.spans[1].spanID}--detail`, 2); - verify(`${trace.spans[2].spanID}--bar`, 3); + verify(`${trace.spans[1].traceID}--${trace.spans[1].spanID}--bar`, 1); + verify(`${trace.spans[1].traceID}--${trace.spans[1].spanID}--detail`, 2); + verify(`${trace.spans[2].traceID}--${trace.spans[2].spanID}--bar`, 3); }); it('works when a parent span is collapsed', () => { const spans = addSpansAndCollapseTheirParent(); - verify(`${spans[1].spanID}--bar`, 1); - verify(`${spans[4].spanID}--bar`, 2); + verify(`${spans[1].traceID}--${spans[1].spanID}--bar`, 1); + verify(`${spans[4].traceID}--${spans[4].spanID}--bar`, 2); }); }); diff --git a/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.tsx b/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.tsx index bb6cf600360..a79cc4496a6 100644 --- a/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.tsx +++ b/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.tsx @@ -286,17 +286,18 @@ export class UnthemedVirtualizedTraceView extends React.Component { const { isDetail, span } = this.getRowStates()[index]; - return `${span.spanID}--${isDetail ? 'detail' : 'bar'}`; + return `${span.traceID}--${span.spanID}--${isDetail ? 'detail' : 'bar'}`; }; getIndexFromKey = (key: string) => { const parts = key.split('--'); - const _spanID = parts[0]; - const _isDetail = parts[1] === 'detail'; + const _traceID = parts[0]; + const _spanID = parts[1]; + const _isDetail = parts[2] === 'detail'; const max = this.getRowStates().length; for (let i = 0; i < max; i++) { const { span, isDetail } = this.getRowStates()[i]; - if (span.spanID === _spanID && isDetail === _isDetail) { + if (span.spanID === _spanID && span.traceID === _traceID && isDetail === _isDetail) { return i; } } diff --git a/public/app/features/explore/TraceView/TraceView.test.tsx b/public/app/features/explore/TraceView/TraceView.test.tsx index 243c3942ad4..be916efcbb0 100644 --- a/public/app/features/explore/TraceView/TraceView.test.tsx +++ b/public/app/features/explore/TraceView/TraceView.test.tsx @@ -143,6 +143,26 @@ describe('TraceView', () => { table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' }); expect(table.innerHTML).toContain('client-uuid-3'); }); + + it('resets detail view for new trace with the identical spanID', () => { + const store = configureStore(); + const { rerender } = render( + + {}} /> + + ); + const span = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[2]; + userEvent.click(span); + //Process is in detail view + expect(screen.getByText(/Process/)).toBeInTheDocument(); + + rerender( + + {}} /> + + ); + expect(screen.queryByText(/Process/)).not.toBeInTheDocument(); + }); }); const response: TraceData & { spans: TraceSpanData[] } = { diff --git a/public/app/features/explore/TraceView/TraceView.tsx b/public/app/features/explore/TraceView/TraceView.tsx index e3c61620d3c..de10dc4a6c2 100644 --- a/public/app/features/explore/TraceView/TraceView.tsx +++ b/public/app/features/explore/TraceView/TraceView.tsx @@ -18,7 +18,9 @@ import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getTimeZone } from 'app/features/profile/state/selectors'; import { StoreState } from 'app/types'; import { ExploreId } from 'app/types/explore'; -import React, { useCallback, useMemo, useState } from 'react'; +import { isEqual } from 'lodash'; +import React, { useCallback, useMemo, useState, useEffect } from 'react'; +import { usePrevious } from 'react-use'; import { useSelector } from 'react-redux'; import { createSpanLinkFactory } from './createSpanLink'; import { UIElements } from './uiElements'; @@ -39,6 +41,10 @@ type Props = { }; export function TraceView(props: Props) { + // At this point we only show single trace + const frame = props.dataFrames[0]; + const prevFrame = usePrevious(frame); + const { expandOne, collapseOne, childrenToggle, collapseAll, childrenHiddenIDs, expandAll } = useChildrenState(); const { detailStates, @@ -50,7 +56,16 @@ export function TraceView(props: Props) { detailTagsToggle, detailWarningsToggle, detailStackTracesToggle, + clearDetailStates, } = useDetailState(); + + // Clear detail state when new trace arrives + useEffect(() => { + if (frame && prevFrame && !isEqual(prevFrame, frame)) { + clearDetailStates(); + } + }, [frame, prevFrame, clearDetailStates]); + const { removeHoverIndentGuideId, addHoverIndentGuideId, hoverIndentGuideIds } = useHoverIndentGuide(); const { viewRange, updateViewRangeTime, updateNextViewRangeTime } = useViewRange(); @@ -63,8 +78,6 @@ export function TraceView(props: Props) { */ const [slim, setSlim] = useState(false); - // At this point we only show single trace. - const frame = props.dataFrames[0]; const traceProp = useMemo(() => transformDataFrames(frame), [frame]); const { search, setSearch, spanFindMatches } = useSearch(traceProp?.spans); const dataSourceName = useSelector((state: StoreState) => state.explore[props.exploreId]?.datasourceInstance?.name); diff --git a/public/app/features/explore/TraceView/useDetailState.test.ts b/public/app/features/explore/TraceView/useDetailState.test.ts index 012c50ec5c1..caf62e1e047 100644 --- a/public/app/features/explore/TraceView/useDetailState.test.ts +++ b/public/app/features/explore/TraceView/useDetailState.test.ts @@ -3,6 +3,18 @@ import { useDetailState } from './useDetailState'; import { TraceLog } from '@jaegertracing/jaeger-ui-components/src/types/trace'; describe('useDetailState', () => { + it('clears detail state', async () => { + const { result } = renderHook(() => useDetailState()); + expect(result.current.detailStates.size).toBe(0); + + act(() => result.current.toggleDetail('span1')); + expect(result.current.detailStates.size).toBe(1); + expect(result.current.detailStates.has('span1')).toBe(true); + + act(() => result.current.clearDetailStates()); + expect(result.current.detailStates.size).toBe(0); + }); + it('toggles detail', async () => { const { result } = renderHook(() => useDetailState()); expect(result.current.detailStates.size).toBe(0); diff --git a/public/app/features/explore/TraceView/useDetailState.ts b/public/app/features/explore/TraceView/useDetailState.ts index 2d60a8ca78d..157d25c7b91 100644 --- a/public/app/features/explore/TraceView/useDetailState.ts +++ b/public/app/features/explore/TraceView/useDetailState.ts @@ -9,6 +9,8 @@ import { TraceLog } from '@jaegertracing/jaeger-ui-components/src/types/trace'; export function useDetailState() { const [detailStates, setDetailStates] = useState(new Map()); + const clearDetailStates = useCallback(() => setDetailStates(new Map()), [setDetailStates]); + const toggleDetail = useCallback( function toggleDetail(spanID: string) { const newDetailStates = new Map(detailStates); @@ -38,6 +40,7 @@ export function useDetailState() { return { detailStates, + clearDetailStates, toggleDetail, detailLogItemToggle, detailLogsToggle: useCallback(