Slice 1/5: feat(calm-hub-ui): loading states and link-based navigation - #3061
Conversation
Extracted from #3001 (OIDC + SCM backend support), which bundled these unrelated UI changes with the GitHub storage backend work. - Explore rail and mobile nav show loading state while counts resolve - Namespace/type in the section header render as links - Sparkline overflow fix (#2728) Original-PR: #3001
…arkline changes Addresses the 4 findings posted on this slice's PR review: - Split ExploreRail/MobileNavMenu's combined loading flag into namespacesLoading/domainsLoading so a faster-resolving section no longer waits on the slower one, and the static root-level rows in MobileNavMenu render immediately instead of hiding behind a spinner tied to counts that don't affect them - Anchor the first/last sparkline labels to their column instead of centering them, so they can no longer reach DiagramSection's overflow-hidden card boundary (#2728) — the previous fix removed the track's own overflow but the actual clip was from an ancestor - Removed a dead ternary in ExploreRail.test.tsx that always took its else branch (screen.getAllByClassName isn't a testing-library API); spinners now carry role=status/aria-label, closing a real accessibility gap along the way Also, while in these files: added a muted empty state to both ExploreRail sections (MobileNavMenu already had one), renamed a local loading flag that collided in name with the new counts-loading props, and encodeURIComponent'd a query param for consistency with its neighbour in SectionHeader. Claude-Session: https://claude.ai/code/session_0199XmacMNTrxWL4x5CSXyp1
|
One item noticed while fixing the loading-state findings, not addressed here: |
…ine slice Addresses the 3 findings from a second review pass on this slice's PR: - ExploreRail/MobileNavMenu now render a distinct 'could not load' message when a counts fetch fails, instead of the same 'Nothing here' text used for a genuinely empty list. Threads Hub's existing namespaceCountsFailed through both surfaces, and adds the equivalent domainCountsFailed (which didn't exist before) for symmetry. - Sparkline's #2728 label-clipping fix only anchored the literal first and last dot; with enough versions in a narrow track, an interior dot close to the edge (e.g. the 2nd of many) could still overflow. Replaced the per-index special case with a CSS clamp() applied to every dot, so no label can leave the track regardless of dot count or track width — the true first/last still keep their edge-hugging text alignment, since they always hit the clamp's bound. - Added link role/href coverage for SectionHeader's namespace/type links (including a case with reserved URL characters), which had gained real navigation behaviour with no assertions on it. Claude-Session: https://claude.ai/code/session_0199XmacMNTrxWL4x5CSXyp1
…ap and spinner duplication - domainControlCount/controlDomainCount in Hub.tsx only gated on domainCountsLoaded, not the domainCountsFailed added in the previous round, so a failed domain-counts fetch rendered a confirmed '0 controls' instead of unknown — inconsistent with the comment right above claiming it mirrors activeNamespaceCounts, which does check the failed flag. Both now gate on both flags; added a Hub-level test simulating the failure via a spy on the mocked CountsService. - Extracted the identical spinner span duplicated across ExploreRail and MobileNavMenu (both touched by this same PR) into a shared LoadingSpinner, so a future style change can't drift between them. Each component keeps its own wrapper/spacing, which differ by context (rail div vs mobile list item). Claude-Session: https://claude.ai/code/session_0199XmacMNTrxWL4x5CSXyp1
|
One more finding from a third review pass (couldn't anchor inline — the diff position isn't resolvable across the intermediate commits):
|
…mbiguous filter-empty state - MobileNavMenu's failure message said 'try again' but there's no retry path (Hub fetches counts once on mount) — replaced with level-specific wording matching ExploreRail's equivalent copy. - ExploreRail's 'Nothing here' fired identically whether the filter matched nothing or there were genuinely no namespaces; the two are now distinguished. Not addressed, noted for the record: - The same unknown-vs-zero distinction doesn't reach MobileNavMenu's leaf-level fetches (resource types, controls) — those catch blocks predate this PR and are unrelated to the counts-loading fix this slice makes; flagged as a separate follow-up on the PR. - Sparkline's label clamp() inverts if the track ever drops under 120px; not reachable at any current desktop or mobile viewport in this codebase per review verification, so left as a known theoretical edge rather than adding a speculative width guard. Claude-Session: https://claude.ai/code/session_0199XmacMNTrxWL4x5CSXyp1
|
Two more items from a fourth review pass:
|
…rflow and generic spinner label - Removing the sparkline track's overflow:hidden (for the #2728 clamp() fix) also dropped vertical containment on the validFrom date row, which had no whiteSpace/overflow bound of its own — an unusually long value could now bleed past the track instead of being clipped. Gave it the same single-line ellipsis treatment the label row already has. - MobileNavMenu's drill-down spinner used a bare 'Loading' label regardless of which section was loading, unlike ExploreRail's section-specific labels on the desktop equivalent. Now says 'Loading namespaces'/'Loading control domains' to match. Declined as architecture/DRY preferences rather than defects (noted on the PR): generalizing the namespace/domain loading-failed state into a shared async-resource abstraction, and deduplicating the loading/failed/empty branch between ExploreRail and MobileNavMenu. Both are legitimate refactors but out of scope for a review-fix pass — the current duplication is small (a few lines) and each component's render differs enough (div vs li, different spacing) that a shared abstraction would add its own indirection cost. Claude-Session: https://claude.ai/code/session_0199XmacMNTrxWL4x5CSXyp1
|
A fifth review pass found two more real, fixed in 1dba077:
Five findings from this same pass were architecture/DRY preferences, not defects — declining rather than churning the diff further:
All are legitimate refactors, but they generalize working code rather than fix something broken, and each component's actual render differs enough (div vs li wrapper, different spacing, different sections) that a shared abstraction adds its own indirection cost. Open to revisiting if a maintainer wants them as a separate follow-up. |
rocketstack-matt
left a comment
There was a problem hiding this comment.
Reviewed independently (own critical + idiom pass, full local build/lint/test run, and a live visual check of the #2728 sparkline fix against a seeded long-name timeline). No defects found beyond what your own five self-review rounds already caught and fixed — all 10 threads check out against the code. Nit: the PR description's Type of Change / Testing checkboxes are stale (still say "refactoring, no functional changes" / tests n/a) relative to what actually shipped across the review rounds — worth a quick update before merge, non-blocking.
Description
maindirectly — it has no dependency on the other slicesType of Change
Affected Components
calm-hub-ui/)Testing
Checklist
Split from #3001.