Skip to content

feat: single drawer navigation#2541

Open
karl-power wants to merge 5 commits into
mainfrom
hdx-3942-single-drawer-navigation
Open

feat: single drawer navigation#2541
karl-power wants to merge 5 commits into
mainfrom
hdx-3942-single-drawer-navigation

Conversation

@karl-power

@karl-power karl-power commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the old stacked/layered side-panel drawers with a single right-hand drawer where all event navigation happens in-place. Everything now renders inside one shell instead of opening a second drawer on top of another.

  • Single-drawer navigation. DBRowSidePanel is refactored into DBRowSidePanelInner, which owns two URL-persisted stacks: sidePanelNavStack for same-source drilldowns (e.g. Surrounding Context, replaced in-place) and sidePanelSourceStack for cross-source navigation (log → trace via the new View Trace action, or session → event). Each frame stores its source id, row id, label, and originating tab. State lives in the URL (sidePanelTab, sidePanelSourceStack, sidePanelNavStack, sessionPanelEvent), so the trail survives reload and shared links.
  • Unified breadcrumbs (SidePanelBreadcrumbs). New shared component used across log, trace, and session panels: source-kind icons (IconLogs/IconConnection/IconDeviceLaptop), label truncation with tooltips, and a clickable trail to jump back to any ancestor.
  • Session panel alignment. SessionSidePanel embeds DBRowSidePanelInner for the selected event instead of portaling a second DBRowSidePanel, so session → event → trace shares the same structure and breadcrumbs. SessionSubpanel/DBSessionPanel report selections via onEventNavigate.
  • Header redesign. Metadata row (timestamp, service, duration, status, span kind), Copy Trace ID, and View Trace for logs with trace context. View Trace / Trace ID are hidden when there's no trace context. Shared SidePanelHeaderActions for shortcuts/full-width/share/close; DBRowSidePanelHeader slimmed to the body section.
  • Navigation fixes. Session Close (X) closes the whole panel; back/Esc pops one level. Returning from a trace restores the prior tab; opening a different event resets the tab and clears stale stacks; pushes jump to the destination's default tab.
  • Background scroll. Removes the side panel overlay on search and sessions pages so that the background lists are scrollable and the user can click to select an alternative log/trace/session.

Screenshots or video

output.mp4

How to test on Vercel preview

Preview routes: /search, /sessions

Navigate around the side panel drawer on both the search and sessions pages.

References

@changeset-bot

changeset-bot Bot commented Jun 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1b7ac98

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 Minor
@hyperdx/api Minor
@hyperdx/otel-collector Minor

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 Jun 29, 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 3, 2026 2:49pm
hyperdx-storybook Ready Ready Preview, Comment Jul 3, 2026 2:49pm

Request Review

@github-actions github-actions Bot added the review/tier-4 Critical — deep review + domain expert sign-off label Jun 29, 2026
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🔴 Tier 4 — Critical

Touches auth, data models, config, tasks, OTel pipeline, ClickHouse, or CI/CD.

Why this tier:

  • Large diff: 1726 production lines changed (threshold: 1000)

Review process: Deep review from a domain expert. Synchronous walkthrough may be required.
SLA: Schedule synchronous review within 2 business days.

Stats
  • Production files changed: 18
  • Production lines changed: 1726 (+ 437 in test files, excluded from tier calculation)
  • Branch: hdx-3942-single-drawer-navigation
  • Author: karl-power

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

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 223 passed • 3 skipped • 1552s

Status Count
✅ Passed 223
❌ Failed 0
⚠️ Flaky 4
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

@greptile-apps

greptile-apps Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces layered event drawers with one right-side drawer. The main changes are:

  • URL-backed navigation stacks for row drilldowns and cross-source navigation.
  • Shared breadcrumbs across log, trace, and session views.
  • Session event navigation rendered inside the session drawer.
  • Updated row header metadata, trace actions, sharing, and close behavior.
  • Scrollable background pages while the drawer is open.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
packages/app/src/components/DBRowSidePanel.tsx Adds the single-drawer row shell, URL-backed navigation stacks, breadcrumbs, and log-to-trace navigation.
packages/app/src/SessionSidePanel.tsx Embeds selected session events in the same drawer and clears stale session navigation state.
packages/app/src/SessionSubpanel.tsx Routes session event clicks through the parent drawer instead of opening a nested portal drawer.

Reviews (8): Last reviewed commit: "address stale stack feedback" | Re-trigger Greptile

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

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Deep Review

Single-drawer navigation refactor (DBRowSidePanelDBRowSidePanelInner with two URL-persisted stacks, SidePanelBreadcrumbs, session-panel embedding, header redesign, overlay removal). ~1,500 lines across 25 files, client-side TS/React. 11 reviewers ran; findings below were re-verified against the checked-out code before grading.

🔴 P0/P1 -- must fix

  • packages/app/src/components/DBRowSidePanel.tsx:288 -- A source-stack frame whose sourceId no longer resolves pins the drawer on a permanent Loading... with no visible Back/Close, because useSource's react-query select returns undefined for a missing id even after the query settles, so isResolvingSource never clears; this is reachable through the PR's flagship shareable-URL flow (deleted/renamed/cross-workspace source id).
    • Fix: Derive resolution state from the query's settled/isSuccess flags and, when the id resolves to nothing, evict that frame or render an explicit error state with a working close control instead of the indefinite loading branch.
    • reliability, adversarial, kieran-typescript, correctness

🟡 P2 -- recommended

  • packages/app/src/components/DBRowSidePanel.tsx:689 -- A shared link carrying a syntactically-valid-but-wrong-shape stack param ({}, a string, a number) survives parseAsJsonEncoded (no shape check) and withDefault (substitutes only on null), then throws forEach is not a function inside the allBreadcrumbs memo during render, dropping the panel into the error boundary on every reload of that URL.

    • Fix: Validate the decoded value is an array of well-formed frames (coerce non-arrays to the empty default, or add an Array.isArray/field guard in the parser) before consuming sourceStack/navStack.
    • reliability, adversarial, correctness, kieran-typescript
  • packages/app/src/components/DBRowSidePanel.tsx:481 -- sidePanelTab is a global URL param that is not cleared on close and is only reset via the stale-stack effect (gated on non-empty stacks), so a persisted tab (e.g. trace) reused against a source of a different kind renders no matching tab item and a blank body until the user manually clicks a tab.

    • Fix: Reset sidePanelTab whenever the mounted initialRowId changes (not only when stacks are stale) and clear it alongside the stacks on panel close.
    • julik-frontend-races, correctness
  • packages/app/src/components/DBRowSidePanel.tsx:293 -- The View Trace push builds a SourceFrame without aliasWith, so baseAliasWith falls back to the originating log source's initialAliasWith, attaching the log source's row-key CTEs to the trace-source query whose rowId is a self-contained TraceId=… AND SpanId=….

    • Fix: Give the pushed cross-source frame its own aliasWith (empty for View Trace) and use activeSourceFrame.aliasWith ?? [] instead of falling back to the root's.
    • correctness
  • packages/app/src/SessionSidePanel.tsx:155 -- Because the embedded inner panel's Esc handler is disabled when onNavigateToParent is set, SessionSidePanel owns Esc and always jumps straight back to the session root, discarding an entire in-place nav/source trail in one keystroke, diverging from the Back button that pops a single level.

    • Fix: Delegate Esc to the embedded panel's one-level handlePanelBack while a nav/source stack is active, and only reset to the session root at the panel's own root.
    • correctness, julik-frontend-races
  • packages/app/src/components/SidePanelBreadcrumbs.tsx:46 -- The core new navigation surface has no unit coverage: the 138-line SidePanelBreadcrumbs (truncation thresholds, tooltip gating, clickable-vs-static rendering) is stubbed to null in the one new test, and the stack handlers (handleSourceStackPush/handlePanelBack/handleBreadcrumbNavigation with originTab restoration) and the View Trace entry point are exercised by no test.

    • Fix: Add unit tests that drive push/pop/breadcrumb-jump with real setters and assert tab restoration, plus a SidePanelBreadcrumbs rendering test for truncation and click behavior.
    • testing, correctness, maintainability
  • packages/app/src/components/DBRowSidePanel.tsx:1 -- The file grows +558 lines to ~1,209, roughly 4× the project's documented 300-line component limit (AGENTS.md / agent_docs/code_style.md), instead of extracting the new navigation-stack logic the way SidePanelBreadcrumbs was split out.

    • Fix: Extract the stack state, push/pop/breadcrumb handlers, and tab-restoration effects into a dedicated hook/module.
    • project-standards, maintainability
🔵 P3 nitpicks (6)
  • packages/app/src/components/DBRowSidePanel.tsx:237 -- persistStacksInUrl? is declared on DBRowSidePanelInnerProps but never destructured, read, or passed by any caller; the implied opt-out of URL persistence does not exist.
    • Fix: Remove the dead prop, or wire it to actually switch stack state between URL and local storage.
    • correctness, maintainability
  • packages/app/src/SessionSidePanel.tsx:98 -- The shared sidePanel* query-param keys and their four setNull clears are duplicated as bare string literals across SessionSidePanel.clearInnerNavigation, DBRowSidePanelErrorBoundary, and DBRowSidePanelInner, so a future key rename silently breaks only some call sites.
    • Fix: Export the key names as shared constants (or a useClearSidePanelNavigation helper) imported by all three sites.
    • kieran-typescript, maintainability
  • packages/app/src/components/DBRowSidePanel.tsx:508 -- initialMainContent (the breadcrumb root label) is captured once with an == null guard and never reset when initialRowId changes on a reused panel, so after switching rows without a remount the root crumb can show the previous row's body.
    • Fix: Reset initialMainContent when initialRowId changes.
    • correctness, julik-frontend-races
  • packages/app/src/components/DBRowSidePanel.tsx:378 -- Repeated drilldowns append to navStack/sourceStack with no depth cap or dedup, growing the JSON-encoded URL param unbounded until it can exceed browser/proxy URL-length limits and truncate the shared link.
    • Fix: Cap stack depth (elide oldest frames) and/or store only minimal frame identity in the URL.
    • adversarial, reliability
  • packages/app/src/components/DBRowSidePanel.tsx:374 -- A rapid double-click of View Trace (or Surrounding Context) runs the functional updater twice before the trigger is hidden, pushing duplicate identical frames that require two Back presses to unwind.
    • Fix: Skip a push whose leaf frame equals the current leaf (same sourceId+rowId), or disable the trigger while a push is in flight.
    • adversarial
  • packages/app/src/components/DBRowSidePanel.tsx:191 -- The nav stack and source stack are handled by four near-mirror code paths (push handlers, the two handlePanelBack branches, the two breadcrumb loops, the push-vs-pop tab effect), so a behavior change must be applied in two places or drift.
    • Fix: Unify NavEntry/SourceFrame into a generic frame and factor the shared push/pop/restore logic into one helper.
    • maintainability

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

Testing gaps:

  • No test feeds malformed/non-array or missing-field values into sidePanelSourceStack/sidePanelNavStack/sessionPanelEvent to confirm the decode boundary degrades instead of throwing.
  • No test covers a source-stack frame whose sourceId fails to resolve (the permanent-loading path).
  • No test exercises the real DBSqlRowTableWithSidebar background row-to-row switch, nor the overlay-removed background-click-to-select interaction (the deleted clickTopmostDrawerOverlay page-object helper was not replaced with click-through coverage).
  • SessionSidePanel tests mock SessionSubpanel and every non-sessionPanelEvent query key, so clearInnerNavigation is never asserted to clear the specific sidePanel* params it targets.

Comment thread packages/app/src/components/DBRowSidePanel.tsx Outdated
Comment thread packages/app/src/components/DBRowSidePanel.tsx Outdated
@karl-power karl-power force-pushed the hdx-3942-single-drawer-navigation branch from a5f3005 to ef2a570 Compare July 3, 2026 14:23
Comment thread packages/app/src/SessionSidePanel.tsx Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/tier-4 Critical — deep review + domain expert sign-off

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant