Skip to content

refactor(ui): Improve SelectOption consistency#8347

Open
alexcarpenter wants to merge 3 commits intomainfrom
carp/select-option-consistency
Open

refactor(ui): Improve SelectOption consistency#8347
alexcarpenter wants to merge 3 commits intomainfrom
carp/select-option-consistency

Conversation

@alexcarpenter
Copy link
Copy Markdown
Member

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 17, 2026

⚠️ No Changeset found

Latest commit: e8d7ae9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions github-actions Bot added the ui label Apr 17, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 17, 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 Apr 23, 2026 5:16pm

Request Review

@alexcarpenter alexcarpenter marked this pull request as ready for review April 23, 2026 17:15
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

This pull request introduces a new SelectOption compound component to the Select module and refactors multiple form and selection components across the codebase to use this standardized component instead of custom option rendering implementations. The changes consolidate repeated custom styling, icon handling, and layout logic from individual components (CreateAPIKeyForm, CheckoutForm, OrgSelect, MemberListTable, PhoneInput) into a reusable compound component with subcomponents for Image, Label, and Detail slots. Additionally, a prototype UI section demonstrating SelectOption rendering patterns is added to the SignInStart component.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 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 accurately summarizes the main change: refactoring SelectOption for consistency across multiple UI components.
Description check ✅ Passed The description contains only a template/checklist with no implementation details, but the PR objective and file summaries confirm the description relates to the changeset.
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.


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.

❤️ Share

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 the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/SignIn/SignInStart.tsx`:
- Around line 81-212: Remove the prototype demo by deleting the SelectOptionDemo
component and its invocation inside SignInStart, and also remove the now-unused
imports Select, SelectButton, SelectOption, SelectOptionList, and Text from the
top of the file; ensure SignInStart only renders the intended sign-in form and
run the linter/TS checks to clean up any remaining unused symbols or types (look
for SelectOptionDemo, its JSX usage in SignInStart, and import declarations that
mention Select/SelectButton/SelectOption/SelectOptionList/Text).
🪄 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: c1935b6d-1771-4e57-a7df-6c2823758f76

📥 Commits

Reviewing files that changed from the base of the PR and between e73d266 and e8d7ae9.

📒 Files selected for processing (7)
  • packages/ui/src/components/APIKeys/CreateAPIKeyForm.tsx
  • packages/ui/src/components/Checkout/CheckoutForm.tsx
  • packages/ui/src/components/OAuthConsent/OrgSelect.tsx
  • packages/ui/src/components/OrganizationProfile/MemberListTable.tsx
  • packages/ui/src/components/SignIn/SignInStart.tsx
  • packages/ui/src/elements/PhoneInput/index.tsx
  • packages/ui/src/elements/Select.tsx

Comment on lines +81 to +212
// PROTOTYPE: Remove before merging
const SelectOptionDemo = () => {
const [orgValue, setOrgValue] = useState<string | null>('org_1');
const [phoneValue, setPhoneValue] = useState<string | null>('us');
const [roleValue, setRoleValue] = useState<string | null>('admin');
const [defaultValue, setDefaultValue] = useState<string | null>('30d');

const orgOptions = [
{ value: 'org_1', label: 'Acme Corp', logoUrl: 'https://img.clerk.com/placeholder' },
{
value: 'org_2',
label: 'Globex Industries Globex Industries Globex Industries',
logoUrl: 'https://img.clerk.com/placeholder',
},
{ value: 'org_3', label: 'Initech', logoUrl: 'https://img.clerk.com/placeholder' },
];

const phoneOptions = [
{ value: 'us', label: 'United States', code: '1' },
{ value: 'gb', label: 'United Kingdom', code: '44' },
{ value: 'de', label: 'Germany', code: '49' },
{ value: 'fr', label: 'France', code: '33' },
];

const roleOptions = [
{ value: 'admin', label: 'Admin' },
{ value: 'member', label: 'Member' },
{ value: 'guest', label: 'Guest' },
];

const expirationOptions = [
{ value: '7d', label: '7 days' },
{ value: '30d', label: '30 days' },
{ value: '90d', label: '90 days' },
{ value: 'never', label: 'Never' },
];

return (
<Col
gap={4}
sx={{ padding: '16px 0' }}
>
<Text variant='subtitle'>SelectOption Variants (prototype)</Text>

<Text
colorScheme='secondary'
sx={{ fontSize: '12px' }}
>
Image + Label (OrgSelect pattern)
</Text>
<Select
options={orgOptions}
value={orgValue}
onChange={option => setOrgValue(option.value)}
matchTriggerWidth
renderOption={(option, _index, isSelected) => (
<SelectOption isSelected={isSelected}>
<SelectOption.Image
src={option.logoUrl}
alt={option.label}
/>
<SelectOption.Label>{option.label}</SelectOption.Label>
</SelectOption>
)}
>
<SelectButton>{orgOptions.find(o => o.value === orgValue)?.label || 'Select org'}</SelectButton>
<SelectOptionList />
</Select>

<Text
colorScheme='secondary'
sx={{ fontSize: '12px' }}
>
Label + Detail (PhoneInput pattern)
</Text>
<Select
options={phoneOptions}
value={phoneValue}
onChange={option => setPhoneValue(option.value)}
matchTriggerWidth
renderOption={(option, _index, isSelected) => (
<SelectOption isSelected={isSelected}>
<SelectOption.Label>{option.label}</SelectOption.Label>
<SelectOption.Detail>+{option.code}</SelectOption.Detail>
</SelectOption>
)}
>
<SelectButton>{phoneOptions.find(o => o.value === phoneValue)?.label || 'Select country'}</SelectButton>
<SelectOptionList />
</Select>

<Text
colorScheme='secondary'
sx={{ fontSize: '12px' }}
>
Label only (RoleSelect pattern)
</Text>
<Select
options={roleOptions}
value={roleValue}
onChange={option => setRoleValue(option.value)}
matchTriggerWidth
renderOption={(option, _index, isSelected) => (
<SelectOption isSelected={isSelected}>
<SelectOption.Label>{option.label}</SelectOption.Label>
</SelectOption>
)}
>
<SelectButton>{roleOptions.find(o => o.value === roleValue)?.label || 'Select role'}</SelectButton>
<SelectOptionList />
</Select>

<Text
colorScheme='secondary'
sx={{ fontSize: '12px' }}
>
Default renderer (Checkout/APIKeys pattern)
</Text>
<Select
options={expirationOptions}
value={defaultValue}
onChange={option => setDefaultValue(option.value)}
matchTriggerWidth
>
<SelectButton>
{expirationOptions.find(o => o.value === defaultValue)?.label || 'Select expiration'}
</SelectButton>
<SelectOptionList />
</Select>
</Col>
);
};
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

Remove prototype demo before merging.

SelectOptionDemo and its render site at line 708 are marked by the author as "PROTOTYPE: Remove before merging" and are unconditionally rendered inside the real SignInStart card. If this lands on main as-is, every integrator's Sign-In screen will display the demo selects above the actual form. Please strip the component, its usage, and the now-unused Select/SelectButton/SelectOption/SelectOptionList/Text imports (line 17 and Text on line 36) before marking the PR ready.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/SignIn/SignInStart.tsx` around lines 81 - 212,
Remove the prototype demo by deleting the SelectOptionDemo component and its
invocation inside SignInStart, and also remove the now-unused imports Select,
SelectButton, SelectOption, SelectOptionList, and Text from the top of the file;
ensure SignInStart only renders the intended sign-in form and run the linter/TS
checks to clean up any remaining unused symbols or types (look for
SelectOptionDemo, its JSX usage in SignInStart, and import declarations that
mention Select/SelectButton/SelectOption/SelectOptionList/Text).

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.

1 participant