Skip to content

fix: loading states unified across the app - #3608

Open
knudtty wants to merge 1 commit into
mainfrom
aaron/fix-loading-states
Open

knudtty wants to merge 1 commit into
mainfrom
aaron/fix-loading-states

Conversation

@knudtty

@knudtty knudtty commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Loading states were difficult to see across the app often and oddly worded. Charts would be loading and display text like "No data found in time range", now changed to "Loading chart data..." with a loading spinner. Also, loading color in the theme is changed to neutral instead of the weird yellow color.

References

  • Linear Issue: Closes HDX-5985

@knudtty
knudtty requested a review from a team as a code owner November 10, 2026 18:50
@vercel

vercel Bot commented Nov 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
hyperdx-oss Ready Ready Preview Nov 10, 2026 6:50pm UTC
hyperdx-storybook Ready Ready Preview Nov 10, 2026 6:50pm UTC

Request Review

@changeset-bot

changeset-bot Bot commented Nov 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dad1437

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

@github-actions github-actions Bot added the review/tier-2 Low risk — AI review + quick human skim label Nov 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔵 Tier 2 — Low Risk

Small, isolated change with no API route or data model modifications.

Why this tier:

  • Standard feature/fix — introduces new logic or modifies core functionality

Review process: AI review + quick human skim (target: 5–15 min). Reviewer validates AI assessment and checks for domain-specific concerns.
SLA: Resolve within 4 business hours.

Stats
  • Production files changed: 9
  • Production lines changed: 113 (+ 47 in test files, excluded from tier calculation)
  • Branch: aaron/fix-loading-states
  • Author: knudtty

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

@github-actions

Copy link
Copy Markdown
Contributor

Duplicate code analysis

3 new clones introduced by this PR.

  • packages/app/src/components/DBHistogramChart.tsx:293-305 ↔ packages/app/src/components/DBNumberChart.tsx:378-390 — 13 lines
  • packages/app/src/components/DBHistogramChart.tsx:293-305 ↔ packages/app/src/components/DBTimeChart.tsx:1084-1096 — 13 lines
  • packages/app/src/components/DBHistogramChart.tsx:297-313 ↔ packages/app/src/components/DBListBarChart.tsx:278-294 — 17 lines

If one of these duplicates a helper that already exists, import it instead
of redefining it. If the duplication is deliberate, ignore this comment —
the check is advisory and never fails the build.


jscpd compared this branch against origin/main
across app, api and common-utils. Only new clones are listed; the codebase
currently holds 111 in total (1.20%).
Run yarn dupes locally to reproduce, or yarn dupes:all for the full picture.

) : graphResults.length === 0 ? (
<div className="d-flex h-100 w-100 align-items-center justify-content-center text-muted">
No data found within time range.
{isLoadingOrPlaceholder && <Loader size="xs" mr="xs" />}

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.

🟠 major — Time chart can get stuck on "Loading Chart Data..." if the previous-period query fails

The empty branch now keys off isLoadingOrPlaceholder (line 610), and that flag includes !data?.isComplete || (config.compareToPreviousPeriod && !previousPeriodData?.isComplete). If compare-to-previous is on, the current period comes back empty, and the previous-period query errors, previousPeriodData stays undefined. The tile then shows a spinner forever, where it used to show "No data found". Base this branch on whether a fetch is actually in flight (isLoading || isPlaceholderData || isPreviousPeriodLoading), or treat a previous-period error as finished. Also add a DBTimeChart test for this branch: none of the PR's tests cover it.

Nothing blocks merge automatically, but a maintainer will expect this fixed if it is a real defect in code this PR changes. If it is about surrounding code, reply and say so instead of patching. Do not widen the PR. How to respond

>
No data found within time range.
<div className="d-flex h-100 w-100 align-items-center justify-content-center text-muted">
{isPlaceholderData && <Loader size="xs" mr="xs" />}

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.

🔵 minor — Same loading/empty markup copied into 8 chart components

The same <div className="d-flex h-100 ... text-muted"><Loader size="xs" mr="xs" />Loading Chart Data...</div> block and the matching isPlaceholderData ternary are now repeated in DBBarChart, DBHistogramChart, DBListBarChart, DBNumberChart, DBPieChart, DBTableChart, DBTimeChart and ServiceDashboardSlowestEventsTile (one copy has already drifted, see that finding). Move it into one shared ChartLoadingState/ChartEmptyState({ isLoading }) component next to ChartErrorState, and have every chart use it.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

<div className="d-flex h-100 w-100 align-items-center justify-content-center text-muted">
{isPlaceholderData && <Loader size="xs" mr="xs" />}
{isPlaceholderData
? 'Loading Chart Data...'

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.

🔵 minor — New loading copy uses Title Case

The new strings 'Loading Chart Data...' break the REQUIRED rule "UI text: use sentence case". The PR description itself writes "Loading chart data...". Use 'Loading chart data...' in all the new branches. Also update the existing initial-loading branches and the test assertions that match on the old text.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

@github-actions

Copy link
Copy Markdown
Contributor

PR Review

If you are a coding agent acting for the author, read this first.
Nothing below blocks merge automatically; a maintainer decides. What they will expect:

  • 🔴 critical or 🟠 major in code this PR adds or changes: fix before asking for review.
  • 🔵 minor in code this PR changes: your call. Fix if small, otherwise reply.
  • Anything about surrounding code, or asking you to widen the change (hoist a helper,
    dedupe with another file, fix other call sites): never fix here, whatever the
    severity. Reply with one sentence; if it is critical, say so plainly so a human sees it.

One commit per review round. After two rounds, stop and ask a maintainer to review
scope rather than addressing more automated findings. Full rule: AGENTS.md.

5 finding(s): 🔴 0 critical · 🟠 1 major · 🔵 4 minor

3 posted as inline comment(s) on the changed lines. 2 listed below.

Findings outside the changed lines

2 minor
  • 🔵 packages/app/src/components/ServiceDashboardSlowestEventsTile.tsx:87 — Slowest-events tile still shows a stale "No data found" during a refetch → This tile uses placeholderData: (prev) => prev, like the DB*Chart components. But its empty branch never reads isPlaceholderData, so an empty previous result still shows "No data found within time range." while the new query runs. The changeset says chart tiles no longer do this. Destructure isPlaceholderData and apply the same loading branch here.
  • 🔵 packages/app/src/components/DBHistogramChart.tsx:380 — Most new refetch tests don't check for the spinner → Only DBBarChart.test checks for .mantine-Loader-root. The Histogram, ListBar, Number, Pie and Table tests only check the text. If the <Loader> were dropped or rendered on the wrong condition, they would still pass, even though the spinner is the point of the PR. Assert on the Loader in these tests too, or render the shared component and test it once. (File: packages/app/src/components/tests/DBHistogramChart.test.tsx and siblings.)

Severity is the reviewer's own estimate and is used for ordering, not filtering. No finding blocks merge automatically; a maintainer decides.

@github-actions

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found. Core change is a safe, well-tested UI loading-state unification; the six categorical/table charts correctly gate on isPlaceholderData and resolve cleanly. The items below are recommended follow-ups, concentrated in DBTimeChart and one partially-updated tile.

🟡 P2 — recommended

  • packages/app/src/components/DBTimeChart.tsx:1107 — The empty-state branch gates the spinner on isLoadingOrPlaceholder, which includes !data?.isComplete; when data is undefined (disabled/idle query with no cached rows) this is permanently true, so the tile shows a never-resolving "Loading Chart Data…" spinner where it previously rendered "No data found within time range."
    • Fix: Require an in-flight fetch for the spinner, e.g. gate on data != null && isLoadingOrPlaceholder (or isFetching || isPlaceholderData) so an undefined-data idle query falls through to the empty message.
    • ce-correctness-reviewer, ce-julik-frontend-races-reviewer
  • packages/app/src/components/ServiceDashboardSlowestEventsTile.tsx:87 — Only the initial-load branch gained a spinner; the data?.data.length === 0 branch still shows a static "No data found within time range." and the component never reads isPlaceholderData, so this tile keeps the stale-empty behavior the PR set out to unify away.
    • Fix: Destructure isPlaceholderData and apply the same spinner-plus-conditional-text pattern used by the other charts to its empty branch.
    • ce-maintainability-reviewer, ce-kieran-typescript-reviewer
  • packages/app/src/components/DBTimeChart.tsx:1105 — The new empty/loading branch is untested; DBTimeChart.test.tsx references neither loading string and exercises only the success path, while the six sibling charts each received a refetch test.
    • Fix: Add tests covering the empty result both while loading (asserts the spinner and "Loading Chart Data…") and when settled (asserts "No data found within time range.").
    • ce-testing-reviewer, ce-correctness-reviewer, ce-julik-frontend-races-reviewer
🔵 P3 nitpicks (6)
  • packages/app/src/components/DBBarChart.tsx:98 — The ~6-line empty/loading JSX block (plus the initial-loading block) is duplicated verbatim across seven chart components, and the duplication has already drifted (DBTimeChart uses a different gating variable; the slowest-events tile was only partially updated).
    • Fix: Extract a shared ChartEmptyState/ChartLoadingState component taking an explicit loading-boolean prop and replace the duplicated blocks.
  • packages/app/src/components/DBTimeChart.tsx:1107 — DBTimeChart gates its empty-state loading text on isLoadingOrPlaceholder while the other six charts use isPlaceholderData, and nothing documents why, inviting a future edit to "normalize" them incorrectly.
    • Fix: Add a short comment at the gating site explaining the chunked/compare-period completeness requirement.
  • packages/app/src/components/__tests__/DBHistogramChart.test.tsx:205 — Five of the six renamed refetch tests assert only the loading text and the absence of the empty text, never that the Loader the PR adds actually renders, so a regression dropping the spinner while keeping the text would pass.
    • Fix: Add a spinner-presence assertion to each, matching the pattern already used in DBBarChart.test.tsx.
  • packages/app/src/components/__tests__/DBBarChart.test.tsx:128 — The sole spinner assertion queries the Mantine-internal .mantine-Loader-root class, which can change on a library upgrade and silently break the test without a behavior change.
    • Fix: Assert via a stable data-testid or an accessibility attribute on the loading container instead of the internal class.
  • packages/app/src/theme/mantineTheme.ts:91 — The Loader default-color override applies to every loader in the app, not just chart tiles, so any loader that relied on the prior brand color changes to neutral gray with no test pinning the new default.
    • Fix: Confirm the app-wide neutral default is intended (it matches the stated goal) and add a regression check, or scope the color to chart loaders if not.
  • packages/app/src/components/DBBarChart.tsx:102 — The loading copy "Loading Chart Data…" is Title Case, which the repo's agent_docs/code_style.md sentence-case rule disallows; the same string already appears in adjacent pre-existing loading branches, so any fix should update all co-located instances together.
    • Fix: Normalize the co-located strings to sentence case ("Loading chart data…") if the style rule is enforced for this surface.

Reviewers (9): ce-correctness-reviewer, ce-testing-reviewer, ce-maintainability-reviewer, ce-project-standards-reviewer, ce-kieran-typescript-reviewer, ce-julik-frontend-races-reviewer, ce-agent-native-reviewer, ce-learnings-researcher, ce-previous-comments-reviewer.

Testing gaps:

  • ServiceDashboardSlowestEventsTile.tsx has no test file; its newly added loading spinner is uncovered.
  • No test pins the neutral Loader color default added in mantineTheme.ts.
  • No test distinguishes a key-change refetch (isPlaceholderData true, spinner) from a same-key background refetch (spinner suppressed) for the six categorical/table charts.

No finding blocks merge automatically. A maintainer will expect P0/P1 findings in code this PR changes to be fixed; P2/P3 are your call -- fix or reply. Never fix findings about surrounding code here; reply instead. Do not widen the PR. How to respond

@github-actions

Copy link
Copy Markdown
Contributor

E2E Test Results

❌ 1 test failed • 463 passed • 1 skipped • 1505s

Status Count
✅ Passed 463
❌ Failed 1
⚠️ Flaky 2
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

This branch was successfully deployed

2 active deployments
Preview – hyperdx-oss — dad14373 Deployed Nov 10, 2026 by vercel[bot]
Preview – hyperdx-storybook — dad14373 Deployed Nov 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/tier-2 Low risk — AI review + quick human skim

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant