Skip to content

chore(ui): Layer architecture for <ConfigureSSO />#8493

Open
iagodahlem wants to merge 30 commits intomainfrom
iago/configure-sso-layers
Open

chore(ui): Layer architecture for <ConfigureSSO />#8493
iagodahlem wants to merge 30 commits intomainfrom
iago/configure-sso-layers

Conversation

@iagodahlem
Copy link
Copy Markdown
Member

@iagodahlem iagodahlem commented May 6, 2026

Summary

Refactors the <__experimental_ConfigureSSO /> UI surface into a layered architecture that separates the navigation primitive (Wizard) from the layout primitives (Step, Stepper, ProfileCard) from the per-step content. No public component API change — the <__experimental_ConfigureSSO /> export keeps its current shape.

Why

The wizard primitive that landed in #8468 ships a working declarative API plus an imperative useRegisterContinueAction registry, but it owns its own chrome (Header, Footer, Body), couples step gating to a wizard-wide FooterActionsContext, and runs URL routing through Switch / Route. That made the breadcrumb non-reusable, blocked per-step footer customization, and required a deepestWizard workaround to dispatch correctly across nested wizards.

This PR reshapes the surface so each layer has a single job:

  • Wizard — pure state machine. No chrome, no Switch / Route, no URL coupling. Steps register themselves via useLayoutEffect and render self when active. goNext / goPrev bubble through nested wizards naturally.
  • Stepper — purely presentational step indicator. Each <Stepper.Item> is a self-rendering button + bullet + label. <Stepper.Skeleton> mirrors it for the loading state.
  • Step — compound layout primitives (Step.Header, Step.Body, Step.Section, Step.Counter, Step.Footer with Footer.Previous / Footer.Continue). All UI-only, no wizard coupling — the same primitives drive the live shell and the skeleton.
  • ProfileCard — semantic <header> / <footer> chrome with the consistent border + padding the surface uses.

The host (ConfigureSSO.tsx) wires the layers together. Each step file is short, opinionated about its own behavior, and assembles the primitives from above.

What changed

New primitives at packages/ui/src/components/ConfigureSSO/elements/:

  • Wizard/ — state-driven, children-rendering wizard with an optional initialStepId + Wizard.Step registration via useLayoutEffect. Drops the previous routing layer entirely.
  • Stepper/ — declarative <Stepper> + <Stepper.Item> + <Stepper.Skeleton>. The container only handles flex layout + separator insertion between items; current/completed/reachable computation moves to the host.
  • Step.tsx — the <Step> layout wrapper plus the compound members listed above. The Footer button compounds (Step.Footer.Previous, Step.Footer.Continue) take their own onClick / isDisabled / isLoading props — no useWizard inside, which is why the same Footer primitives work in ConfigureSSOSkeleton.
  • ProfileCard.tsx — header / footer chrome.

Surface-level wiring:

  • ConfigureSSO.tsx adopts the new shape end-to-end. The inline ProfileCard + NavBar layout collapses into composed primitives. The wizard's old internal chrome is gone.
  • ConfigureSSOHeader reads useWizard() and maps the active steps into <Stepper.Item>s; it also filters select-provider out of the breadcrumb by id.
  • Each step file destructures useWizard() directly, owns its own <Step.Footer> with explicit goPrev / goNext handlers, and renders its body via Step.Body + Step.Section.
  • SelectProviderStep boilerplate is added as the first wizard step. It's filtered out of the breadcrumb in ConfigureSSOHeader by id (no hidden flag on the wizard primitive — kept the primitive clean). Real provider-tile UI lands separately.
  • The previous FooterActionsContext registry (push / pop / deepestWizard plumbing) is gone — per-step footer rendering removes the need for it.

Out of scope — planned follow-ups

  • Real Select Provider tile UI + FlowProvider.provider state
  • deriveInitialStep(connection) and wiring to Wizard.initialStepId
  • Per-step query lifecycle (Test step's runs, Configure step's POST result handling)
  • Reset connection + Configure-again flows
  • Element-descriptor sweep — // TODO: add descriptor comments mark the placements
  • Localization keys for Previous / Continue / Step X / Y

Test plan

  • Sandbox renders <__experimental_ConfigureSSO /> end-to-end with the placeholder content
  • Breadcrumb shows 4 visible items (Verify domain / Configure / Test / Confirmation). Select Provider doesn't appear in the breadcrumb at any time
  • When on Select Provider, no breadcrumb bullet is rendered as current. Previous is disabled, Continue is enabled
  • When on Verify Domain with the ProvideEmail conditional sub-step hidden (user has a verified primary email), Step.Counter doesn't render (single inner step → badge auto-hides)
  • Continue / Previous navigate within nested wizards, then bubble to the outer wizard at the inner-last-step boundary
  • Initial loading shows ConfigureSSOSkeleton once on cold start; subsequent re-fetches don't re-trigger the full skeleton

Type of change

  • 📖 Refactoring

@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

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

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment May 7, 2026 6:38pm

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 6, 2026

🦋 Changeset detected

Latest commit: 80a1933

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

This PR includes changesets to release 2 packages
Name Type
@clerk/ui Patch
@clerk/chrome-extension Patch

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

iagodahlem added 16 commits May 7, 2026 14:42
…rActions context

Adds a parallel wizard primitive at packages/ui/src/components/ConfigureSSO/elements/Wizard/,
copied verbatim from packages/ui/src/components/ConfigureSSO/wizard/, then refactored:

- Renamed exports from ConfigureSSOWizard / useConfigureSSOWizard to Wizard / useWizard.
- Split FooterActionsContext from WizardContext. WizardContext owns navigation only
  (activeSteps, currentStep, goNext, goPrev, isLoading). FooterActionsContext owns
  step-action registration (continueAction, setContinueAction, chrome stack registry).
- Stripped UI from the Wizard root: removed Header, Footer, StepIndicator, Body, and
  helpers. Wizard now provides context + step extraction + active-step rendering only.
  Header and Footer will be added back as ConfigureSSO layout components in a follow-up.
- Preserved nested-wizard URL scoping via Option (a): the Wizard root keeps a Switch
  with a Route per step, so each Route children get a scoped RouteContext for nested
  wizards. Step itself stays a null-returning descriptor (its children are harvested
  by extractSteps and rendered inside the Switch). This stays close to the existing
  shape and avoids requiring a free-floating Route from Step.

Laura's existing wizard/ folder is untouched and still in use by ConfigureSSO.tsx.
The new elements/Wizard/ is dormant until ConfigureSSO.tsx switches over in a later
commit.
The Wizard primitive renders step children directly inside each Route, with
no Col wrapper and no element descriptors. Layout sizing belongs to the
host's ConfigureSSO layout component, and per-step descriptors belong to
each step's own root element — neither is the wizard's concern.
Extracts the numbered breadcrumb rendering from the existing wizard
Header into a presentational primitive at packages/ui/src/components/
ConfigureSSO/elements/Breadcrumbs/. The primitive takes items,
currentIndex, and onItemClick — no awareness of the wizard or its
context. Wires existing element descriptors (configureSSOWizardHeader*)
through to the same DOM elements they covered in the old Header.
Replaces the items/currentIndex prop pair with `<Breadcrumbs.Item>`
children and a `currentId` prop on the parent. Mirrors the pattern
used by `<Wizard>` / `<Wizard.Step>` in the same component folder.
Breadcrumbs walks its children via an extractItems helper (with
fragment support) and computes the active index from `currentId`
internally. Reachability logic, element descriptors, and the visual
rendering are unchanged.
Adds three new top-level components under packages/ui/src/components/
ConfigureSSO/ that consume the wizard primitive and the breadcrumbs
primitive:

- ConfigureSSOLayout: ProfileCard shell with the navbar sidebar and a
  body Col that owns the flex sizing for the wizard's step output.
- ConfigureSSOHeader: thin wrapper that drives the declarative
  Breadcrumbs primitive from the wizard's active steps + currentStep.
- ConfigureSSOFooter: shared Previous / Continue footer that
  dispatches to the deepest mounted wizard via the FooterActions
  context registry.

Nothing wires these in yet — that swap lands in a follow-up commit.
Removes the shared StepLayout wrapper from each step file and inlines
the header (Heading + subtitle) plus body Col directly. Each step now
owns its own visual chrome:

- ProvideEmailStep, VerifyDomainStep, ConfigureCreateAppStep,
  TestConfigurationStep render their own header row and body.
- ConfirmationStep drops the header entirely per the Figma design.

Step files also switch wizard hook imports from the old wizard/ folder
to the new elements/Wizard/ primitive, and each step attaches its own
configureSSOWizardBody descriptor at its outermost rendered element.

Flow.Part does not accept elementDescriptor / elementId props, so each
step uses the fallback shape: a Col wrapper inside Flow.Part carries
the descriptor. The wrapper Col keeps flex: 1 / minHeight: 0 so the
inner body Col's flex: 1 / overflowY: auto continues to size against
the layout's flex column context.

useRegisterContinueAction usage is unchanged in this commit; the move
to a Wizard.Step canContinue prop lands separately. The shared
StepLayout file is left on disk (no more imports) and gets removed in
a final cleanup commit.
Wires the new ConfigureSSO surface into the layered components shipped
in the previous commits:

- ConfigureSSO.tsx adopts the new shape — Flow.Part wrap inside
  Flow.Root for convention parity with OrganizationProfile, an initial
  load gate at the root that returns a centered spinner inside
  ConfigureSSOLayout, and a FooterActionsProvider wrap around the
  layout. The inline ProfileCard / NavBar / Col chrome moves out;
  ConfigureSSOLayout owns it now.
- ConfigureSSOSteps switches its wizard imports from the old wizard/
  folder to the new elements/Wizard/ primitive. The declarative step
  tree shape is preserved.
- isLoading is lifted out of ConfigureSSOFlowContext and out of the
  wizard's context value. The root-level gate handles initial loading;
  subsequent re-fetches no longer flip the wizard into a loading state
  in context. ConfigureSSOFooter drops the isLoading-driven disable.
- The Wizard primitive no longer imports useConfigureSSOFlow — it's a
  fully generic primitive at the navigation layer now.
- The unused legacy wizard folder also drops its isLoading reads
  (spinner branch in Body, skeleton in Header, force-disable in
  Footer) so the typechecker stays clean while it sits on disk.

The old wizard/ folder is unused after this commit but stays on disk
for a final cleanup commit.
Replaces the route-based wizard primitive with a state-driven version:

- Wizard renders {children} directly. Header, Footer, and step
  descriptors all render as siblings inside the wizard scope. The
  Switch / Route / useRouter routing layer is gone.
- Wizard.Step is a self-rendering component now: it registers itself
  with the parent wizard via useLayoutEffect on mount, then renders
  its children only when its id matches the wizard's currentStep.
  Inactive steps render null.
- Active-step state is internal — first registered step becomes the
  default; goNext / goPrev / goToStep mutate that state. Nested
  wizards still bubble to parent.goNext on inner-last-step continue.
- Step descriptors no longer carry a path; navigation is purely by id.
- Footer now reads the deepest wizard's value from reactive context
  state (not a ref), so navigation inside a nested wizard re-renders
  the Footer and isFirstStep / isLastStep stay in sync.

Also picks up earlier in-flight work in ConfigureSSO: rename
ConfigureSSOLayout to ConfigureSSOCard (the inner Col flex wrapper
was dropped — the body Col on the card scrollbox owns sizing now),
and Header / Steps / Footer mount as siblings inside the wizard
scope so the breadcrumb and footer chrome render alongside the
active step body.
Splits the navbar chrome into ConfigureSSONavbar so the layout
composition is more readable. Adds a ConfigureSSOSkeleton that
mirrors the live shell — breadcrumb placeholders, centered content
spinner, disabled footer buttons — used by the initial-load gate.

Drops the orphaned ConfigureSSOCard (replaced by inline
ProfileCard.Root + the new navbar component). Adjusts breadcrumb
bullet sizing and the separator caret to match the latest visual
direction.
- Add elements/ProfileCard with ProfileCardHeader + ProfileCardFooter
  primitives (semantic <header>/<footer>, border + padding chrome).
- ConfigureSSOHeader, ConfigureSSOFooter, and ConfigureSSOSkeleton
  wrap their content in the new primitives instead of inlining the
  Flex chrome.
- Breadcrumbs primitive trimmed: outer Flex no longer renders its
  own border / padding (lives in ProfileCardHeader now). Bullet
  refined — neutralAlpha400 background, foreground-on-completed
  label color, white check, number wrapped in Text for color
  contrast. Descriptor placement flagged with TODOs for the
  cleanup sweep.
- Wrap the wizard tree in ConfigureSSOFlowProvider so steps can
  read enterpriseConnection state.
- VerifyDomainStep's placeholder isDisabled commented out for
  manual step-navigation testing until verification ships.
Renames the wizard's step-indicator primitive to its conventional
name (Stepper). Strips state derivation from the primitive so it's
purely presentational:

- elements/Breadcrumbs/* moves to elements/Stepper/*. Types are
  renamed accordingly; the BreadcrumbsActiveItem internal type is
  dropped.
- The Stepper container no longer walks its children to extract
  descriptors — children-as-array is used only to insert the
  chevron separator between items.
- Stepper.Item becomes a fully self-rendering component. It takes
  label, bullet (ReactNode — caller passes the number or check
  icon), isCurrent, isCompleted, isReachable, and onClick. The
  primitive doesn't compute reachability or pick the bullet shape.
- ConfigureSSOHeader takes ownership of the wizard → stepper
  mapping: reads useWizard() for activeSteps + currentStep,
  computes per-item flags, decides whether to render the check or
  the index number in the bullet, and wires onClick to goToStep.

Visual rendering is identical. Descriptor placement is left for the
final descriptor sweep (todo comments preserved where Iago left
them).
Extends the elements/ProfileCard.tsx primitive set:

- ProfileCardBody: <main>-semantic scrollable container that sits
  between header and footer in the card layout. Adds flex sizing
  and overflowY:auto.
- ProfileCardSection: titled-or-untitled content block. Optionally
  takes localized title + subtitle. Multiple sections can stack
  inside a single ProfileCardBody. Confirmation step uses the
  no-title shape per Figma.

Refactors all five ConfigureSSO step files to render their body
content via these primitives instead of inlining the Heading +
subtitle + body Col chrome. Each step is now noticeably shorter and
the visual chrome lives in one place.

Per-step descriptor wrapping (configureSSOWizardBody) is left in
place — descriptor placement migration is a separate cleanup pass.
Adds a compound Skeleton on the Stepper that renders a placeholder
stepper given a totalSteps count. Each placeholder item mirrors the
real Stepper.Item layout (bullet + label) with washed-out
neutralAlpha100 backgrounds, with a same-tone chevron separator
between items.

ConfigureSSOSkeleton drops its inline BreadcrumbItemSkeleton and
BreadcrumItemIconSkeleton helpers in favor of
<Stepper.Skeleton totalSteps={4} />. The two skeleton helpers were
internalized to the Stepper module — they're not exported.
…ining steps

- Renames steps/StepLayout.tsx to steps/Step.tsx and compounds the
  four primitives under a single Step namespace: Step is the layout
  wrapper, Step.Section is the padded content block, Step.Header is
  the section variant with the bottom-border title row, Step.Body is
  the section variant for the scrollable main area. Mirrors the
  Stepper.Item / Stepper.Skeleton pattern.
- Fixes sx-merging in Step / Step.Section / Step.Body by adopting
  the codebase's array-form pattern (sx={[default, userSx]}). The
  previous spread-into-object approach broke when a function-form sx
  was passed and Step.Body silently dropped user sx entirely.
- Refactors ProvideEmailStep, ConfigureCreateAppStep,
  TestConfigurationStep, and ConfirmationStep to the same shape
  VerifyDomainStep already uses: <Step><Step.Header /><Step.Body /></Step>
  with the per-step descriptor on the outer Step wrapper.
  Confirmation drops the header per Figma; Body provides its own
  padding.
- Promotes the Step compound primitive to packages/ui/src/components/
  ConfigureSSO/elements/Step.tsx — it lives alongside the other
  shared step-level primitives (Stepper, ProfileCard) instead of
  inside the steps/ folder.
- Updates all five step files to import Step from ../elements/Step.
- Drops ProfileCardBody and ProfileCardSection from
  elements/ProfileCard.tsx — those responsibilities now live on
  Step.Body and Step.Section, so the ProfileCard module is back to
  just Header and Footer chrome.
- ConfigureSSOSkeleton calls Stepper.Skeleton without an explicit
  totalSteps; the default of 4 is used.
Replace flat `colorMutedForeground` background with the shared `common.mutedBackground` helper for visual consistency with other muted surfaces.
iagodahlem added 10 commits May 7, 2026 14:42
Each step now renders its own <Step.Footer />. The shared
ConfigureSSOFooter and the FooterActionsContext registry that backed
it (continueAction state, the deepest-mounted-wizard stack with
push/pop/publishWizardValue, and useRegisterContinueAction) are all
gone.

The Wizard primitive simplifies accordingly: no more wrapping the
outermost wizard in FooterActionsProvider, no more registering each
wizard's value into a chrome stack. useWizard().goNext already
handles nested fall-through via parentWizard?.goNext, so a step
rendered inside a nested wizard has its <Step.Footer />'s
useWizard() naturally point at the right wizard — no out-of-band
ref needed.

Step.Footer is added as a compound member of Step in
elements/Step.tsx alongside Section/Header/Body. It reads useWizard
for sane defaults (goNext, goPrev, isFirstStep, isLastStep) and
accepts override props for the cases that need them
(continueDisabled, continueLoading, onContinue, hidePrevious,
etc.).

All five step files updated. Confirmation uses
<Step.Footer hideContinue /> since it's the last step.
ConfigureSSOFooter and elements/Wizard/FooterActionsContext are
deleted.
Keeps the footer at a consistent height even when its buttons are
hidden or replaced — useful for step-specific footer compositions
that may render different button sets per step.
Replaces the prop-heavy Step.Footer (with previousLabel /
continueLabel / hidePrevious / hideContinue / *Disabled / *Loading /
on*) with a compound primitive set:

- Step.Footer is now a thin wrapper around ProfileCardFooter that
  accepts arbitrary children.
- Step.Footer.Previous and Step.Footer.Continue are purely
  presentational buttons (no useWizard, no defaults). Each accepts
  onClick, isDisabled, isLoading, and optional label.

Each step destructures useWizard at the top and wires goNext /
goPrev / isFirstStep / isLastStep into the buttons explicitly.
Confirmation drops the Continue button entirely. ConfigureSSOSkeleton
also adopts Step.Footer with both buttons disabled — the skeleton
now mirrors the live shell using the same primitives, with no
wizard context required.

The whole footer surface is reusable outside the wizard now and
shrinks dramatically at the call site.
The verify-email-domain step now has a single outer wrapper that
owns the Flow.Part + Step + Step.Header chrome. The two inner
sub-steps (ProvideEmail and VerifyEmailAddress) are content-only
(Step.Body + Step.Footer) and share the outer header through an
inner Wizard. Step.Header sits inside the inner Wizard so its
descendants — specifically the new Step.Counter — see the inner
wizard's state.

- VerifyDomainStep.tsx is renamed to VerifyEmailAddressStep.tsx to
  free the outer name and clarify what the inner content actually
  does (verify the user's email address as proof of domain
  ownership). Both inner files now render only the body + footer
  for their sub-screen.
- New VerifyEmailDomainStep.tsx is the outer step. It reads the
  user's primary email address, conditionally renders the
  ProvideEmail sub-step, and always renders the
  VerifyEmailAddressStep sub-step.
- ConfigureSSO.tsx drops its useUser read and the inline inner
  Wizard for verify-email-domain — the outer step encapsulates
  both. The Configure outer step keeps its inline inner Wizard for
  now (single sub-step there).

Adds Step.Counter as a pure-UI compound member (takes total +
current numbers, auto-hides when total <= 1). Step.Header now
positions its children as the right-side slot of the title row,
matching the original Laura layout.
Reorganizes the steps directory and rebalances responsibilities
between Step.Body and Step.Section:

- The verify-domain wrapper, ProvideEmailStep, and the inner
  email-verification screen all live in a single VerifyDomainStep.tsx
  file. The renamed inner screen is EnterVerificationCodeStep — its
  job is the actual code entry; the wrapper handles the shared header
  and provider routing.
- The configure outer step is consolidated into a single ConfigureStep
  component (no inner wizard yet — it's a single screen for now).
  ConfigureCreateAppStep is gone.
- Step.Body becomes a scrollable container with no padding — pure
  flex+scroll wrapper. Steps that want padded content add a
  Step.Section inside Body. ConfirmationStep and
  TestConfigurationStep adopt the new Step.Body + Step.Section shape.
- ConfigureSSO.tsx updates the wizard tree: outer step ids are
  verify-domain / configure / test / confirmation. The verify-domain
  step renders the new VerifyDomainStep wrapper directly; configure
  step renders ConfigureStep directly (no inline inner wizard).
…ections

Adds a separate Step.Section for the test SSO URL with a bottom border divider, and updates the placeholder text in the existing section to indicate test results.
Wrap wizard steps in a single Step.Body at the VerifyDomainStep level
instead of duplicating Step.Body inside each inner step. Inner steps
(ProvideEmail, EnterVerificationCode) now render Step.Section directly
with flex/center alignment, matching the layout used in ConfirmationStep.
Adds the Select Provider step at the top of the ConfigureSSO wizard
tree. The step is structurally a regular wizard step — same Step
chrome, same goNext / goPrev mechanics, same per-step descriptor —
but ConfigureSSOHeader filters it out of the breadcrumb by id so it
doesn't appear in the Stepper. When the user is on Select Provider,
no breadcrumb bullet is highlighted (currentIndex resolves to -1
against the filtered list, all items render muted) — matches the
design intent.

The step body is placeholder UI for now. The provider tile selection,
the FlowProvider integration, and the initial-step derivation all
land separately when we actually build the Select Provider screen.
The wizard now accepts an optional initialStepId prop. When passed,
that becomes the active step on mount; when omitted, the existing
"first registered step becomes default" behavior is preserved.

This is the hook the host needs to land server-derived initial step
logic later — e.g., ConfigureSSO computing the initial step from
the user's enterprise connection state. Hosts that don't pass it
work unchanged.
Removes the trailing period from the description text in the SelectProvider and VerifyDomain wizard steps for consistency.
Removes the ConfigureSSOSteps wrapper and renders the Wizard.Step children directly inside the Wizard. Also sets the initial step to `configure` so the wizard opens on the configuration step.
@iagodahlem iagodahlem changed the title Iago/configure sso layers refactor(ui): layered architecture for ConfigureSSO wizard May 7, 2026
@iagodahlem iagodahlem changed the title refactor(ui): layered architecture for ConfigureSSO wizard chore(ui): Layer architecture for <ConfigureSSO /> May 7, 2026
Adds a patch-level changeset for @clerk/ui covering the layered
primitive refactor on <__experimental_ConfigureSSO />. Also reverts
the sandbox template's local-dev publishable key back to the
original value tracked in main.
iagodahlem added 2 commits May 7, 2026 15:36
Delete the wizard subdirectory under ConfigureSSO, including ConfigureSSOWizard, ConfigureSSOWizardContext, types, and index, now that the wizard steps have been inlined into the parent component.
Let the Wizard fall back to its default first step instead of forcing the `configure` step on mount.
@iagodahlem iagodahlem marked this pull request as ready for review May 7, 2026 18:38
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 7, 2026

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8493

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 80a1933

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR refactors the experimental <__experimental_ConfigureSSO /> component from a monolithic ConfigureSSOWizard-based architecture into composable, layered primitives. A new Wizard component manages step state and navigation; a presentational Stepper renders the step breadcrumb (filtering out a hidden SelectProviderStep); a Step compound component encapsulates step layout with Header, Body, Footer, and Counter subparts; and ProfileCard elements provide semantic header/footer chrome. The flow context is simplified to remove isLoading, placing responsibility for loading states at the component level. All step implementations are refactored to use the new primitives and useWizard() hook for navigation, and the old StepLayout and ConfigureSSOWizard are removed.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 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
Title check ✅ Passed The title 'chore(ui): Layer architecture for <ConfigureSSO />' clearly and specifically describes the main refactoring work: reorganizing the ConfigureSSO component into a layered architecture separating navigation, layout, and content concerns.
Description check ✅ Passed The description comprehensively explains the refactoring rationale, new architecture layers (Wizard, Stepper, Step, ProfileCard), specific changes made, out-of-scope items, and test plan. It is directly related to the changeset and provides meaningful context.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/components/ConfigureSSO/elements/ProfileCard.tsx`:
- Line 4: The type alias ProfileCardHeaderProps uses React.PropsWithChildren but
the React namespace isn't imported; import PropsWithChildren from 'react' and
update the type to use PropsWithChildren (or keep React.PropsWithChildren but
ensure React is imported) so the ProfileCardHeaderProps definition and any
usages compile without TS2304; search for ProfileCardHeaderProps and the
ProfileCard component in ProfileCard.tsx to apply the import and type fix.
🪄 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

Run ID: c35dd481-dca7-44f9-9bb6-4cacc4aef0f0

📥 Commits

Reviewing files that changed from the base of the PR and between 4c44aa6 and 80a1933.

📒 Files selected for processing (29)
  • .changeset/wizard-architecture-layers.md
  • packages/ui/src/components/ConfigureSSO/ConfigureSSO.tsx
  • packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx
  • packages/ui/src/components/ConfigureSSO/ConfigureSSOHeader.tsx
  • packages/ui/src/components/ConfigureSSO/ConfigureSSONavbar.tsx
  • packages/ui/src/components/ConfigureSSO/ConfigureSSOSkeleton.tsx
  • packages/ui/src/components/ConfigureSSO/elements/ProfileCard.tsx
  • packages/ui/src/components/ConfigureSSO/elements/Step.tsx
  • packages/ui/src/components/ConfigureSSO/elements/Stepper/Stepper.tsx
  • packages/ui/src/components/ConfigureSSO/elements/Stepper/index.ts
  • packages/ui/src/components/ConfigureSSO/elements/Stepper/types.ts
  • packages/ui/src/components/ConfigureSSO/elements/Wizard/Wizard.tsx
  • packages/ui/src/components/ConfigureSSO/elements/Wizard/WizardContext.tsx
  • packages/ui/src/components/ConfigureSSO/elements/Wizard/index.ts
  • packages/ui/src/components/ConfigureSSO/elements/Wizard/types.ts
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureCreateAppStep.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfirmationStep.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ProvideEmailStep.tsx
  • packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx
  • packages/ui/src/components/ConfigureSSO/steps/StepLayout.tsx
  • packages/ui/src/components/ConfigureSSO/steps/TestConfigurationStep.tsx
  • packages/ui/src/components/ConfigureSSO/steps/VerifyDomainStep.tsx
  • packages/ui/src/components/ConfigureSSO/steps/index.ts
  • packages/ui/src/components/ConfigureSSO/wizard/ConfigureSSOWizard.tsx
  • packages/ui/src/components/ConfigureSSO/wizard/ConfigureSSOWizardContext.tsx
  • packages/ui/src/components/ConfigureSSO/wizard/index.ts
  • packages/ui/src/components/ConfigureSSO/wizard/types.ts
  • packages/ui/src/elements/contexts/index.tsx
💤 Files with no reviewable changes (7)
  • packages/ui/src/components/ConfigureSSO/wizard/ConfigureSSOWizardContext.tsx
  • packages/ui/src/components/ConfigureSSO/wizard/types.ts
  • packages/ui/src/components/ConfigureSSO/wizard/index.ts
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureCreateAppStep.tsx
  • packages/ui/src/components/ConfigureSSO/steps/StepLayout.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ProvideEmailStep.tsx
  • packages/ui/src/components/ConfigureSSO/wizard/ConfigureSSOWizard.tsx

import { descriptors, Flex } from '@/customizables';
import { common } from '@/styledSystem';

type ProfileCardHeaderProps = React.PropsWithChildren;
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.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if any other tsx/ts files in this package use React.PropsWithChildren without a React import
rg -l 'React\.PropsWithChildren' --type=ts --type=tsx | head -20

rg -n 'React\.PropsWithChildren' --type=ts 2>/dev/null | head -20
rg -n 'React\.PropsWithChildren' -g '*.tsx' | head -20

# Check if files that use React.PropsWithChildren also import React
rg -l 'React\.PropsWithChildren' -g '*.tsx' | xargs -I{} sh -c 'echo "=== {} ==="; head -5 {}'

Repository: clerk/javascript

Length of output: 19549


🏁 Script executed:

cat -n packages/ui/src/components/ConfigureSSO/elements/ProfileCard.tsx

Repository: clerk/javascript

Length of output: 1452


🏁 Script executed:

cat packages/ui/tsconfig.json

Repository: clerk/javascript

Length of output: 1134


🏁 Script executed:

cat packages/ui/src/global.d.ts

Repository: clerk/javascript

Length of output: 767


🏁 Script executed:

# Check if there's a React ambient global declaration or if `@types/react` has export as namespace
cat packages/ui/src/global.d.ts | head -20

# Check if there's a root tsconfig or other global.d.ts files
fd global.d.ts packages/

# Check package.json for `@types/react`
grep -A 5 -B 5 '@types/react' packages/ui/package.json

Repository: clerk/javascript

Length of output: 1232


🏁 Script executed:

cat packages/ui/package.json | grep -A 10 -B 2 '@types/react'

Repository: clerk/javascript

Length of output: 42


🏁 Script executed:

cat packages/ui/package.json | head -50

Repository: clerk/javascript

Length of output: 1491


🏁 Script executed:

cat packages/ui/package.json | grep -E '"(dependencies|devDependencies|peerDependencies)"' -A 20

Repository: clerk/javascript

Length of output: 1466


🏁 Script executed:

# Verify that TypeScript would error on this by checking if there's any build configuration that adds ambient React
# Also check the root tsconfig or any type roots
cat tsconfig.json 2>/dev/null | head -30 || echo "No root tsconfig"

Repository: clerk/javascript

Length of output: 732


🏁 Script executed:

# Let's verify that the code would actually fail to compile by checking if there's a build output or error
# Also check ConfigureSSONavbar.tsx to confirm it properly imports React
head -15 packages/ui/src/components/ConfigureSSO/ConfigureSSONavbar.tsx

Repository: clerk/javascript

Length of output: 699


Import PropsWithChildren from 'react' to fix TypeScript compilation error.

Lines 4 and 20 reference React.PropsWithChildren without importing the React namespace. The jsx: react-jsx setting in tsconfig only enables JSX syntax without importing React—it does not make React.* available as a namespace. This causes a TS2304: Cannot find name 'React' error.

🔧 Proposed fix
+import { type PropsWithChildren } from 'react';
 import { descriptors, Flex } from '@/customizables';
 import { common } from '@/styledSystem';
 
-type ProfileCardHeaderProps = React.PropsWithChildren;
+type ProfileCardHeaderProps = PropsWithChildren;
 
 // ...
 
-type ProfileCardFooterProps = React.PropsWithChildren;
+type ProfileCardFooterProps = PropsWithChildren;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type ProfileCardHeaderProps = React.PropsWithChildren;
type ProfileCardHeaderProps = PropsWithChildren;
🤖 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/components/ConfigureSSO/elements/ProfileCard.tsx` at line 4,
The type alias ProfileCardHeaderProps uses React.PropsWithChildren but the React
namespace isn't imported; import PropsWithChildren from 'react' and update the
type to use PropsWithChildren (or keep React.PropsWithChildren but ensure React
is imported) so the ProfileCardHeaderProps definition and any usages compile
without TS2304; search for ProfileCardHeaderProps and the ProfileCard component
in ProfileCard.tsx to apply the import and type fix.


return (
<Flex
// TODO: add descriptor
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do you think it's feasible to add descriptors on this PR? or on a follow up?

sx={theme => ({
gap: theme.space.$1x5,
padding: 0,
color: isCurrent || isCompleted ? theme.colors.$colorForeground : theme.colors.$colorMutedForeground,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we already introduce the "green mark" style for the step item here, if completed? Also can do in a follow up

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants