feat(ui): add UserButton controller - #9185
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 3f61f68 The changes in this PR will be included in the next version bump. This PR includes changesets to release 0 packagesWhen changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types 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 |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdded Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: ⚪ Minimal · up to The PR connects UserButton actions to live account and organization data; the only remaining concern is limited interaction-test coverage for pending actions and closing after selection. No actionable merge-blocking risk remains after normal checks and review. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 18.75% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 4 files. (1 skipped: 1 unsupported.) Comment |
8786841 to
ba22f92
Compare
ba22f92 to
c9bdf23
Compare
c9bdf23 to
93aa6f2
Compare
93aa6f2 to
4643795
Compare
4643795 to
4467f3d
Compare
4467f3d to
fbd5c2f
Compare
The connected test drives the real controller against a mocked Clerk, which is what makes it worth having and also what makes it slow. Cases that only ever asserted what the popover renders now sit in the view test, leaving the connected one to prove the layers compose. Also covers `hidePersonal` reaching the popover through the container.
UserButtonProps picked only modePriority off the root, so the connected component was hard-wired to the combined surface and the orgs/user modes were reachable only by composing UserButtonView directly.
Presses a custom row on the connected UserButton and checks the app's callback runs and the popover closes behind it.
An instance with organizations turned off has none to lead with or list, so the button is the account's whatever `mode` asked for — `orgs` would otherwise render an empty shell of a switcher. clerk-js withholds its own OrganizationSwitcher at the mount boundary, which an app importing this one never crosses, so the gate lives in the component.
The popover's open state and the one action in flight are the same flow, so they now live in one machine instead of two useStates. Re-entry, clearing busy, and closing on success stop being hand-written in the container: RUN is simply unhandled while busy, and busy is only reachable from open. Dismissing the popover mid-action now abandons the result rather than letting it land in a surface that is already gone.
Follows the view's rename of `'orgs'` to `'organization'`. Also corrects the integration suite's opening comment, which attributed close-on-success to the container and had the navigation case backwards.
Carries the app's own pages and links into the profile the UserButton opens, through useCustomPages and the built-in page list it orders them against.
Moves useCustomPages and useUserProfilePages out of the shared mosaic hooks folder into user-button.pages, and routes the custom page order through the same applyOrder rule the menu uses, which also stops a custom page named after a built-in from being sent twice.
Description
Stacked on #9184. Connects the
UserButtonview to live Clerk data.useUserButtonController()returns a'loading' | 'hidden' | 'ready'union. When it isreadyit carries the view's data contract and the callback behind every row.user-button.tsxis the connected container that owns the popover.Where the data comes from:
activeSessionfromuseUser()anduseSession(). The name follows Clerk's own order: first and last name, then username, then the identifier.activeOrganizationfromuseOrganization(), wherenullis the personal workspace.memberships,suggestions, andinvitationsfromuseOrganizationList(), paged as the list scrolls.hasOrganizationsfrom the user resource, so it can answer before those lists load.additionalSessionsfrom the client, without the active one.org:sys_memberships:manage.What the rows do:
setActive. Signing out callssignOut. Invitations and suggestions accept in place and revalidate the list.userProfileUrl,organizationProfileUrl,createOrganizationUrl.afterSelectOrganizationUrlandafterSelectPersonalUrlsay where picking a workspace lands.hidePersonalwithholds it by request.customMenuItemsreach the menu through the container, and a custom action closes the popover behind whatever it opens.The button renders nothing until Clerk answers. While it loads, a signed-out visitor and a session still resolving are indistinguishable, so anything rendered then is a button promised to people who are never going to get one.
<ClerkLoading>is where an app that knows its own nav puts a placeholder.Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change