Story-3051 · pramaan-app · 28 Jul 2026
A spinner, not a sentence
The matter-team drawer answered “is the data here yet?” with a flat line of
text — “Loading matter team…” pinned to the top-left of the
panel — while every route in the app answers the same question with one calm, centred
spinner (RouteLoading). This Story retires the odd-one-out text loaders on the
Team roster, the Access-history timeline, and the add-teammate view, so the drawer’s
waiting state finally matches the rest of PRAMAAN.
The one idea
RouteLoading already centres a spinner and fills its parent. Its only
habit that is wrong inside a drawer is the min-h-[60vh] floor it carries for
full-page route fallbacks — on a shorter drawer body (a laptop height, or the
add-teammate view whose footer eats space) that floor would overflow and force a scroll. One
utility class, min-h-0, cancels that floor. Nothing else is hand-rolled.
Before & after
Loading matter team…
Three surfaces, one treatment
| Surface | File | Before → after |
|---|---|---|
| Team roster | matter-team-roster.tsx |
<p>{t("rosterLoading")}</p> → <RouteLoading className="min-h-0" /> |
| Access history | matter-team-access-history.tsx |
<p>{t("historyLoading")}</p> → <RouteLoading className="min-h-0" /> |
| Add teammate | matter-team-add-member-view.tsx |
<p>{t("rosterLoading")}</p> → <RouteLoading className="min-h-0" /> |
Why min-h-0 is the whole story on sizing
RouteLoading’s base is
flex h-full min-h-[60vh] w-full items-center justify-center bg-card. The
h-full and centring already do the work; the drawer only has to drop the
full-page floor. cn(base, "min-h-0") resolves to a block that is exactly the
drawer-body height — it fills the scroll area and centres the spinner, and it can never
force a scroll, even in the footer’d add-teammate view or a short viewport.
i18n — delete the orphans, keep parity
With the text gone, rosterLoading and historyLoading are referenced
nowhere. Both keys are removed from both messages/en.json and
messages/hi.json — no orphaned keys, locale parity preserved. The error and
empty-state copy on these surfaces is untouched.
Proving the loading state — in the real drawer
The drawer’s loading branch is driven by live TanStack Query hooks, so the seeded
Storybook stories never show it. Three new stories — RosterLoading,
AddTeammateLoading, AccessHistoryLoading — drive the
real MatterTeamDrawer into its isLoading branch by letting
its queries hang, so what renders is the shipped path, not a fixture.
A setQueryDefaults never-resolving queryFn was tried first and
rejected: the generated hook passes its own inline queryFn, which wins the merge,
so the real request ran and flipped the drawer to its error branch after a tick. Hanging the
transport is what actually holds the loading state — verified before choosing it.
Verification
pnpm lint — 0 errors.
pnpm typecheck — clean.
pnpm test — 1628 passed.
pnpm check:routes / check:catalog / check:css-tokens
— fresh / OK (no new tokens; min-h-0 is a spacing utility, not a colour
literal).
pnpm storybook:drift / storybook:build — green.
pnpm build — production build clean.
Visually confirmed from the built Storybook: all three loading stories render exactly one
centred spinner (role="status"), no leftover “Loading…” text,
and the add-teammate view keeps its footer with the spinner centred above it.
Files changed
6 file(s), +74 / -19 lines
| Path | Role | + | - |
|---|---|---|---|
| messages/en.json | Config | +0 | -2 |
| messages/hi.json | Config | +0 | -2 |
| src/components/matter-detail/team/matter-team-access-history.tsx | Component | +3 | -5 |
| src/components/matter-detail/team/matter-team-add-member-view.tsx | Component | +3 | -5 |
| src/components/matter-detail/team/matter-team-drawer.stories.tsx | Component | +65 | -0 |
| src/components/matter-detail/team/matter-team-roster.tsx | Component | +3 | -5 |