Skip to content

feat: trace waterfall redesign#2565

Merged
kodiakhq[bot] merged 6 commits into
mainfrom
hdx-3945-3952-trace-waterfall-redesign
Jul 8, 2026
Merged

feat: trace waterfall redesign#2565
kodiakhq[bot] merged 6 commits into
mainfrom
hdx-3945-3952-trace-waterfall-redesign

Conversation

@karl-power

@karl-power karl-power commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Redesigns the trace waterfall chart and cleans up the surrounding trace panel layout.

Waterfall redesign (HDX-3945)

  • Per-service colors: each service gets a stable color (sorted for render-order stability). Green is reserved for correlated log rows and red for error spans, so a service color is never confused with a log or error.
  • Bar & label layout: no text inside bars — the operation/service label sits beside a vertical service color bar, with the span duration shown as muted text and the span body revealed on hover, placed on whichever side has more room.
  • Depth controls: expand/collapse one level and expand/collapse all, only shown when the trace has collapsible nodes.

Trace panel layout (HDX-3952)

  • Removed the duplicated Trace ID from the panel body (it lives in the side panel header). The Trace ID Expression editor now only appears as a fallback when no trace id resolves for the event.
  • Moved the Correlated Logs source selector into the waterfall controls bar as a compact selector.

Collapse hint bug (HDX-4444)

  • Removed the flaky tooltip-based Alt+click collapse hint (CollapseTooltipLabel / useDisclosure) that could render multiple times when chevrons were highlighted in quick succession. Collapse is now an always-present chevron control.

Supporting changes

  • useWaterfallSearchState now persists the filter language (whereLanguage) in the URL so shared links / reloads rebuild the query in the language it was written in.
  • SearchWhereInput accepts a parentRef for popover positioning.

Testing

  • Added DBTraceWaterfallChart.test.tsx covering the depth-control logic.
  • Updated DBTracePanel.test.tsx for the new layout.

Screenshots or video

Screenshot 2026-07-01 at 17 12 42

How to test on Vercel preview

Preview routes: /search

Steps:

  1. Open the trace timeline and explore the new features described above.

References

@changeset-bot

changeset-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ff6e4de

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Patch
@hyperdx/api Patch
@hyperdx/otel-collector Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview, Comment Jul 8, 2026 1:28pm
hyperdx-storybook Ready Ready Preview, Comment Jul 8, 2026 1:28pm

Request Review

@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🟡 Tier 3 — Standard

Introduces new logic, modifies core functionality, or touches areas with non-trivial risk.

Why this tier:

  • Diff size: 997 production lines changed (Tier 2 max: < 250)

Review process: Full human review — logic, architecture, edge cases.
SLA: First-pass feedback within 1 business day.

Stats
  • Production files changed: 10
  • Production lines changed: 997 (+ 482 in test files, excluded from tier calculation)
  • Branch: hdx-3945-3952-trace-waterfall-redesign
  • Author: karl-power

To override this classification, remove the review/tier-3 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR redesigns the trace waterfall chart with per-service span colors, a vertical service color bar in the label column, depth controls (expand/collapse one level and all), and moves the correlated-logs source selector into the waterfall controls bar. It also introduces a filter-fallback mechanism so a bad filter expression never blanks the chart — the offending query falls back to unfiltered data and surfaces an inline error instead.

  • Waterfall redesign: per-service color palette (green and red reserved for logs/errors), icon-based error/warning indicators in the label area, span body revealed beside the duration label on hover, and explicit chevron depth controls replacing the flaky Alt+click tooltip.
  • Trace panel cleanup: removed duplicate Trace ID from panel body, trace ID expression editor now only surfaces as a fallback, and the correlated-logs source selector moved into the controls bar via the new controlsExtra prop.
  • Filter resilience: useFilteredEventsAroundFocus wraps useEventsAroundFocus with an automatic unfiltered fallback on query error; useWaterfallSearchState now persists whereLanguage in the URL so shared links preserve the query language.

Confidence Score: 5/5

Safe to merge — the filter-fallback mechanism and language-persistence changes are well-tested, and all edge cases in the depth-control helpers are covered by unit tests.

The waterfall redesign touches several interlocking pieces (color mapping, depth controls, language-aware filter queries, fallback error handling) but each is cleanly isolated and tested. The useFilteredEventsAroundFocus fallback pattern correctly follows React hook rules, the color-mix highlight approach works with fully resolved hex values from getChartColorError(), and the two previous thread concerns are both addressed in this PR.

No files require special attention — the new useFilteredEventsAroundFocus wrapper and the useWaterfallSearchState language persistence are the most behaviorally significant changes and both look correct.

Important Files Changed

Filename Overview
packages/app/src/components/DBTraceWaterfallChart.tsx Core waterfall redesign: adds per-service color map, new depth controls (computeExpand/CollapseOneLevel/All), replaces SearchInputV2 with SearchWhereInput (language-aware), introduces useFilteredEventsAroundFocus for filter-error fallback, and threads hiddenRowExpressionLanguage through to the backend query.
packages/app/src/components/DBTracePanel.tsx Trace panel cleanup: removes duplicated Trace ID header block, converts source selector from a standalone section to a controlsExtra slot in the waterfall controls bar, limits Trace ID expression editor to the no-traceId fallback case.
packages/app/src/components/TimelineChart/TimelineXAxis.tsx Splits single tick container into two: a full-height grid-lines layer (painted behind rows at zero z-index) and the existing sticky label header, both driven by the same spacing computation to stay column-aligned.
packages/app/src/components/TimelineChart/TimelineChartRowEvents.tsx Removes inline body text from bars; moves duration label outside the bar with a new barDetail/barDetailBody CSS pattern that reveals the span body on row hover using CSS class selectors.
packages/app/src/hooks/useWaterfallSearchState.tsx Adds traceWhereLanguage and logWhereLanguage URL params, persisted and cleared alongside the filter text, so shared links and reloads reconstruct the filter in the same language it was written in.
packages/app/src/components/tests/DBTraceWaterfallChart.test.tsx Adds thorough test coverage: depth controls visibility, filter-error fallback behavior (filter error vs. fatal error), separate span/log filter inputs, and a full suite for all four depth-control pure functions.
packages/app/src/utils.ts Removes unused getChartColorErrorHighlight and getChartColorWarningHighlight exports; highlight colors for error/service bars are now computed inline with color-mix().
packages/app/src/components/SearchInput/SearchWhereInput.tsx Adds optional parentRef prop, forwarded to the SQL editor so its autocomplete popover can portal to document.body when the waterfall's overflow:hidden column would otherwise clip it.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[useFilteredEventsAroundFocus] --> B[useEventsAroundFocus - filtered query]
    B --> C{filterFailed?\nenabled && hiddenExpr && error}
    C -- No --> D[return filtered rows + fatalError]
    C -- Yes --> E[useEventsAroundFocus - fallback, no filter]
    E --> F{fallback also errored?}
    F -- No --> G[return fallback rows + filterError]
    F -- Yes --> H[return empty rows + fatalError]
    D --> I[DBTraceWaterfallChartContainer]
    G --> I
    H --> I
    I --> J{traceError / fatalError?}
    J -- Yes --> K[Show full-chart error block]
    J -- No --> L[Render TimelineChart]
    L --> M[traceFilterError? Show inline error]
    L --> N[logError? Show inline error]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[useFilteredEventsAroundFocus] --> B[useEventsAroundFocus - filtered query]
    B --> C{filterFailed?\nenabled && hiddenExpr && error}
    C -- No --> D[return filtered rows + fatalError]
    C -- Yes --> E[useEventsAroundFocus - fallback, no filter]
    E --> F{fallback also errored?}
    F -- No --> G[return fallback rows + filterError]
    F -- Yes --> H[return empty rows + fatalError]
    D --> I[DBTraceWaterfallChartContainer]
    G --> I
    H --> I
    I --> J{traceError / fatalError?}
    J -- Yes --> K[Show full-chart error block]
    J -- No --> L[Render TimelineChart]
    L --> M[traceFilterError? Show inline error]
    L --> N[logError? Show inline error]
Loading

Reviews (11): Last reviewed commit: "Merge branch 'main' into hdx-3945-3952-t..." | Re-trigger Greptile

Comment thread packages/app/src/components/DBTraceWaterfallChart.tsx
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 227 passed • 3 skipped • 1524s

Status Count
✅ Passed 227
❌ Failed 0
⚠️ Flaky 1
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Deep Review

No critical issues found. No P0/P1 ship-blockers: the trace-id WHERE clause is escaped (SqlString.escape, tested), the depth-control helpers match their unit tests, and the two feared race conditions — the split x-axis grid/label containers desyncing and the removed useDisclosure collapse tooltip — were each independently refuted (the containers are resized in one synchronous recompute with a shared null-guard). The items below are recommended fixes and nits.

🟡 P2 — recommended

  • packages/app/src/components/DBTraceWaterfallChart.tsx:625 — When a shared/legacy URL carries a where filter but no matching language param, the form defaults its language toggle from the stored preference while the query executes with a hardcoded lucene fallback, so the input can display SQL while the filter actually runs as Lucene (and a re-submit then reinterprets the text).
    • Fix: Default the form's language field from the same value that runs the query (traceFilterLanguage/logFilterLanguage) so display and execution can never disagree.
    • correctness, adversarial
  • packages/app/src/components/DBTracePanel.tsx:379 — The relocated source selector is now hardcoded to the label Correlated logs even when the panel is opened from a log event, where the selector actually picks the correlated trace source (the prior code labeled that case Trace Source).
    • Fix: Restore the conditional label so it reads Trace Source when the parent source is a log and Correlated logs otherwise.
  • packages/app/src/hooks/useWaterfallSearchState.tsx:29 — The new traceWhereLanguage/logWhereLanguage URL round-trip and the clear() reset have no test; the hook is mocked in its only consumer test, so the added branches never execute under test.
    • Fix: Add a hook test asserting onSubmit writes both language params, empty strings coerce to null, undefined leaves a param untouched, and clear() resets all four params.
  • packages/app/src/components/DBTraceWaterfallChart.tsx:110 — The redesigned barColor/serviceColorMap behavior (log → green, error → red, stable per-service colors that exclude green/red) is asserted only via rows.length, never against the computed backgroundColor.
    • Fix: Assert the mocked chart rows' backgroundColor: a log row is green, an error span is red, and two services get distinct colors that stay stable across re-renders.
  • packages/app/src/components/DBTraceWaterfallChart.tsx:614 — The 'lucene' | 'sql' normalization/default logic is duplicated across the filter-language derivation, the useForm defaults, and the clear handler, and the copies already differ subtly — that drift is the root of the display/execution mismatch above.
    • Fix: Extract one normalizeLanguage(value): 'lucene' | 'sql' helper and use it at every site.
    • maintainability, kieran-typescript
🔵 P3 nitpicks (7)
  • packages/app/src/components/DBTraceWaterfallChart.tsx:838 — A fatal correlated-log load failure renders only inside the expanded filter form, so with filters collapsed the failure is invisible to the user.
    • Fix: Surface a fatal (non-filter) log error regardless of the filter panel's expanded state.
  • packages/app/src/components/DBTraceWaterfallChart.tsx:1087 — The label's service-color strip (barBackgroundColor) and the timeline bar (barColor) are computed by two separate code paths that already diverge on error handling, inviting future drift.
    • Fix: Route both through a single shared color function, or document why the label strip intentionally omits the error branch.
  • packages/app/src/components/DBTraceWaterfallChart.tsx:136 — The fallback span colors #6A7077/#A9AFB7 are hardcoded and duplicated across barColor and barBackgroundColor, bypassing the palette system used elsewhere.
    • Fix: Hoist them to named constants (or palette tokens) defined once.
  • packages/app/src/utils.ts:421 — Removing getChartColorErrorHighlight/getChartColorWarningHighlight leaves the warningHighlight/errorHighlight palette keys and CSS tokens with no readers.
    • Fix: Drop the now-unused keys and tokens, or annotate them as intentionally retained.
  • packages/app/src/hooks/useWaterfallSearchState.tsx:24 — The language fields are typed as raw string, so the 'lucene' | 'sql' invariant is only re-established by narrowing at the consumer and a bad value would serialize to the URL unchecked.
    • Fix: Type the language fields as a 'lucene' | 'sql' union (or parse with nuqs parseAsStringEnum).
  • packages/app/src/components/DBTraceWaterfallChart.tsx:840Node.children is typed SpanRow[] though children are recursively Nodes, forcing the new collectParents/traverse code to fall back to any.
    • Fix: Make Node.children: Node[] and type the traversal helpers against Node.
  • packages/app/src/components/DBTraceWaterfallChart.tsx:1 — The file is now 1537 lines, well past the project's 300-line component guideline, and this redesign grew it rather than extracting sub-components (pre-existing, worsened here).
    • Fix: Extract cohesive pieces (the row label, the depth toolbar, the axis-sync effect) into separate components/hooks.

Reviewers (10): correctness, adversarial, kieran-typescript, julik-frontend-races, testing, maintainability, performance, project-standards, agent-native, learnings-researcher.

Testing gaps:

  • Depth-control buttons' click → visibleNodes state change is not integration-tested (only the pure helpers and button presence are covered).
  • The TimelineXAxis two-container grid/label split (imperative DOM) has no test.
  • The TimelineChartRowEvents body relocation / row-reverse placement is untested.
  • useFilteredEventsAroundFocus is not tested for the combined case (one source's filter errors while the other source fatally fails).

Note: Performance confirmed the un-memoized per-render O(n) scans (foundMinOffset, the three count filters, initialScrollRowIndex) and the per-row JSX label construction at the 50k-row limit are pre-existing and not introduced here; the diff adds only a small per-row color strip and icons, so no new regression. No relevant prior learnings exist in docs/solutions/ (application-frontend knowledge base is currently empty), and no agent-native parity gaps were found — the one stateful addition (filter + language) round-trips through the URL and is mirrored by the existing trace_waterfall MCP tool.

@elizabetdev elizabetdev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

It's looking good! 🎉

From a design perspective we no longer need the left tabs because they moved to the right side panel of the traces view.

Now that we have a top keyboard shortcut action, I don't think we need to show the keyboard shortcut above the spans.

@karl-power

Copy link
Copy Markdown
Contributor Author

It's looking good! 🎉

From a design perspective we no longer need the left tabs because they moved to the right side panel of the traces view.

Now that we have a top keyboard shortcut action, I don't think we need to show the keyboard shortcut above the spans.

Thanks for taking a look @elizabetdev! Both of those points should already be addressed in #2541

The changes here are focused mostly on the waterfall/timeline.

@karl-power karl-power force-pushed the hdx-3945-3952-trace-waterfall-redesign branch 2 times, most recently from eb9103a to c57c581 Compare July 7, 2026 14:12
@teeohhem

teeohhem commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

@karl-power the P0 is a legit issue with the combined filters approach

🔴 P0/P1 — must fix
packages/app/src/components/DBTraceWaterfallChart.tsx:646 — the single filter input is written to both traceWhere and logWhere and both queries now run in the user-selected SQL language, so a span-only-column filter (e.g. the sqlPlaceholder's own StatusCode = 'Error' example) is applied as NOT (...) against the log source, whose table lacks that column; the resulting query error sets logError, and error = traceError || logError replaces the entire waterfall — valid spans included — with a raw error block.
Fix: isolate log-source filter/query errors from the span render path so a log-only failure cannot blank the whole chart, and/or stop mirroring a span-scoped filter verbatim onto the log query.
adversarial, correctness

Before: separate log/trace filter, no issue
image

Combined, if you search for something like SpanName='whatever', you will get this error as SpanName doesn't exist on the logs source.
image

@kodiakhq

kodiakhq Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

This PR currently has a merge conflict. Please resolve this and then re-add the automerge label.

@kodiakhq kodiakhq Bot removed the automerge label Jul 7, 2026
Prefer the URL's whereLanguage param when seeding the filter language
toggle so a shared link / reload shows the same language the query runs
in, and reset the toggle on clear so a stale value isn't re-serialized
into the URL on the next submit. Also drop the now-unused
getChartColorErrorHighlight / getChartColorWarningHighlight exports.
@karl-power

Copy link
Copy Markdown
Contributor Author

@karl-power the P0 is a legit issue with the combined filters approach

Thanks, I missed this. I just updated it now to split the combined filter into separate spans and logs inputs. A failing filter falls back to unfiltered results with the error surfaced inline next to its input.

@teeohhem

teeohhem commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

@karl-power slick :)
image

@kodiakhq kodiakhq Bot merged commit 6e25c1d into main Jul 8, 2026
20 checks passed
@kodiakhq kodiakhq Bot deleted the hdx-3945-3952-trace-waterfall-redesign branch July 8, 2026 13:33
jordan-simonovski pushed a commit that referenced this pull request Jul 13, 2026
## Summary



Redesigns the trace waterfall chart and cleans up the surrounding trace panel layout.

### Waterfall redesign (HDX-3945)
- **Per-service colors**: each service gets a stable color (sorted for render-order stability). Green is reserved for correlated log rows and red for error spans, so a service color is never confused with a log or error.
- **Bar & label layout**: no text inside bars — the operation/service label sits beside a vertical service color bar, with the span duration shown as muted text and the span body revealed on hover, placed on whichever side has more room.
- **Depth controls**: expand/collapse one level and expand/collapse all, only shown when the trace has collapsible nodes.

### Trace panel layout (HDX-3952)
- Removed the duplicated Trace ID from the panel body (it lives in the side panel header). The Trace ID Expression editor now only appears as a fallback when no trace id resolves for the event.
- Moved the Correlated Logs source selector into the waterfall controls bar as a compact selector.

### Collapse hint bug (HDX-4444)
- Removed the flaky tooltip-based Alt+click collapse hint (`CollapseTooltipLabel` / `useDisclosure`) that could render multiple times when chevrons were highlighted in quick succession. Collapse is now an always-present chevron control.

### Supporting changes
- `useWaterfallSearchState` now persists the filter language (`whereLanguage`) in the URL so shared links / reloads rebuild the query in the language it was written in.
- `SearchWhereInput` accepts a `parentRef` for popover positioning.

## Testing
- Added `DBTraceWaterfallChart.test.tsx` covering the depth-control logic.
- Updated `DBTracePanel.test.tsx` for the new layout.
 
### Screenshots or video



<img width="1685" height="1011" alt="Screenshot 2026-07-01 at 17 12 42" src="https://github.com/user-attachments/assets/a626f16b-60c0-4864-adc3-900e3ddf2a15" />

### How to test on Vercel preview



**Preview routes:**  `/search`

**Steps:**

1. Open the trace timeline and explore the new features described above.

### References



- Linear Issue: Closes HDX-3945, HDX-3952, HDX-4443, and HDX-4444.
- Related PRs: #1970
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants