From b0d3caeef29d421e835f28641b672641d3e4adfb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 1 May 2026 01:08:58 +0000 Subject: [PATCH 1/5] Add Show spans/Show logs visibility toggles to trace waterfall panel Users can now independently show/hide spans and log events in the trace waterfall using dedicated checkboxes. The 'Show spans' checkbox is always visible; the 'Show logs' checkbox appears when a log source is configured. Filtering is applied client-side after the DAG flattening step, so the toggle state has no effect on data fetching. Counts in the header update to reflect only visible rows. Co-authored-by: Mike Shi --- .../src/components/DBTraceWaterfallChart.tsx | 39 +++++++++++--- .../__tests__/DBTraceWaterfallChart.test.tsx | 51 +++++++++++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/packages/app/src/components/DBTraceWaterfallChart.tsx b/packages/app/src/components/DBTraceWaterfallChart.tsx index dfca012dab..674c6ab285 100644 --- a/packages/app/src/components/DBTraceWaterfallChart.tsx +++ b/packages/app/src/components/DBTraceWaterfallChart.tsx @@ -610,6 +610,8 @@ export function DBTraceWaterfallChartContainer({ const [collapsedIds, setCollapsedIds] = useState>(new Set()); const [showSpanEvents, setShowSpanEvents] = useState(true); + const [showSpans, setShowSpans] = useState(true); + const [showLogs, setShowLogs] = useState(true); const { nodesMap, flattenedNodes } = useMemo(() => { const rootNodes: Node[] = []; @@ -737,8 +739,16 @@ export function DBTraceWaterfallChartContainer({ [nodesMap], ); - const spanCount = flattenedNodes.length; - const errorCount = flattenedNodes.filter( + const visibleNodes = useMemo(() => { + if (showSpans && showLogs) return flattenedNodes; + return flattenedNodes.filter(node => { + if (node.type === SourceKind.Log) return showLogs; + return showSpans; + }); + }, [flattenedNodes, showSpans, showLogs]); + + const spanCount = visibleNodes.length; + const errorCount = visibleNodes.filter( node => node.StatusCode === 'Error' || node.SeverityText?.toLowerCase() === 'error', @@ -763,7 +773,7 @@ export function DBTraceWaterfallChartContainer({ const timelineRows = useMemo( () => - flattenedNodes.map((result, i) => { + visibleNodes.map((result, i) => { const tookMs = (result.Duration || 0) * 1000; const startOffset = new Date(result.Timestamp).getTime(); const start = startOffset - minOffset; @@ -940,7 +950,7 @@ export function DBTraceWaterfallChartContainer({ }), [ collapsedIds, - flattenedNodes, + visibleNodes, formatTime, highlightedRowWhere, isFilterActive, @@ -952,8 +962,7 @@ export function DBTraceWaterfallChartContainer({ setCollapseTooltipShown, ], ); - // TODO: Highlighting support - const initialScrollRowIndex = flattenedNodes.findIndex(v => { + const initialScrollRowIndex = visibleNodes.findIndex(v => { return v.id === highlightedRowWhere; }); @@ -1013,6 +1022,22 @@ export function DBTraceWaterfallChartContainer({ {errorCountString} + setShowSpans(!showSpans)} + data-testid="show-spans-checkbox" + /> + {logTableSource && ( + setShowLogs(!showLogs)} + data-testid="show-logs-checkbox" + /> + )} An unknown error occurred. - ) : flattenedNodes.length === 0 ? ( + ) : visibleNodes.length === 0 ? ( (emptyState ?? (
No matching spans or logs found
)) diff --git a/packages/app/src/components/__tests__/DBTraceWaterfallChart.test.tsx b/packages/app/src/components/__tests__/DBTraceWaterfallChart.test.tsx index debedf9c1b..058f40dc79 100644 --- a/packages/app/src/components/__tests__/DBTraceWaterfallChart.test.tsx +++ b/packages/app/src/components/__tests__/DBTraceWaterfallChart.test.tsx @@ -6,6 +6,7 @@ import { } from '@hyperdx/common-utils/dist/types'; import { screen, waitFor } from '@testing-library/react'; import { renderHook } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { TimelineChart } from '@/components/TimelineChart'; import useOffsetPaginatedQuery from '@/hooks/useOffsetPaginatedQuery'; @@ -310,6 +311,56 @@ describe('DBTraceWaterfallChartContainer', () => { screen.getByText('http span https://api.example.com/users'), ).toBeInTheDocument(); }); + + it('renders Show spans and Show logs checkboxes when log source is present', async () => { + setupQueryMocks({ traceData: mockTraceData, logData: mockLogData }); + renderComponent(); + await waitForLoading(); + + expect(screen.getByTestId('show-spans-checkbox')).toBeInTheDocument(); + expect(screen.getByTestId('show-logs-checkbox')).toBeInTheDocument(); + }); + + it('does not render Show logs checkbox when no log source', async () => { + setupQueryMocks({ traceData: mockTraceData }); + renderComponent(null); + await waitForLoading(); + + expect(screen.getByTestId('show-spans-checkbox')).toBeInTheDocument(); + expect(screen.queryByTestId('show-logs-checkbox')).not.toBeInTheDocument(); + }); + + it('hides log rows when Show logs is unchecked', async () => { + const user = userEvent.setup(); + setupQueryMocks({ traceData: mockTraceData, logData: mockLogData }); + renderComponent(); + await waitForLoading(); + + expect(MockTimelineChart.latestProps.rows.length).toBe(2); + + const showLogsCheckbox = screen.getByTestId('show-logs-checkbox'); + await user.click(showLogsCheckbox); + + await waitFor(() => { + expect(MockTimelineChart.latestProps.rows.length).toBe(1); + }); + }); + + it('hides span rows when Show spans is unchecked', async () => { + const user = userEvent.setup(); + setupQueryMocks({ traceData: mockTraceData, logData: mockLogData }); + renderComponent(); + await waitForLoading(); + + expect(MockTimelineChart.latestProps.rows.length).toBe(2); + + const showSpansCheckbox = screen.getByTestId('show-spans-checkbox'); + await user.click(showSpansCheckbox); + + await waitFor(() => { + expect(MockTimelineChart.latestProps.rows.length).toBe(1); + }); + }); }); describe('useEventsAroundFocus', () => { From 887878f08f6aec670eb2ffdcaec7216577c4f9c1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 1 May 2026 01:59:36 +0000 Subject: [PATCH 2/5] Replace checkboxes with compact Chip toggles for waterfall visibility controls Switches from three full Checkbox components to Mantine Chip pills, giving a more compact, discoverable UI. Chips are visually prominent enough to be noticed but take significantly less horizontal space than labeled checkboxes. Co-authored-by: Mike Shi --- .../src/components/DBTraceWaterfallChart.tsx | 31 ++++++++++++------- .../__tests__/DBTraceWaterfallChart.test.tsx | 24 +++++++------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/packages/app/src/components/DBTraceWaterfallChart.tsx b/packages/app/src/components/DBTraceWaterfallChart.tsx index 674c6ab285..d3eb6d1d8e 100644 --- a/packages/app/src/components/DBTraceWaterfallChart.tsx +++ b/packages/app/src/components/DBTraceWaterfallChart.tsx @@ -17,7 +17,7 @@ import { Anchor, Box, Center, - Checkbox, + Chip, Code, Divider, Group, @@ -1022,28 +1022,35 @@ export function DBTraceWaterfallChartContainer({ {errorCountString} - setShowSpans(!showSpans)} - data-testid="show-spans-checkbox" - /> + data-testid="show-spans-chip" + > + Spans + {logTableSource && ( - setShowLogs(!showLogs)} - data-testid="show-logs-checkbox" - /> + data-testid="show-logs-chip" + > + Logs + )} - setShowSpanEvents(!showSpanEvents)} - /> + data-testid="show-span-events-chip" + > + Span events + { ).toBeInTheDocument(); }); - it('renders Show spans and Show logs checkboxes when log source is present', async () => { + it('renders Spans and Logs chips when log source is present', async () => { setupQueryMocks({ traceData: mockTraceData, logData: mockLogData }); renderComponent(); await waitForLoading(); - expect(screen.getByTestId('show-spans-checkbox')).toBeInTheDocument(); - expect(screen.getByTestId('show-logs-checkbox')).toBeInTheDocument(); + expect(screen.getByTestId('show-spans-chip')).toBeInTheDocument(); + expect(screen.getByTestId('show-logs-chip')).toBeInTheDocument(); }); - it('does not render Show logs checkbox when no log source', async () => { + it('does not render Logs chip when no log source', async () => { setupQueryMocks({ traceData: mockTraceData }); renderComponent(null); await waitForLoading(); - expect(screen.getByTestId('show-spans-checkbox')).toBeInTheDocument(); - expect(screen.queryByTestId('show-logs-checkbox')).not.toBeInTheDocument(); + expect(screen.getByTestId('show-spans-chip')).toBeInTheDocument(); + expect(screen.queryByTestId('show-logs-chip')).not.toBeInTheDocument(); }); - it('hides log rows when Show logs is unchecked', async () => { + it('hides log rows when Logs chip is toggled off', async () => { const user = userEvent.setup(); setupQueryMocks({ traceData: mockTraceData, logData: mockLogData }); renderComponent(); @@ -338,15 +338,15 @@ describe('DBTraceWaterfallChartContainer', () => { expect(MockTimelineChart.latestProps.rows.length).toBe(2); - const showLogsCheckbox = screen.getByTestId('show-logs-checkbox'); - await user.click(showLogsCheckbox); + const showLogsChip = screen.getByTestId('show-logs-chip'); + await user.click(showLogsChip); await waitFor(() => { expect(MockTimelineChart.latestProps.rows.length).toBe(1); }); }); - it('hides span rows when Show spans is unchecked', async () => { + it('hides span rows when Spans chip is toggled off', async () => { const user = userEvent.setup(); setupQueryMocks({ traceData: mockTraceData, logData: mockLogData }); renderComponent(); @@ -354,8 +354,8 @@ describe('DBTraceWaterfallChartContainer', () => { expect(MockTimelineChart.latestProps.rows.length).toBe(2); - const showSpansCheckbox = screen.getByTestId('show-spans-checkbox'); - await user.click(showSpansCheckbox); + const showSpansChip = screen.getByTestId('show-spans-chip'); + await user.click(showSpansChip); await waitFor(() => { expect(MockTimelineChart.latestProps.rows.length).toBe(1); From 77e769808460ebaea84bbb393897274cfd777b53 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 1 May 2026 02:19:54 +0000 Subject: [PATCH 3/5] Use 'Show:' label prefix with tighter chip padding for sleeker look Adds a 'Show:' label before the chip group, reduces chip padding inline to 8px and height to 22px, and uses Chip.Group for proper grouping. Co-authored-by: Mike Shi --- .../src/components/DBTraceWaterfallChart.tsx | 71 +++++++++++-------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/packages/app/src/components/DBTraceWaterfallChart.tsx b/packages/app/src/components/DBTraceWaterfallChart.tsx index d3eb6d1d8e..50add6a79c 100644 --- a/packages/app/src/components/DBTraceWaterfallChart.tsx +++ b/packages/app/src/components/DBTraceWaterfallChart.tsx @@ -1022,35 +1022,48 @@ export function DBTraceWaterfallChartContainer({ {errorCountString} - setShowSpans(!showSpans)} - data-testid="show-spans-chip" - > - Spans - - {logTableSource && ( - setShowLogs(!showLogs)} - data-testid="show-logs-chip" - > - Logs - - )} - setShowSpanEvents(!showSpanEvents)} - data-testid="show-span-events-chip" - > - Span events - + + + Show: + + + setShowSpans(!showSpans)} + data-testid="show-spans-chip" + styles={{ + label: { paddingInline: 8, height: 22, minHeight: 22 }, + }} + > + Spans + + {logTableSource && ( + setShowLogs(!showLogs)} + data-testid="show-logs-chip" + styles={{ + label: { paddingInline: 8, height: 22, minHeight: 22 }, + }} + > + Logs + + )} + setShowSpanEvents(!showSpanEvents)} + data-testid="show-span-events-chip" + styles={{ + label: { paddingInline: 8, height: 22, minHeight: 22 }, + }} + > + Span events + + + Date: Fri, 1 May 2026 02:31:33 +0000 Subject: [PATCH 4/5] Mute chip color to gray and add spacing between Show label and chips Co-authored-by: Mike Shi --- .../src/components/DBTraceWaterfallChart.tsx | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/packages/app/src/components/DBTraceWaterfallChart.tsx b/packages/app/src/components/DBTraceWaterfallChart.tsx index 50add6a79c..2b1484c67c 100644 --- a/packages/app/src/components/DBTraceWaterfallChart.tsx +++ b/packages/app/src/components/DBTraceWaterfallChart.tsx @@ -1022,46 +1022,51 @@ export function DBTraceWaterfallChartContainer({ {errorCountString} - + Show: - setShowSpans(!showSpans)} - data-testid="show-spans-chip" - styles={{ - label: { paddingInline: 8, height: 22, minHeight: 22 }, - }} - > - Spans - - {logTableSource && ( + setShowLogs(!showLogs)} - data-testid="show-logs-chip" + color="gray" + checked={showSpans} + onChange={() => setShowSpans(!showSpans)} + data-testid="show-spans-chip" styles={{ label: { paddingInline: 8, height: 22, minHeight: 22 }, }} > - Logs + Spans - )} - setShowSpanEvents(!showSpanEvents)} - data-testid="show-span-events-chip" - styles={{ - label: { paddingInline: 8, height: 22, minHeight: 22 }, - }} - > - Span events - + {logTableSource && ( + setShowLogs(!showLogs)} + data-testid="show-logs-chip" + styles={{ + label: { paddingInline: 8, height: 22, minHeight: 22 }, + }} + > + Logs + + )} + setShowSpanEvents(!showSpanEvents)} + data-testid="show-span-events-chip" + styles={{ + label: { paddingInline: 8, height: 22, minHeight: 22 }, + }} + > + Span events + + From 3e4343e5c50a5cd0c009ce59e96318f0c21d81cd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 2 May 2026 00:33:08 +0000 Subject: [PATCH 5/5] Fix misleading empty state when toggles hide all items; drop redundant Chip.Group Show 'All items are hidden by filters' when data exists but visibility toggles hide everything, instead of the generic 'No matching spans or logs found'. Replace Chip.Group (unused for controlled chips) with a plain Group. Co-authored-by: Mike Shi --- .../src/components/DBTraceWaterfallChart.tsx | 76 ++++++++++--------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/packages/app/src/components/DBTraceWaterfallChart.tsx b/packages/app/src/components/DBTraceWaterfallChart.tsx index 2b1484c67c..77c348c9da 100644 --- a/packages/app/src/components/DBTraceWaterfallChart.tsx +++ b/packages/app/src/components/DBTraceWaterfallChart.tsx @@ -1026,48 +1026,46 @@ export function DBTraceWaterfallChartContainer({ Show: - - + + setShowSpans(!showSpans)} + data-testid="show-spans-chip" + styles={{ + label: { paddingInline: 8, height: 22, minHeight: 22 }, + }} + > + Spans + + {logTableSource && ( setShowSpans(!showSpans)} - data-testid="show-spans-chip" + checked={showLogs} + onChange={() => setShowLogs(!showLogs)} + data-testid="show-logs-chip" styles={{ label: { paddingInline: 8, height: 22, minHeight: 22 }, }} > - Spans + Logs - {logTableSource && ( - setShowLogs(!showLogs)} - data-testid="show-logs-chip" - styles={{ - label: { paddingInline: 8, height: 22, minHeight: 22 }, - }} - > - Logs - - )} - setShowSpanEvents(!showSpanEvents)} - data-testid="show-span-events-chip" - styles={{ - label: { paddingInline: 8, height: 22, minHeight: 22 }, - }} - > - Span events - - - + )} + setShowSpanEvents(!showSpanEvents)} + data-testid="show-span-events-chip" + styles={{ + label: { paddingInline: 8, height: 22, minHeight: 22 }, + }} + > + Span events + + @@ -1122,9 +1120,13 @@ export function DBTraceWaterfallChartContainer({ An unknown error occurred. ) : visibleNodes.length === 0 ? ( - (emptyState ?? ( -
No matching spans or logs found
- )) + flattenedNodes.length > 0 ? ( +
All items are hidden by filters
+ ) : ( + (emptyState ?? ( +
No matching spans or logs found
+ )) + ) ) : (