Skip to content

fix(sidebar): stop counts flickering to 0 and layout collapsing during nav transitions#576

Open
eliran-ops wants to merge 1 commit intomainfrom
feat/fix-sidebar-counts-flicker-to-zero-on-initial-load
Open

fix(sidebar): stop counts flickering to 0 and layout collapsing during nav transitions#576
eliran-ops wants to merge 1 commit intomainfrom
feat/fix-sidebar-counts-flicker-to-zero-on-initial-load

Conversation

@eliran-ops
Copy link
Copy Markdown
Contributor

@eliran-ops eliran-ops commented Apr 29, 2026

Summary

SKY-824 — two related Resources sidebar regressions:

  • 39 — Sidebar resource counts go to "0" for every kind during initial load instead of preserving last-known values or hiding counts. Looks like ~2s of "data loss".
  • 65 — The hierarchical sidebar (Workloads / Networking / …) briefly collapses to the flat fallback (Pods / Deployments / etc., all counts 0) during nav transitions, causing UI flicker.

Root cause

  • The count map coerced every missing key to 0 (`resourceCounts[key] ?? 0`), so during the window between "nothing fetched yet" and "counts payload arrived" every kind rendered a confident "0" badge.
  • During route transitions the parent briefly re-renders with `apiResources={undefined}` for one frame; the sidebar then drops to its flat `CORE_RESOURCE_TYPES` fallback for that frame and snaps back to hierarchical the next.

Fix

  1. New shared hook `useLastDefined(value)` in `packages/k8s-ui/src/hooks/`. Latches the last non-undefined value via a ref so it doesn't trigger re-renders, and never replaces a defined value with stale data. 5 unit tests pin the contract.
  2. `ResourcesSidebar` wraps both `apiResources` and `resourceCounts` in `useLastDefined` so a one-frame undefined drop doesn't collapse the layout or blank the badges.
  3. Distinguish loading from zero in counts:
    • Internal `counts: Record<string, number | null>`. `null` = not loaded; `0` = API confirmed zero.
    • Badge UI renders an en-dash (`–`) for null with `text-theme-text-disabled` so loading badges are visually subordinate.
    • Category "show this kind" filter treats null as "show, might have data" rather than hiding it.
    • Category totals coerce nulls to 0 for the collapsed-summary badge — honest because the total is over a known finite set of kinds.

Out of scope

The `Checks` page mentioned in bug 40 lives in `radar-hub-web`, not radar OSS.

Test plan

  • `cd packages/k8s-ui && npm test` — all passing (5 new for `useLastDefined`)
  • `cd web && npm run tsc` — clean
  • `cd web && npm run build` — clean

Made with Cursor


Note

Low Risk
UI-only behavior change plus a small shared hook; main risk is subtle regressions in sidebar visibility/count rendering during loading states.

Overview
Prevents ResourcesSidebar from flickering during navigation/loading by latching the last non-undefined apiResources and resourceCounts values via a new useLastDefined hook.

Updates count handling to distinguish loading vs zero (number | null), rendering a placeholder dash and keeping kinds visible while counts are unknown, instead of briefly showing 0 everywhere. Adds unit tests for useLastDefined and exports it from the hooks index.

Reviewed by Cursor Bugbot for commit c0ac5cb. Bugbot is set up for automated code reviews on this repo. Configure here.

…g nav transitions

SKY-824 covers two related Resources sidebar regressions:

39. Sidebar resource counts go to "0" for every kind during initial
    load instead of preserving last-known values or hiding counts.
    Looks like ~2 seconds of "data loss".
65. The hierarchical sidebar (categories like Workloads / Networking)
    briefly collapses to the flat fallback (Pods / Deployments /
    etc. with all counts 0) during nav transitions, causing UI
    flicker.

Root cause:

- The count map coerced *every* missing key to 0 (`resourceCounts[key]
  ?? 0`), so during the window between "nothing fetched yet" and
  "counts payload arrived" every kind rendered a confident "0" badge.
- During route transitions the parent briefly re-renders with
  `apiResources={undefined}` for one frame; the sidebar then drops to
  the flat `CORE_RESOURCE_TYPES` fallback for that frame, then snaps
  back to hierarchical — perceived as a layout flicker.

Fix:

1. New shared hook `useLastDefined<T>(value)` in
   `packages/k8s-ui/src/hooks/`. Latches the last non-undefined value
   without re-rendering on its own; never replaces a defined value
   with stale data. 5 unit tests pin the contract.

2. `ResourcesSidebar` wraps both `apiResources` and `resourceCounts`
   in `useLastDefined`, so a one-frame undefined drop doesn't
   collapse the layout / blank the badges.

3. Distinguish "loading" from "confirmed zero" in counts:
   - Internal `counts: Record<string, number | null>`. `null` means
     the count payload hasn't arrived for this key yet; `0` means the
     API confirmed zero.
   - The badge UI renders an en-dash (`–`) for `null`, with a
     `text-theme-text-disabled` class so loading badges are visually
     subordinate to live ones.
   - Category "show this kind" filter treats null as "show, might
     have data" rather than "0, hide".
   - Category totals coerce nulls to 0 for the collapsed-summary
     badge so we still show *some* number; this is honest because the
     total is over a known finite set of kinds.

The `Checks` page mentioned in bug 40 lives in radar-hub-web, not
radar OSS, so it is out of scope for this PR.

Linear: SKY-824
Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants