Skip to content

feat(ui): migrate organization profile members panel to mosaic#9109

Open
kylemac wants to merge 1 commit into
mainfrom
carp/mosaic-members-panel
Open

feat(ui): migrate organization profile members panel to mosaic#9109
kylemac wants to merge 1 commit into
mainfrom
carp/mosaic-members-panel

Conversation

@kylemac

@kylemac kylemac commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Stress-test PR — this migration was produced by the mosaic Claude Code skill to evaluate the skill's effectiveness. Based on carp/mosaic-skills (#9106) so the diff shows only the members-panel work. Likely to be closed after review.

What this does

Migrates the OrganizationProfile members panel into Mosaic using the skill's machine → controller → view workflow, wires it into the Members tab of the Mosaic OrganizationProfile, and documents it in swingset.

Members-list section (packages/ui/src/mosaic/sections/members-list.*)

  • .machine.ts — pure flow rules: search input-value vs. committed query (SEARCH_CHANGE/SEARCH_SUBMIT) + a removing state with an injected async remove invoke and error handling. No React, no Clerk.
  • .controller.tsx — the only Clerk layer: fetches memberships keyed off the machine's query, gates on org:sys_memberships:read/:manage, derives plain MemberRow[], injects the per-row remove effect (destroy() + fire-and-forget revalidate()).
  • .view.tsx — Clerk-free rendering: search form, members table, gated Remove button, Prev/Next pagination, loading/empty/error states. Built only from existing Mosaic components.
  • .tsx — thin wrapper; renders nothing until the controller is ready.

Panel + AIO wiring

  • New organization-profile-members panel (+ view) composes the section.
  • AIO organization-profile.tsx/view take a members prop, replacing the "Members content" placeholder.

Swingset docs

MembersList (Section) and OrganizationProfileMembers (Panel) story + mdx (archetype C), AIO page updated, registered in registry.ts + DocsViewer.

Verification

  • @clerk/ui type-check: 0 errors · swingset type-check: 0 errors
  • Mosaic suite: 302 passed (incl. 27 new machine/controller/view tests)
  • Empty changeset — mosaic isn't reachable through any published @clerk/ui entry point, so no user-facing impact.

Parity audit (deferred items to track)

Read + remove + search + paginate are fully migrated. Consciously deferred (each needs a Mosaic primitive that doesn't exist yet):

  • Role editing (RoleSelect + membership.update) — needs a Mosaic Select; role renders read-only.
  • Role-set-migration warning (hasRoleSetMigration) — needs a Mosaic Alert + useFetchRoles.
  • localizeCustomRole — shows roleName instead of the localized custom role.
  • Member avatar (UserPreview image) — needs a Mosaic Avatar.

Deliberate changes: ThreeDotsMenu → inline Remove button; awaited → fire-and-forget revalidate; full numbered pagination → Prev/Next. Consistent with existing mosaic sections (hardcoded English strings, error.message). Invitations/Requests sub-tabs and the seat-usage footer were out of scope for this pass.

Summary by CodeRabbit

  • New Features

    • Added an organization profile Members panel displaying member names, roles, identifiers, join dates, and status badges.
    • Added member search, pagination, loading and empty states, permission-based controls, and member removal with progress and error feedback.
    • Added the Members panel to the Organization Profile navigation and component API.
  • Documentation

    • Added an interactive Members panel example to the documentation and component showcase.
  • Tests

    • Added coverage for rendering, searching, pagination, permissions, and member removal workflows.

@changeset-bot

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d09b2ef

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

When 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

@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jul 10, 2026 2:25pm
swingset Ready Ready Preview, Comment Jul 10, 2026 2:25pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an organization profile members panel with permission-aware membership data, search, removal, pagination, loading and error states, profile integration, Storybook/MDX documentation, registry wiring, and focused tests.

Changes

Organization members panel

Layer / File(s) Summary
Membership state and controller
packages/ui/src/mosaic/organization/organization-profile-members-panel.machine.ts, packages/ui/src/mosaic/organization/organization-profile-members-panel.controller.tsx, packages/ui/src/mosaic/organization/__tests__/*controller*, packages/ui/src/mosaic/organization/__tests__/*machine*
Adds typed search and removal state handling, permission-gated membership loading, member row mapping, removal effects, pagination, and controller/machine coverage.
Members view and profile integration
packages/ui/src/mosaic/organization/organization-profile-members-panel.view.tsx, packages/ui/src/mosaic/organization/organization-profile-members-panel.tsx, packages/ui/src/mosaic/organization/organization-profile-view.tsx, packages/ui/src/mosaic/organization/organization-profile.tsx, packages/ui/src/mosaic/organization/__tests__/*view*
Renders the members table, search form, badges, removal controls, loading and error states, pagination, and connects the panel to the organization profile.
Story and documentation wiring
packages/swingset/src/stories/*members-panel*, packages/swingset/src/stories/organization-profile.stories.tsx, packages/swingset/src/lib/registry.ts, packages/swingset/src/components/DocsViewer.tsx, .changeset/wild-members-gather.md
Adds demo data and stories, registers the story and MDX page, composes the panel in the profile story, and adds a changeset.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OrganizationProfile
  participant OrganizationProfileMembersPanel
  participant MembershipResource
  OrganizationProfile->>OrganizationProfileMembersPanel: render members slot
  OrganizationProfileMembersPanel->>MembershipResource: load organization memberships
  MembershipResource-->>OrganizationProfileMembersPanel: return rows and pagination
  OrganizationProfileMembersPanel->>MembershipResource: destroy selected membership
  MembershipResource-->>OrganizationProfileMembersPanel: return removal result
  OrganizationProfileMembersPanel-->>OrganizationProfile: render updated panel state
Loading

Poem

I’m a rabbit with members in rows,
Search hops quickly wherever it goes.
Remove with a click, pagination in flight,
Badges and stories make the panel bright.
Binky, binky—the profile feels right!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: migrating the organization profile members panel to Mosaic.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Jul 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9109

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9109

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9109

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9109

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9109

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9109

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9109

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9109

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9109

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9109

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9109

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9109

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9109

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9109

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9109

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9109

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9109

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9109

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9109

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9109

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9109

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9109

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9109

commit: d09b2ef

@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-10T14:26:25.147Z

Summary

Metric Count
Packages analyzed 19
Packages with changes 0
🔴 Breaking changes 0
🟡 Non-breaking changes 0
🟢 Additions 0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d09b2ef.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
packages/swingset/src/stories/organization-profile-members-panel.stories.tsx (1)

46-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the required Swingset story function signature.

Default is exported but has neither a props: Record<string, unknown> parameter nor an explicit return type. Align it with the standard knobsAsProps story pattern and declare the return type.

As per coding guidelines, story functions must accept Record<string, unknown> props and TypeScript functions should declare explicit return types.

🤖 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/organization-profile-members-panel.stories.tsx`
at line 46, Update the exported Default story function to accept a props
parameter typed as Record<string, unknown>, following the repository’s
knobsAsProps story pattern, and add an explicit return type consistent with the
rendered story component.

Source: Coding guidelines

packages/ui/src/mosaic/organization/__tests__/organization-profile-members-panel.view.test.tsx (1)

120-127: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test only covers a single row; doesn't verify other rows' disabled state during removal.

This test renders only one row (rows: [row({ id: 'mem_1' })]) with pendingMembershipId: 'mem_1', so it confirms the pending row is disabled/relabeled but doesn't assert whether other rows' remove buttons remain enabled or disabled while a removal is in flight. Given the machine (organization-profile-members-panel.machine.ts) silently drops REMOVE_MEMBER events dispatched while in the removing state, a test with multiple rows — one pending, one not — asserting the non-pending row's button state would close this gap.

🤖 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/organization/__tests__/organization-profile-members-panel.view.test.tsx`
around lines 120 - 127, Extend the test "disables and relabels the row currently
being removed" to render at least two rows: the pending row with id "mem_1" and
a different non-pending row. Keep asserting the pending row's "Removing…" button
is disabled, and additionally assert the non-pending row's remove button remains
enabled while removal is in progress.
🤖 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/ui/src/mosaic/organization/organization-profile-members-panel.tsx`:
- Around line 6-9: Document the exported OrganizationProfileMembersPanel
function with JSDoc describing that it returns null while the controller is not
ready and what content it renders when ready; then have Docs review the
generated public reference entry for OrganizationProfile.MembersPanel.

In
`@packages/ui/src/mosaic/organization/organization-profile-members-panel.view.tsx`:
- Around line 15-23: Document the exported
OrganizationProfileMembersPanelViewProps interface and
OrganizationProfileMembersPanelView component with JSDoc, covering each public
prop, the component’s return value, and a concise usage example; place the
documentation directly above the corresponding declarations.
- Line 27: Update the local components Badge, HeaderCell, and Cell to explicitly
declare ReactElement as their return type, and import ReactElement as a type
from React. Preserve their existing props and JSX implementations.

In `@packages/ui/src/mosaic/organization/organization-profile-view.tsx`:
- Line 15: The exported OrganizationProfileView function lacks required API
documentation and an explicit return type. Add JSDoc describing the general,
members, and apiKeys parameters and the rendered result, then annotate
OrganizationProfileView’s return type as ReactElement.

---

Nitpick comments:
In
`@packages/swingset/src/stories/organization-profile-members-panel.stories.tsx`:
- Line 46: Update the exported Default story function to accept a props
parameter typed as Record<string, unknown>, following the repository’s
knobsAsProps story pattern, and add an explicit return type consistent with the
rendered story component.

In
`@packages/ui/src/mosaic/organization/__tests__/organization-profile-members-panel.view.test.tsx`:
- Around line 120-127: Extend the test "disables and relabels the row currently
being removed" to render at least two rows: the pending row with id "mem_1" and
a different non-pending row. Keep asserting the pending row's "Removing…" button
is disabled, and additionally assert the non-pending row's remove button remains
enabled while removal is in progress.
🪄 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: a4f6ede3-627f-40f0-9d5b-102ab510836a

📥 Commits

Reviewing files that changed from the base of the PR and between 145e4d3 and d09b2ef.

📒 Files selected for processing (15)
  • .changeset/wild-members-gather.md
  • packages/swingset/src/components/DocsViewer.tsx
  • packages/swingset/src/lib/registry.ts
  • packages/swingset/src/stories/organization-profile-members-panel.mdx
  • packages/swingset/src/stories/organization-profile-members-panel.stories.tsx
  • packages/swingset/src/stories/organization-profile.stories.tsx
  • packages/ui/src/mosaic/organization/__tests__/organization-profile-members-panel.controller.test.tsx
  • packages/ui/src/mosaic/organization/__tests__/organization-profile-members-panel.machine.test.ts
  • packages/ui/src/mosaic/organization/__tests__/organization-profile-members-panel.view.test.tsx
  • packages/ui/src/mosaic/organization/organization-profile-members-panel.controller.tsx
  • packages/ui/src/mosaic/organization/organization-profile-members-panel.machine.ts
  • packages/ui/src/mosaic/organization/organization-profile-members-panel.tsx
  • packages/ui/src/mosaic/organization/organization-profile-members-panel.view.tsx
  • packages/ui/src/mosaic/organization/organization-profile-view.tsx
  • packages/ui/src/mosaic/organization/organization-profile.tsx

Comment on lines +6 to +9
export function OrganizationProfileMembersPanel(): ReactElement | null {
const controller = useOrganizationProfileMembersPanelController();
if (controller.status !== 'ready') {
return null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Document the exported members panel.

OrganizationProfileMembersPanel is exported and exposed as OrganizationProfile.MembersPanel, but has no JSDoc describing its loading/null behavior or rendered content. Add the public API documentation and have Docs review the generated reference entry.

As per coding guidelines, public TypeScript APIs must be documented with JSDoc. As per path instructions, exported APIs under packages/** are customer-facing reference documentation.

🤖 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/organization/organization-profile-members-panel.tsx`
around lines 6 - 9, Document the exported OrganizationProfileMembersPanel
function with JSDoc describing that it returns null while the controller is not
ready and what content it renders when ready; then have Docs review the
generated public reference entry for OrganizationProfile.MembersPanel.

Sources: Coding guidelines, Path instructions

Comment on lines +15 to +23
export interface OrganizationProfileMembersPanelViewProps {
snapshot: Snapshot<OrganizationProfileMembersPanelContext>;
send: (event: OrganizationProfileMembersPanelEvent) => void;
rows: MemberRow[];
canManage: boolean;
page: number;
pageCount: number;
isLoading: boolean;
onPageChange: (page: number) => void;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Document the exported view contract.

OrganizationProfileMembersPanelViewProps and OrganizationProfileMembersPanelView are exported but lack JSDoc. Document the public props, return value, and usage example so generated reference documentation matches the implementation.

As per coding guidelines, public TypeScript APIs must be documented with JSDoc. As per path instructions, exported APIs under packages/** are customer-facing reference documentation.

Also applies to: 84-93

🤖 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/organization/organization-profile-members-panel.view.tsx`
around lines 15 - 23, Document the exported
OrganizationProfileMembersPanelViewProps interface and
OrganizationProfileMembersPanelView component with JSDoc, covering each public
prop, the component’s return value, and a concise usage example; place the
documentation directly above the corresponding declarations.

Sources: Coding guidelines, Path instructions

}

/** Small pill used for the "You" / "Banned" row markers. */
function Badge({ children, intent = 'neutral' }: { children: ReactNode; intent?: 'neutral' | 'destructive' }) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add explicit return types to local components.

Badge, HeaderCell, and Cell return JSX but rely on inference. Annotate them with ReactElement and import that type.

Proposed fix
-import type { ReactNode } from 'react';
+import type { ReactElement, ReactNode } from 'react';

-function Badge({ children, intent = 'neutral' }: { children: ReactNode; intent?: 'neutral' | 'destructive' }) {
+function Badge({ children, intent = 'neutral' }: { children: ReactNode; intent?: 'neutral' | 'destructive' }): ReactElement {
...
-function HeaderCell({ children, align = 'start' }: { children?: ReactNode; align?: 'start' | 'end' }) {
+function HeaderCell({ children, align = 'start' }: { children?: ReactNode; align?: 'start' | 'end' }): ReactElement {
...
-function Cell({ children, align = 'start' }: { children?: ReactNode; align?: 'start' | 'end' }) {
+function Cell({ children, align = 'start' }: { children?: ReactNode; align?: 'start' | 'end' }): ReactElement {

As per coding guidelines, TypeScript functions should always have explicit return types.

Also applies to: 48-48, 67-67

🤖 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/organization/organization-profile-members-panel.view.tsx`
at line 27, Update the local components Badge, HeaderCell, and Cell to
explicitly declare ReactElement as their return type, and import ReactElement as
a type from React. Preserve their existing props and JSX implementations.

Source: Coding guidelines

}

export function OrganizationProfileView({ general, apiKeys }: OrganizationProfileViewProps) {
export function OrganizationProfileView({ general, members, apiKeys }: OrganizationProfileViewProps) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Document and type the exported profile view.

OrganizationProfileView is exported and its slot contract now includes members, but the function has neither JSDoc nor an explicit return type. Document general, members, apiKeys, and the return value, then annotate the return as ReactElement.

As per coding guidelines, public TypeScript APIs require JSDoc and explicit return types. As per path instructions, exported APIs under packages/** feed customer-facing reference documentation.

🤖 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/organization/organization-profile-view.tsx` at line
15, The exported OrganizationProfileView function lacks required API
documentation and an explicit return type. Add JSDoc describing the general,
members, and apiKeys parameters and the rendered result, then annotate
OrganizationProfileView’s return type as ReactElement.

Sources: Coding guidelines, Path instructions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants