feat(ui): add UserButton view component - #9184
Conversation
🦋 Changeset detectedLatest commit: 6408f05 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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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:
📝 WalkthroughWalkthroughAdds a client-side Mosaic Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/electron
@clerk/electron-passkeys
@clerk/eslint-plugin
@clerk/expo
@clerk/expo-google-signin
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
API Changes Report
Summary
No API Changes DetectedAll packages have stable APIs with no detected changes. Report generated by Break Check Last ran on |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/swingset/src/lib/registry.ts`:
- Around line 3-8: Update the manually constructed AccountButton story module to
preserve the story’s raw __source export. Import __source from
account-button.stories and include it in the module object alongside
AccountButtonDefault, AccountButtonMultipleAccounts, AccountButtonPersonal, and
accountButtonMeta; apply the same change to the corresponding second
construction.
In `@packages/swingset/src/stories/account-button.mdx`:
- Around line 3-21: Restructure the AccountButton MDX documentation into
Archetype C format: replace the current descriptive sections with one short
paragraph covering owned state and the wired lower-layer pieces, then provide
exactly one Story. Add a composition array to that Story listing each direct
dependency, including the headless popover primitive and slot recipes, and
remove the current multi-story layout.
In `@packages/swingset/src/stories/account-button.stories.tsx`:
- Around line 10-14: Update the AccountButton story metadata to assign
accountButtonRecipe to meta.styles. In each story function, cast the knobs
Record<string, unknown> through knobsAsProps and spread the resulting props into
AccountButton so playground values are applied.
In `@packages/ui/src/mosaic/components/account-button.view.tsx`:
- Around line 446-808: Split the presentational leaves and sections from
account-button.view.tsx into focused React modules, extracting symbols such as
Avatar, Row, Header, WorkspaceList, AccountsSection, and Footer while preserving
their current behavior and context usage. Keep the public compound component
composition and exports in account-button.view.tsx, and update imports as needed
so the file remains focused and within the project’s preferred component size.
- Around line 21-85: Add comprehensive customer-facing JSDoc to the exported
AccountButtonAccount, AccountButtonMembership, AccountButtonSuggestion,
AccountButtonInvitation, AccountButtonData, AccountButtonCallbacks, and props
contracts, documenting field semantics including onUpgrade. Add explicit
React.JSX.Element return types to all four exported AccountButton components,
then request Docs-team review for the generated public API documentation.
- Around line 57-68: Remove the inert status field from AccountButtonData for
v1, since the current loading value still requires and renders ready-state data.
Update AccountButton consumers, including the account-button view, to rely on
the existing ready-state shape without status checks; only retain status if you
introduce a genuine discriminated union with a separate loading rendering path.
- Around line 488-535: Update the selection control rendered by Row to expose
its active state through the appropriate aria-current attribute, using the
active prop’s value. Apply the attribute to the interactive button and the
non-interactive div consistently, while leaving the visual check icon behavior
unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Repository UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 05507a88-c836-4e0f-b43f-e0d914595582
📒 Files selected for processing (8)
.changeset/account-button-switcher.mdpackages/swingset/src/components/DocsViewer.tsxpackages/swingset/src/lib/registry.tspackages/swingset/src/stories/account-button.mdxpackages/swingset/src/stories/account-button.stories.tsxpackages/ui/src/mosaic/components/account-button.view.tsxpackages/ui/src/mosaic/icons/registry.tsxpackages/ui/src/mosaic/primitives/popover.tsx
| import { | ||
| Default as AccountButtonDefault, | ||
| meta as accountButtonMeta, | ||
| MultipleAccounts as AccountButtonMultipleAccounts, | ||
| Personal as AccountButtonPersonal, | ||
| } from '../stories/account-button.stories'; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Preserve __source when constructing the story module.
The story exports its raw source, but this manually built module drops it, preventing AccountButton code footers from rendering.
Proposed fix
import {
+ __source as accountButtonSource,
Default as AccountButtonDefault,
meta as accountButtonMeta,
MultipleAccounts as AccountButtonMultipleAccounts,
Personal as AccountButtonPersonal,
} from '../stories/account-button.stories';
const accountButtonModule: StoryModule = {
meta: accountButtonMeta,
+ __source: accountButtonSource,
Default: AccountButtonDefault,As per path instructions, Components-layer stories export __source to enable code footers.
Also applies to: 130-135
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/swingset/src/lib/registry.ts` around lines 3 - 8, Update the
manually constructed AccountButton story module to preserve the story’s raw
__source export. Import __source from account-button.stories and include it in
the module object alongside AccountButtonDefault, AccountButtonMultipleAccounts,
AccountButtonPersonal, and accountButtonMeta; apply the same change to the
corresponding second construction.
Source: Path instructions
| # AccountButton | ||
|
|
||
| The account & organization switcher that sits behind the user button. The active account sits at the | ||
| top with its organizations (including any **suggested** workspaces you can Join), and **additional | ||
| accounts** are listed below. Hovering the active account reveals **Sign out**; clicking any additional | ||
| account switches to it. **Settings / Members** open the combined profile modal, and the surface also | ||
| exposes **Add organization**, **Add account**, and **Sign out of all accounts**. | ||
|
|
||
| It is the styled Mosaic component composed from the headless `@clerk/headless` popover primitive plus | ||
| slot recipes, and inherits the primitive's open/close behavior, focus management, and ARIA wiring. This | ||
| first pass is presentational: the parts render from the props on `AccountButtonRoot` (mock data in the | ||
| examples). A `useAccountButtonController()` that reads live Clerk resources is a drop-in follow-up. | ||
|
|
||
| ## Example | ||
|
|
||
| <Story | ||
| name='Default' | ||
| storyModule={AccountButtonStories} | ||
| /> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Use the Archetype C documentation structure.
Lead with one short paragraph describing owned state and wired lower-layer pieces, followed by one <Story> whose composition array names each direct dependency. The current multiple-story layout follows neither requirement.
As per path instructions, Archetype C MDX must use a single ownership/composition paragraph followed by one Story with a dependency composition array.
Also applies to: 68-89
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/swingset/src/stories/account-button.mdx` around lines 3 - 21,
Restructure the AccountButton MDX documentation into Archetype C format: replace
the current descriptive sections with one short paragraph covering owned state
and the wired lower-layer pieces, then provide exactly one Story. Add a
composition array to that Story listing each direct dependency, including the
headless popover primitive and slot recipes, and remove the current multi-story
layout.
Source: Path instructions
| export const meta: StoryMeta = { | ||
| group: 'Components', | ||
| title: 'AccountButton', | ||
| source: 'packages/ui/src/mosaic/components/account-button.view.tsx', | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Wire the stories into the playground knobs.
Set meta.styles to accountButtonRecipe, add the local knobsAsProps cast, and spread the resulting props into each AccountButton. The current stories discard every playground value.
As per path instructions, story functions must cast Record<string, unknown> through knobsAsProps, and Mosaic recipe metadata must be assigned to meta.styles.
Also applies to: 36-112
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/swingset/src/stories/account-button.stories.tsx` around lines 10 -
14, Update the AccountButton story metadata to assign accountButtonRecipe to
meta.styles. In each story function, cast the knobs Record<string, unknown>
through knobsAsProps and spread the resulting props into AccountButton so
playground values are applied.
Source: Path instructions
| export interface AccountButtonAccount { | ||
| sessionId: string; | ||
| userId: string; | ||
| name: string; | ||
| email: string; | ||
| imageUrl?: string; | ||
| } | ||
|
|
||
| export interface AccountButtonMembership { | ||
| kind: 'membership'; | ||
| organizationId: string; | ||
| name: string; | ||
| imageUrl?: string; | ||
| membersCount?: number; | ||
| planLabel?: string; | ||
| upgradeable?: boolean; | ||
| membershipRequestCount?: number; | ||
| } | ||
|
|
||
| export interface AccountButtonSuggestion { | ||
| kind: 'suggestion'; | ||
| id: string; | ||
| organizationId: string; | ||
| name: string; | ||
| imageUrl?: string; | ||
| status: 'pending' | 'accepted'; | ||
| } | ||
|
|
||
| export interface AccountButtonInvitation { | ||
| kind: 'invitation'; | ||
| id: string; | ||
| organizationId: string; | ||
| organizationName: string; | ||
| imageUrl?: string; | ||
| } | ||
|
|
||
| export interface AccountButtonData { | ||
| status: 'loading' | 'ready'; | ||
| activeAccount: AccountButtonAccount; | ||
| /** `null` => the personal workspace is active. */ | ||
| activeOrganizationId: string | null; | ||
| /** Explicit; do not derive from `memberships.length`. */ | ||
| hasOrganizations: boolean; | ||
| memberships: AccountButtonMembership[]; | ||
| suggestions: AccountButtonSuggestion[]; | ||
| invitations: AccountButtonInvitation[]; | ||
| additionalAccounts: AccountButtonAccount[]; | ||
| } | ||
|
|
||
| /** All optional. An unhandled action hides (or de-activates) the affordance it drives. */ | ||
| export interface AccountButtonCallbacks { | ||
| onSelectOrganization?: (organizationId: string) => void; | ||
| onSelectPersonal?: () => void; | ||
| onAcceptSuggestion?: (suggestionId: string) => void; | ||
| onAcceptInvitation?: (invitationId: string) => void; | ||
| onSwitchAccount?: (sessionId: string) => void; | ||
| onSignOutSession?: (sessionId: string) => void; | ||
| onSignOutAll?: () => void; | ||
| onManageOrganization?: () => void; | ||
| onManageMembers?: () => void; | ||
| onManageAccount?: () => void; | ||
| onCreateOrganization?: () => void; | ||
| onAddAccount?: () => void; | ||
| onUpgrade?: () => void; | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Complete the public API documentation and return types.
The exported resource, callback, and props contracts—especially onUpgrade—lack customer-facing field semantics, while the four exported components have no explicit return types. Add comprehensive JSDoc and React.JSX.Element annotations, then request Docs-team review.
As per coding guidelines, all public APIs require comprehensive JSDoc and explicit return types. As per path instructions, public JSDoc under packages/** feeds generated Clerk Docs.
Also applies to: 812-839, 841-905
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/mosaic/components/account-button.view.tsx` around lines 21 -
85, Add comprehensive customer-facing JSDoc to the exported
AccountButtonAccount, AccountButtonMembership, AccountButtonSuggestion,
AccountButtonInvitation, AccountButtonData, AccountButtonCallbacks, and props
contracts, documenting field semantics including onUpgrade. Add explicit
React.JSX.Element return types to all four exported AccountButton components,
then request Docs-team review for the generated public API documentation.
Sources: Coding guidelines, Path instructions
| export interface AccountButtonData { | ||
| status: 'loading' | 'ready'; | ||
| activeAccount: AccountButtonAccount; | ||
| /** `null` => the personal workspace is active. */ | ||
| activeOrganizationId: string | null; | ||
| /** Explicit; do not derive from `memberships.length`. */ | ||
| hasOrganizations: boolean; | ||
| memberships: AccountButtonMembership[]; | ||
| suggestions: AccountButtonSuggestion[]; | ||
| invitations: AccountButtonInvitation[]; | ||
| additionalAccounts: AccountButtonAccount[]; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Do not publish an inert status state.
status='loading' renders the complete ready UI and still requires every ready-state resource. Remove status for v1, or make AccountButtonData a real discriminated union with a loading rendering path; correcting this after release would constrain @clerk/ui compatibility.
As per coding guidelines, use discriminated unions for state management and keep packages/ui APIs backwards-compatible.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/mosaic/components/account-button.view.tsx` around lines 57 -
68, Remove the inert status field from AccountButtonData for v1, since the
current loading value still requires and renders ready-state data. Update
AccountButton consumers, including the account-button view, to rely on the
existing ready-state shape without status checks; only retain status if you
introduce a genuine discriminated union with a separate loading rendering path.
Source: Coding guidelines
| function Row({ name, secondary, shape, imageUrl, onSelect, active, badge, trailing, hoverAction }: RowProps) { | ||
| const { item, select, name: nameSlot, secondary: secondarySlot } = useRecipe(accountButtonRecipe); | ||
| const inner = ( | ||
| <> | ||
| <Avatar | ||
| name={name} | ||
| imageUrl={imageUrl} | ||
| shape={shape} | ||
| size='md' | ||
| /> | ||
| <span css={{ display: 'flex', flexDirection: 'column', minWidth: 0, flex: 1 }}> | ||
| <span css={{ display: 'flex', alignItems: 'center', gap: '0.375rem', minWidth: 0 }}> | ||
| <span {...nameSlot}>{name}</span> | ||
| {badge} | ||
| </span> | ||
| {secondary ? <span {...secondarySlot}>{secondary}</span> : null} | ||
| </span> | ||
| </> | ||
| ); | ||
| return ( | ||
| <div {...item}> | ||
| {onSelect ? ( | ||
| <button | ||
| type='button' | ||
| onClick={onSelect} | ||
| {...select} | ||
| > | ||
| {inner} | ||
| </button> | ||
| ) : ( | ||
| <div | ||
| {...select} | ||
| style={{ cursor: 'default' }} | ||
| > | ||
| {inner} | ||
| </div> | ||
| )} | ||
| {active ? ( | ||
| <Icon | ||
| name='check' | ||
| size='sm' | ||
| sx={t => ({ color: t.color.cardForeground, flexShrink: 0 })} | ||
| /> | ||
| ) : null} | ||
| {trailing} | ||
| {hoverAction} | ||
| </div> | ||
| ); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Expose the active row to assistive technology.
The check icon is purely visual. Mark the selection control with aria-current so screen-reader users can identify the active account or organization.
Proposed fix
<button
type='button'
+ aria-current={active ? 'true' : undefined}
onClick={onSelect}
{...select}As per coding guidelines, React components must implement proper ARIA attributes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/mosaic/components/account-button.view.tsx` around lines 488 -
535, Update the selection control rendered by Row to expose its active state
through the appropriate aria-current attribute, using the active prop’s value.
Apply the attribute to the interactive button and the non-interactive div
consistently, while leaving the visual check icon behavior unchanged.
Source: Coding guidelines
dfae67d to
ef35f2d
Compare
ef35f2d to
8f7ffa2
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
packages/ui/src/mosaic/user-button/user-button.view.tsx (2)
477-538: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winExpose the active row to assistive technology.
The check icon at Lines 527-533 is purely visual. Add
aria-currentto the interactivebutton(Line 512) and the non-interactivediv(Line 520) inRow, driven by theactiveprop, so screen-reader users can identify the active account or organization.This mirrors the same unresolved concern raised previously on the sibling
account-button.view.tsxfile.As per coding guidelines, "Implement proper ARIA attributes for accessibility in React components."
♿ Proposed fix
{onSelect ? ( <button type='button' + aria-current={active ? 'true' : undefined} onClick={onSelect} {...select} > {inner} </button> ) : ( <div {...select} + aria-current={active ? 'true' : undefined} style={{ cursor: 'default' }} > {inner} </div> )}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/mosaic/user-button/user-button.view.tsx` around lines 477 - 538, Update the interactive and non-interactive row elements in Row to expose active state through aria-current, deriving the attribute from the active prop. Apply the same value to both the button and fallback div while preserving their existing behavior and rendering.
447-813: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy liftSplit this file into focused modules.
This file still holds the data contracts, recipe, context, presentational leaves (
Avatar,Row,SuggestedBadge,InlineButton,HoverAction,AddRow), sections (Header,WorkspaceList,SessionsSection,Footer), and the public compound parts in one 912-line module. Extract the leaves and sections into separate files and keep the compound composition (UserButtonRoot,UserButtonTrigger,UserButtonPopup,UserButton) here.This mirrors the same unresolved concern raised previously on the sibling
account-button.view.tsxfile.As per coding guidelines, "Limit component size to 150-200 lines; extract logic into custom hooks."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/mosaic/user-button/user-button.view.tsx` around lines 447 - 813, Split the user-button view into focused modules: move the presentational leaves Avatar, Row, SuggestedBadge, InlineButton, HoverAction, and AddRow, plus Header, WorkspaceList, SessionsSection, and Footer, into separate files with their required shared contracts and imports. Keep UserButtonRoot, UserButtonTrigger, UserButtonPopup, and UserButton in the current module, preserving their existing composition and behavior while reducing the file to focused component logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/swingset/src/lib/registry.ts`:
- Around line 108-113: Update the UserButton story imports to include the
exported __source value, then assign it to the __source property when
constructing userButtonModule alongside the existing story metadata. Preserve
the current module construction behavior for all other fields.
In `@packages/swingset/src/stories/user-button.mdx`:
- Around line 5-14: Merge the interaction and implementation descriptions in the
user-button intro into one short, present-tense paragraph, preserving the key
behavior and composition details while removing redundant wording.
- Around line 68-90: Replace the nonstandard “States & scenarios” heading in the
user-button documentation with the established “Examples” section name, keeping
the Personal and MultipleSessions stories and their descriptions under that
section.
---
Duplicate comments:
In `@packages/ui/src/mosaic/user-button/user-button.view.tsx`:
- Around line 477-538: Update the interactive and non-interactive row elements
in Row to expose active state through aria-current, deriving the attribute from
the active prop. Apply the same value to both the button and fallback div while
preserving their existing behavior and rendering.
- Around line 447-813: Split the user-button view into focused modules: move the
presentational leaves Avatar, Row, SuggestedBadge, InlineButton, HoverAction,
and AddRow, plus Header, WorkspaceList, SessionsSection, and Footer, into
separate files with their required shared contracts and imports. Keep
UserButtonRoot, UserButtonTrigger, UserButtonPopup, and UserButton in the
current module, preserving their existing composition and behavior while
reducing the file to focused component logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 6df10ab6-9322-4890-89da-e4419eb949e3
📒 Files selected for processing (8)
.changeset/user-button-switcher.mdpackages/swingset/src/components/DocsViewer.tsxpackages/swingset/src/lib/registry.tspackages/swingset/src/stories/user-button.mdxpackages/swingset/src/stories/user-button.stories.tsxpackages/ui/src/mosaic/icons/registry.tsxpackages/ui/src/mosaic/primitives/popover.tsxpackages/ui/src/mosaic/user-button/user-button.view.tsx
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
clerk/clerk_go(manual)clerk/dashboard(manual)clerk/accounts(manual)clerk/backoffice(manual)clerk/clerk(manual)clerk/clerk-docs(manual)clerk/cloudflare-workers(manual)
🚧 Files skipped from review as they are similar to previous changes (2)
- .changeset/user-button-switcher.md
- packages/ui/src/mosaic/primitives/popover.tsx
| import { | ||
| Default as UserButtonDefault, | ||
| meta as userButtonMeta, | ||
| MultipleSessions as UserButtonMultipleAccounts, | ||
| Personal as UserButtonPersonal, | ||
| } from '../stories/user-button.stories'; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Preserve __source when constructing userButtonModule.
user-button.stories.tsx exports __source via its ?raw self-import, but this import list (Lines 108-113) omits it, and userButtonModule (Lines 185-190) never sets it. Any consumer of StoryModule.__source for the code-footer feature won't have it for UserButton.
This mirrors the same unresolved concern raised previously for accountButtonModule.
🛠️ Proposed fix
import {
+ __source as userButtonSource,
Default as UserButtonDefault,
meta as userButtonMeta,
MultipleSessions as UserButtonMultipleAccounts,
Personal as UserButtonPersonal,
} from '../stories/user-button.stories';
...
const userButtonModule: StoryModule = {
meta: userButtonMeta,
+ __source: userButtonSource,
Default: UserButtonDefault,
Personal: UserButtonPersonal,
MultipleSessions: UserButtonMultipleAccounts,
};Also applies to: 185-190
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/swingset/src/lib/registry.ts` around lines 108 - 113, Update the
UserButton story imports to include the exported __source value, then assign it
to the __source property when constructing userButtonModule alongside the
existing story metadata. Preserve the current module construction behavior for
all other fields.
Adds a ClerkLogo component and centers the branding row on it, in place of the word mark in text.
Organization requests are scoped by FAPI to the session that makes them, so another signed-in account's organizations cannot be listed without switching to it first. The view modelled them anyway, which left a permanently empty branch. Replace `additionalAccounts` (session + memberships) with `additionalSessions`, and `onSelectWorkspace(sessionId, organizationId)` with `onSelectOrganization`, which can only ever mean the active account. Move the other accounts into their own "Accounts" group so they never read as workspaces, with "Add account" on the group heading; the footer keeps it where there is no such group. Drop the props nothing rendered (`status`, `userId`, `upgradeable`, `membershipRequestCount`, `onUpgrade`), gate a suggestion's Join button on its status so an accepted one reports "Requested" rather than re-submitting, and add `paging` so organizations past the first page can be fed in on scroll.
Card became a compound component, so the UserButton popup renders Card.Root. Its StyleX styles arrived after the radius tokens were renamed by size, so point them at the renamed token.
0127447 to
791fa21
Compare
Show the workspace section when the only thing to list is a pending invitation or suggestion, otherwise a user with no memberships can never accept one. Mark the workspace avatars decorative so their fallback initials stop padding the accessible name of the row they sit in.
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 sizes track the Icon scale (sm 14px, md 16px) so a spinner can stand in for the icon it replaces, and the UserButton's trailing column is now one slot the width of the menu button, so the spinner, the active check, and the menu all sit on the same centre line.
In `user` mode the accounts list now holds only the accounts to switch to: the Accounts heading drops away, Add account moves to the foot of the surface the way Create organization does on an org-only surface, and signing out of the active account sits beside the gear in the header.
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.
… invitations The trigger and header found the active organization by joining `activeOrganizationId` against `memberships`, which misses while that list is still loading, so the surface painted the account and then flipped to the organization. `activeOrganization` carries it whole instead, and the workspace list holds its place with a loading row until its first page lands. An accepted invitation is a workspace, not something left to accept, so it lists as a row you click to switch to, and drops once the membership list catches up with it.
New Mosaic
UserButton: account + org switcher (multi-session + org select/suggest/invite) behind a popover. Presentational view only; controller is follow-up.UserButtonRoot/Trigger/Popup+ all-in-oneUserButtonDeferred:
useUserButtonController(), pagination, real avatars.Note: added optional
onUpgrade(design shows Upgrade link); accent purple hardcoded (no token yet).