feat(app): make View Trace action more prominent with a one-time nudge - #2815
feat(app): make View Trace action more prominent with a one-time nudge#2815elizabetdev wants to merge 11 commits into
Conversation
The log side panel's "View Trace" action was easy to miss: it rendered as subtle inline text blending into the dimmed metadata row. Make it an outlined (secondary) button using the trace source icon, larger and right-aligned so it reads as an action. Add a one-time dismissible popover pointing at the button the first time a user opens a log that has a trace. It persists dismissal under its own localStorage key (hdx-view-trace-callout-dismissed), stays pinned until acknowledged (Got it or clicking View Trace), and only transiently hides on Escape so a stray click or drawer resize can't burn the message before it is read. Also add a data-source-icons skill documenting the canonical icon per source kind so trace/log/session/metric icons stay consistent. Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: 0a6d4bc The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🔵 Tier 2 — Low RiskSmall, isolated change with no API route or data model modifications. Why this tier:
Review process: AI review + quick human skim (target: 5–15 min). Reviewer validates AI assessment and checks for domain-specific concerns. Stats
|
Greptile SummaryThe PR makes the log side panel’s View Trace action more prominent and adds a persistent, one-time explanatory callout.
Confidence Score: 4/5The PR should not merge until Escape no longer navigates away from or closes the side panel while the callout is open. The transient row-state leak has been removed, but the callout still opts out of Escape handling while the unchanged panel hotkey consumes Escape to pop navigation or close the panel, leaving the previously reported interaction defect outstanding. Files Needing Attention: packages/app/src/components/ViewTraceCalloutButton.tsx, packages/app/src/components/DBRowSidePanel.tsx
|
| Filename | Overview |
|---|---|
| packages/app/src/components/ViewTraceCalloutButton.tsx | Introduces the prominent trace button and persistent callout, but the previously reported Escape interaction remains reachable. |
| packages/app/src/components/DBRowSidePanel.tsx | Replaces the inline trace action with the extracted component while retaining the panel-level Escape navigation handler. |
| packages/app/src/components/tests/ViewTraceCalloutButton.test.tsx | Covers visibility gates and both explicit persistent-dismissal paths. |
| packages/app/tests/e2e/utils/base-test.ts | Seeds the shared dismissal key for E2E tests using a serializable init-script argument. |
Reviews (10): Last reviewed commit: "fix(app): avoid unsafe type assertion in..." | Re-trigger Greptile
E2E Test Results✅ All tests passed • 275 passed • 1 skipped • 1068s
Tests ran across 4 shards in parallel. |
Deep Review🔴 P0/P1 — must fix
🟡 P2 — recommended
🔵 P3 nitpicks (6)
Reviewers (8): correctness, adversarial, testing, maintainability, kieran-typescript, julik-frontend-races, project-standards, agent-native. Testing gaps:
Coverage note: |
Address review feedback on #2815: - P0/P1: the callout's closeOnEscape collided with the pre-existing document-level useHotkeys(['esc'], handlePanelBack), so Escape closed or rewound the drawer instead of acting cleanly on the callout. The callout no longer intercepts Escape (closeOnEscape={false}); Escape keeps its normal panel behavior. - P1: the transient viewTraceCalloutClosed state was never reset across row changes in the same (unkeyed) panel instance, so one Escape suppressed the callout for every later eligible log. Removed the transient close entirely. The callout is now dismissed only by explicit acknowledgement (Got it, or clicking View Trace), which persists under its own localStorage key; otherwise it stays pinned and simply reappears the next time the panel opens on an eligible log, which naturally resets per open. Co-authored-by: Cursor <cursoragent@cursor.com>
Address P2/P3 review feedback on #2815: - Extract the ~70-line inline callout popover out of the 1300-line DBRowSidePanel into a dedicated ViewTraceCalloutButton component, keeping the panel focused and the nudge independently testable (AGENTS.md file-size guidance). - Share the localStorage key via a dependency-free VIEW_TRACE_CALLOUT_DISMISSED_KEY constant, imported by both the component and the Playwright fixture, so the two copies can no longer drift and silently un-seed the E2E suppression. - Add role="status" / aria-live="polite" to the callout so screen readers announce it when it auto-opens. - Add a unit test suite covering the opened gate (enabled vs disabled), the already-dismissed case, and both persist paths (Got it vs View Trace). Co-authored-by: Cursor <cursoragent@cursor.com>
The callout shows to anyone without the dismissal flag, including brand-new users, fresh profiles, and incognito sessions who never saw a prior placement. "easier to find now" framed it as a change announcement for those users, so switch to neutral copy that just describes what the button does. Co-authored-by: Cursor <cursoragent@cursor.com>
Reflect the acknowledgement-only dismissal and that Escape keeps its normal side-panel behavior, replacing the earlier transient-dismiss wording. Co-authored-by: Cursor <cursoragent@cursor.com>
|
<!-- deep-review --> Deep Review🔴 P0/P1 — must fix
🟡 P2 — recommended
🔵 P3 nitpicks (6)
Reviewers (7): correctness, adversarial, testing, frontend-races, maintainability, kieran-typescript, project-standards. Testing gaps:
|
Explain why the one-time callout keeps closeOnClickOutside={false}: it's a
deliberate design choice so a stray click can't dismiss the hint before it is
read. Dismissal is via "Got it" or clicking View Trace, and it never reappears
once acknowledged.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Re: the P0/P1 on This is intentional and kept by design. The callout is a one-time, one-line hint that only ever covers a small strip directly below the button, and it's dismissed deliberately via Got it or by clicking View Trace — after which it never reappears. We specifically do not want an outside click to dismiss it, because a stray click shouldn't burn the message before the user has read it. We accept that the covered controls are briefly non-interactive until the user acknowledges the hint (a single click). I've documented this decision inline at the |
|
<!-- deep-review --> Deep Review✅ No critical issues found. Nothing in this diff breaks the happy path — the button renders, navigation is unchanged, and the dismissal persists on the normal click. The findings below are all about the popover: an auto-opening, portalled overlay that deliberately cannot be dismissed by outside click or Escape, whose only exit is a small "Got it" button. 🟡 P2 -- recommended
🔵 P3 nitpicks (8)
Reviewers (6): correctness, testing, maintainability, project-standards, julik-frontend-races, adversarial. Testing gaps:
Coverage note: this run had no shell, |
Deep Review✅ No critical issues found. Nothing in this diff causes data loss, an auth bypass, injection, or a guaranteed crash, and the localStorage key, the 🟡 P2 -- recommended
🔵 P3 nitpicks (5)
Reviewers (4): ce-correctness-reviewer, ce-testing-reviewer, ce-maintainability-reviewer, ce-julik-frontend-races-reviewer. Coverage caveat: this sandbox had no working shell and no Testing gaps:
|
…ker config Replace the `as string` assertion with String() when seeding the View Trace callout dismissal key, removing the @typescript-eslint/no-unsafe-type-assertion warning that pushed the app over its ESLint --max-warnings cap. Also teach knip about the common-utils Stryker setup (config-only `@stryker-mutator/core` dependency and `stryker` binary) so the pre-commit hook and Knip CI stop reporting them as unused/unlisted. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
Users reported that the log side panel's View Trace action is easy to miss. Since traces moved out of a dedicated tab, the action rendered as low-emphasis inline text (
variant="subtle",compact-xs) blending into the dimmed metadata row (timestamp, service, Trace ID).This PR makes it noticeable and adds a gentle one-time nudge:
ViewTraceCalloutButton): outlined (secondary) button, larger (compact-sm), right-aligned (ml="auto"), using the trace source icon (IconConnection, matchingSOURCE_KIND_ICONS) plus a trailing arrow. Samedata-testid/ navigation behavior, so E2E helpers are unchanged.VIEW_TRACE_CALLOUT_DISMISSED_KEY), deliberately not inhdx-user-preferences(that's for real user settings).role="status"/aria-live="polite"so screen readers announce it when it auto-opens.data-source-iconsskill: documents the canonical Tabler icon perSourceKind(log/trace/session/metric/promql) to keep icon usage consistent going forward.base-test.tsseeds the dismissed flag (via the shared key) so the auto-popover never interferes with side-panel specs.Why
The subtle inline treatment didn't read as an action to users coming from the old Trace tab or the previous "View trace" link. Emphasizing the button (without reintroducing tab/push navigation inconsistency) plus a self-retiring, placement-neutral nudge addresses discoverability with minimal behavioral change.
Screenshot
Review feedback addressed
closeOnEscape, so it can't collide with the pre-existinguseHotkeys(['esc'], handlePanelBack); Escape behaves exactly as before.viewTraceCalloutClosedstate entirely, so a dismissal can no longer persist across row changes in the same panel instance.ViewTraceCalloutButtoncomponent (keepsDBRowSidePanelfocused, makes the nudge testable); added a unit test suite (opened gate, already-dismissed, and both persist paths); shared the localStorage key via a dependency-free constant imported by both the component and the E2E fixture; addedrole/aria-livefor screen readers; and used placement-neutral copy so brand-new users don't get a change announcement for a placement they never saw.Test plan
yarn workspace @hyperdx/app run ci:unit—DBRowSidePanel.*+ newViewTraceCalloutButtonsuites passyarn lint:fixclean; pre-commit hooks (prettier/eslint/knip) passhdx-view-trace-callout-dismissed: true); Escape performs normal panel back/close; resizing the drawer does not dismiss