Skip to content

refactor(ui): gate mosaic organization sections by permission and admin-delete#9041

Merged
alexcarpenter merged 5 commits into
mainfrom
feat/mosaic-org-section-gating
Jun 30, 2026
Merged

refactor(ui): gate mosaic organization sections by permission and admin-delete#9041
alexcarpenter merged 5 commits into
mainfrom
feat/mosaic-org-section-gating

Conversation

@alexcarpenter

@alexcarpenter alexcarpenter commented Jun 30, 2026

Copy link
Copy Markdown
Member

Summary

Mirrors the legacy OrganizationGeneralPage visibility gating in the mosaic delete/leave sections (packages/ui/src/mosaic/sections/). Previously both sections rendered for everyone once useOrganization() loaded. Now:

  • Delete renders only when the user has the org:sys_profile:delete permission and organization.adminDeleteEnabled is true.
  • Leave renders only when there is an active organization membership.

This matches OrganizationDeleteSection / OrganizationLeaveSection in packages/ui/src/components/OrganizationProfile/OrganizationGeneralPage.tsx.

How

  • The gating lives in the controllers (the only Clerk-aware layer in the mosaic flow), surfaced as a new hidden status alongside loading / ready. The delete controller reads the permission via useSession().session.checkAuthorization({ permission: 'org:sys_profile:delete' }) — the same thing legacy useProtect does internally, kept inside the mosaic boundary (only @clerk/shared/react).
  • The wrappers render nothing until ready (if (controller.status !== 'ready') { return null; }). They no longer render SectionSkeleton: during the load window the controller can't tell a permitted user from a gated one, so a skeleton would flash then vanish for gated users. Rendering nothing until ready matches legacy exactly.
  • Controllers now declare explicit return-type unions instead of per-return as const, so TypeScript checks every return arm against the contract.

SectionSkeleton (and the Skeleton primitive it alone used) are now unused; left in place as design-system building blocks.

Changeset

Empty — mosaic is not on the @clerk/ui public export surface, so there's no user-facing change to the published package.

Summary by CodeRabbit

  • New Features

    • Organization delete and leave sections now wait for all required data before showing actions, improving loading behavior.
    • Delete and leave options are hidden unless the current user is allowed to perform them.
  • Bug Fixes

    • Prevented these sections from briefly collapsing into hidden state while data is still loading.
    • Removed loading placeholders in favor of showing nothing until the action is actually available.
  • Documentation

    • Updated architecture guidance to match the new display behavior.
  • Tests

    • Expanded coverage for loading, hidden, and ready states across organization actions.

…in-delete

Mirror the legacy OrganizationGeneralPage gating in the mosaic delete/leave
sections: the delete section only renders when the user has the
org:sys_profile:delete permission and organization.adminDeleteEnabled is true;
the leave section only renders when there is an active organization membership.

The gating lives in the controllers (the only Clerk-aware layer), surfaced as a
new 'hidden' status. The wrappers render nothing until 'ready', matching legacy
and avoiding a skeleton that would flash then vanish for gated users. Controllers
now declare explicit return-type unions instead of per-return `as const`.
@changeset-bot

changeset-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 11d1f3d

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 Jun 30, 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 Jun 30, 2026 11:53am
swingset Ready Ready Preview, Comment Jun 30, 2026 11:53am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ce36f05-a966-4256-a7fe-a2a777fc3856

📥 Commits

Reviewing files that changed from the base of the PR and between 727b618 and 11d1f3d.

📒 Files selected for processing (6)
  • packages/ui/src/mosaic/sections/__tests__/delete-organization.controller.test.tsx
  • packages/ui/src/mosaic/sections/__tests__/delete-organization.machine.test.ts
  • packages/ui/src/mosaic/sections/__tests__/delete-organization.view.test.tsx
  • packages/ui/src/mosaic/sections/__tests__/leave-organization.controller.test.tsx
  • packages/ui/src/mosaic/sections/__tests__/leave-organization.machine.test.ts
  • packages/ui/src/mosaic/sections/__tests__/leave-organization.view.test.tsx
💤 Files with no reviewable changes (6)
  • packages/ui/src/mosaic/sections/tests/leave-organization.machine.test.ts
  • packages/ui/src/mosaic/sections/tests/leave-organization.view.test.tsx
  • packages/ui/src/mosaic/sections/tests/delete-organization.view.test.tsx
  • packages/ui/src/mosaic/sections/tests/delete-organization.machine.test.ts
  • packages/ui/src/mosaic/sections/tests/delete-organization.controller.test.tsx
  • packages/ui/src/mosaic/sections/tests/leave-organization.controller.test.tsx

📝 Walkthrough

Walkthrough

Controllers for DeleteOrganization and LeaveOrganization now return a typed discriminated union (loading | hidden | ready). The delete controller adds session-based permission gating (org:sys_profile:delete) and an adminDeleteEnabled check. Both section components render null unless status is ready, removing the SectionSkeleton fallback. Module files are renamed to dot-separated conventions.

Changes

Org Section Controller Gating

Layer / File(s) Summary
LeaveOrganization controller states and render guard
packages/ui/src/mosaic/sections/leave-organization.controller.tsx, packages/ui/src/mosaic/sections/leave-organization.tsx, packages/ui/src/mosaic/sections/leave-organization.view.tsx, packages/swingset/src/stories/leave-organization.stories.tsx
LeaveOrganizationController union distinguishes undefined membership (loading) from null (hidden). Component returns null unless ready. View and story imports updated to .machine/.view paths.
DeleteOrganization authorization gating and render guard
packages/ui/src/mosaic/sections/delete-organization.controller.tsx, packages/ui/src/mosaic/sections/delete-organization-controller.tsx, packages/ui/src/mosaic/sections/delete-organization.tsx, packages/ui/src/mosaic/sections/delete-organization.view.tsx, packages/swingset/src/stories/delete-organization.stories.tsx
New delete-organization.controller.tsx introduces DeleteOrganizationController union and gates readiness on org:sys_profile:delete permission and adminDeleteEnabled. Old controller updated with matching typed union. Component returns null unless ready.
Controller and module-path test updates
packages/ui/src/mosaic/sections/__tests__/delete-organization.controller.test.tsx, packages/ui/src/mosaic/sections/__tests__/leave-organization.controller.test.tsx, packages/ui/src/mosaic/sections/__tests__/delete-organization.machine.test.ts, packages/ui/src/mosaic/sections/__tests__/delete-organization.view.test.tsx, packages/ui/src/mosaic/sections/__tests__/leave-organization.machine.test.ts, packages/ui/src/mosaic/sections/__tests__/leave-organization.view.test.tsx
Controller mocks made configurable per-test. Harnesses render actual controller.status. New test cases cover loading/hidden/ready states. Machine and view test imports updated to new module paths.
Docs and changeset
references/mosaic-architecture.md, .changeset/mosaic-org-section-gating.md
Architecture doc updated to show status !== 'ready' null guard. Changeset entry added.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • clerk/javascript#9017: Modifies the same useDeleteOrganizationController and useLeaveOrganizationController hooks, with overlapping changes to Clerk hook integration and controller state flow.

Poem

🐇 Hop hop, the sections now hide when they should,
No skeleton flickers where nothing is good.
Permission checked, adminDeleteEnabled true —
Only the ready ones render for you.
The modules renamed with a dot in between,
The cleanest of controllers this warren has seen!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 clearly summarizes the main change: permission-based gating for mosaic organization sections.
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

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

@pkg-pr-new

pkg-pr-new Bot commented Jun 30, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

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

@clerk/electron-passkeys

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

@clerk/eslint-plugin

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 11d1f3d

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-30T11:54:51.274Z

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 11d1f3d.

@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: 3

🤖 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 @.changeset/mosaic-org-section-gating.md:
- Around line 1-2: Add a real changeset entry for the published `@clerk/ui`
package instead of keeping this placeholder empty. Update the changeset in the
.changeset file to describe the shipped packages/ui behavior change so
versioning and changelog generation include it, and make sure the entry is
associated with `@clerk/ui` rather than leaving only the frontmatter marker.

In `@packages/ui/src/mosaic/sections/delete-organization-controller.tsx`:
- Around line 19-20: The authorization check in DeleteOrganizationController is
running before useSession() has finished loading, so the transient undefined
session state is treated as denied. Update the logic that derives the hidden/not
allowed state to first respect the session loading flag from useSession()
(alongside useOrganization()), and only evaluate authorization after loading is
complete. Add a regression test covering the isLoaded: false with session ===
undefined case to ensure the controller does not hide prematurely.

In `@packages/ui/src/mosaic/sections/leave-organization-controller.tsx`:
- Around line 36-41: The `useLeaveOrganizationController` logic is treating an
unresolved `membership === undefined` as `hidden` when `isLoaded` is already
true, which prematurely hides the leave section during Clerk hydration. Update
the controller’s branching so `hidden` is only returned for an explicit `null`
membership, while `undefined` continues to return `loading`, and keep the
existing `organization` check aligned with that behavior. Also add or adjust the
tests for `useLeaveOrganizationController` to cover the transient `organization`
present / `membership undefined` case and verify it stays in `loading`.
🪄 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: 2e0945d6-f73d-489a-93b6-6a75a0b1a6e9

📥 Commits

Reviewing files that changed from the base of the PR and between c5697d7 and 3dcba2c.

📒 Files selected for processing (7)
  • .changeset/mosaic-org-section-gating.md
  • packages/ui/src/mosaic/sections/__tests__/delete-organization-controller.test.tsx
  • packages/ui/src/mosaic/sections/__tests__/leave-organization-controller.test.tsx
  • packages/ui/src/mosaic/sections/delete-organization-controller.tsx
  • packages/ui/src/mosaic/sections/delete-organization.tsx
  • packages/ui/src/mosaic/sections/leave-organization-controller.tsx
  • packages/ui/src/mosaic/sections/leave-organization.tsx

Comment thread .changeset/mosaic-org-section-gating.md
Comment thread packages/ui/src/mosaic/sections/delete-organization-controller.tsx Outdated
Comment thread packages/ui/src/mosaic/sections/leave-organization-controller.tsx Outdated
…osaic org sections

A still-loading session (delete) or unresolved membership (leave) was being
collapsed into 'hidden' instead of 'loading'. Gate the delete permission check
on the session being loaded, and treat membership === undefined as loading while
reserving 'hidden' for an explicit null.
@kylemac

kylemac commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@alexcarpenter (minor thing. i dont feel strongly) but wdyt about leave-organization.controller.tsx and leave-organization.controller.test.tsx

Rename the delete/leave section slices to <name>.<layer> (.machine/.controller/.view),
update the wrappers, swingset stories, and the mosaic-architecture file-shape doc
(also refreshing the stale wrapper example to render null until ready).
@alexcarpenter alexcarpenter merged commit be5588b into main Jun 30, 2026
53 checks passed
@alexcarpenter alexcarpenter deleted the feat/mosaic-org-section-gating branch June 30, 2026 11:55
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.

3 participants