feat(ui): add UserButton controller - #9185
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Repository UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
8786841 to
ba22f92
Compare
ba22f92 to
c9bdf23
Compare
dfae67d to
ef35f2d
Compare
ef35f2d to
8f7ffa2
Compare
c9bdf23 to
93aa6f2
Compare
075c15d to
4e052b8
Compare
93aa6f2 to
4643795
Compare
4643795 to
4467f3d
Compare
2bff351 to
0127447
Compare
4467f3d to
fbd5c2f
Compare
0127447 to
791fa21
Compare
fbd5c2f to
8f358b9
Compare
Every other action — switching account, signing out of one, joining a suggested or invited workspace — now resolves back into an open popover so the result is visible where it happened. The swingset prototypes fake the round trip they make against Clerk, so the spinner and stood-down rows are demonstrable without a running app.
The trigger carried the avatar alone. It now names what is active beside it — the organization and its plan wherever one heads the trigger, the account otherwise — behind `showLabel`, which defaults on. Badge's `neutral` color was unreadable in both schemes: its fill is a 900 and its text token is a text color, not an on-fill one. It now rides the same black/white scrim the button's neutral fill does.
`showLabel` becomes `renderTriggerLabel`, and the plan badge gets its own `renderPlanBadge`. The badge is part of the label, so it needs both.
…ccount The trigger and the popup's header now always name the same workspace. `combined` carries both switchers, so `modePriority` picks which one it leads with: the active organization by default, the account with `modePriority="user"`. Both are still listed either way.
8f358b9 to
6ff2457
Compare
Summary
Stacked on #9184 (the presentational view). Adds the controller layer that connects
UserButtonto live Clerk data, plus its tests (TDD).useUserButtonController()(user-button.controller.tsx) — a plain hook (no machine) returning a'loading' | 'hidden' | 'ready'discriminated union. Whenreadyit carriesUserButtonData & UserButtonCallbacks1:1 with the contract inuser-button.view.tsx.UserButton(user-button.tsx) — the connected container. Owns popover open state and close-on-success (Option A: wraps each one-shot callback with.finally(close); manage/create navigations do not auto-close).UserButton→UserButtonView, reserving the bareUserButtonfor the connected container. Updated the swingset story + mdx.Data mapping (Clerk → contract)
activeSessionfromuseUser()+useSession(), name fallback first+last > username > email.activeOrganizationIdfromuseOrganization()(null= personal).memberships/suggestions/invitationsfromuseOrganizationList(organizationListParams)(reused from the legacy switcher).hasOrganizationsfromuserMemberships.count(never array length).additionalSessionsfromclerk.client.signedInSessions, excluding the active user's session.membershipRequestCountonly on the active-org row and only withorg:sys_memberships:manage.planLabel/upgradeabledeferred (undefined).Actions (navigation mode)
setActivefor select/switch,signOutfor sign-out,router.navigate(clerk.build*Url())for manage/create,.accept()+revalidate()for invitations/suggestions.Tests
12 controller tests (
__tests__/user-button.controller.test.tsx) using the mosaicvi.mock('@clerk/shared/react')harness (copied fromorganization-profile-delete-section.controller.test.tsx). Written failing first, then implemented to green.Notes / deviations from the handoff
Two points where the handoff wasn't directly implementable; resolved as follows:
onSelectOrganizationnavigation. The handoff referenceddisplayConfig.afterSelectOrganizationUrl, but that field only exists as a component prop, not on the environment. Per decision, navigation goes throughsetActive({ organization, redirectUrl })(matching the legacy switcher), usingdisplayConfig.afterCreateOrganizationUrlas the redirect. Fully type-safe, no cast.loading → <UserButtonView status="loading" />, but the view requiresactiveSession(which is only present whenready) and the view was out of scope to restructure. The container rendersnullwhile loading/hidden and the view only whenready. The controller still exposesstatus: 'loading'for future use.Checklist
turbo test --filter @clerk/ui(12 new + full suite: 2666 passing)type-checkcleaneslintclean (0 errors)