-
Notifications
You must be signed in to change notification settings - Fork 467
feat(ui): add user profile account section #9380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3c99147
514824f
ff94412
3ce08ba
44b7772
b73694c
5622c9a
886d472
ea033f5
76faade
78a2532
30bf82a
eb86e9e
e33b63a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| --- | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| --- | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| --- | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| --- | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| --- | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import * as Stories from './user-profile-account-section.stories'; | ||
|
|
||
| # UserProfileAccountSection | ||
|
|
||
| Account details, profile image, email addresses, and phone numbers composed with `Section`. | ||
|
|
||
| <Story | ||
| name='Default' | ||
| storyModule={Stories} | ||
| composition={[ | ||
| { name: 'Section', href: '/components/section', layer: 'Components' }, | ||
| { name: 'Avatar', href: '/components/avatar', layer: 'Components' }, | ||
| ]} | ||
| /> | ||
|
Comment on lines
+3
to
+14
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift Add the required documentation sections. Add As per coding guidelines, “Playground / Props / Usage are mandatory and always in this order” and “Document the default value for every prop in a dedicated Default column.” 🧰 Tools🪛 LanguageTool[grammar] ~3-~3: Ensure spelling is correct (QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1) 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { UserProfileAccountSectionView } from '@clerk/ui/mosaic/user-profile/user-profile-account-section.view'; | ||
|
|
||
| import type { StoryMeta } from '@/lib/types'; | ||
|
|
||
| export { default as __source } from './user-profile-account-section.stories?raw'; | ||
|
|
||
| export const meta: StoryMeta = { | ||
| group: 'User', | ||
| title: 'UserProfileAccountSection', | ||
| source: 'packages/ui/src/mosaic/user-profile/user-profile-account-section.view.tsx', | ||
| }; | ||
|
|
||
| export function Default() { | ||
| return ( | ||
| <UserProfileAccountSectionView | ||
| emails={[ | ||
| { id: 'email_1', value: 'item1@clerk.dev', isDefault: true, isVerified: true }, | ||
| { id: 'email_2', value: 'item2@clerk.dev', isVerified: true }, | ||
| ]} | ||
| imageUrl='https://avatars.githubusercontent.com/u/51144033?v=4' | ||
| name='Preston Booth' | ||
| phones={[{ id: 'phone_1', value: '+1 801-888-8181', isDefault: true, isVerified: true }]} | ||
| username='prestonxyz' | ||
| onAddEmail={() => undefined} | ||
| onAddPhone={() => undefined} | ||
| onEditProfilePicture={() => undefined} | ||
| onManageEmail={() => undefined} | ||
| onManagePhone={() => undefined} | ||
| onNameChange={() => undefined} | ||
| onUsernameChange={() => undefined} | ||
| /> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import * as Stories from './user-profile-connected-accounts-section.stories'; | ||
|
|
||
| # UserProfileConnectedAccountsSection | ||
|
|
||
| Connected and available external identity providers with provider-specific actions. | ||
|
|
||
| <Story | ||
| name='Default' | ||
| storyModule={Stories} | ||
| composition={[ | ||
| { name: 'Section', href: '/components/section', layer: 'Components' }, | ||
| { name: 'Button', href: '/components/button', layer: 'Components' }, | ||
| { name: 'Icon', href: '/components/icon', layer: 'Components' }, | ||
| ]} | ||
| /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { UserProfileConnectedAccountsSectionView } from '@clerk/ui/mosaic/user-profile/user-profile-connected-accounts-section.view'; | ||
|
|
||
| import type { StoryMeta } from '@/lib/types'; | ||
|
|
||
| export { default as __source } from './user-profile-connected-accounts-section.stories?raw'; | ||
|
Comment on lines
+1
to
+5
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Add the required story self-import. Each file re-exports
As per coding guidelines, “Every 📍 Affects 4 files
🤖 Prompt for AI AgentsSources: Coding guidelines, Learnings |
||
|
|
||
| export const meta: StoryMeta = { | ||
| group: 'User', | ||
| title: 'UserProfileConnectedAccountsSection', | ||
| source: 'packages/ui/src/mosaic/user-profile/user-profile-connected-accounts-section.view.tsx', | ||
| }; | ||
|
|
||
| export function Default() { | ||
| return ( | ||
| <UserProfileConnectedAccountsSectionView | ||
| accounts={[ | ||
| { | ||
| id: 'google', | ||
| provider: 'Google', | ||
| identifier: 'test@google.com', | ||
| iconUrl: 'https://img.clerk.com/static/google.svg', | ||
| connected: true, | ||
| }, | ||
| { | ||
| id: 'apple', | ||
| provider: 'Apple', | ||
| iconUrl: 'https://img.clerk.com/static/apple.svg', | ||
| connected: false, | ||
| }, | ||
| ]} | ||
| onConnect={() => undefined} | ||
| onManage={() => undefined} | ||
| /> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import * as Stories from './user-profile-delete-section.stories'; | ||
|
|
||
| # UserProfileDeleteSection | ||
|
|
||
| The terminal destructive action for deleting the current user account. | ||
|
|
||
| <Story | ||
| name='Default' | ||
| storyModule={Stories} | ||
| composition={[ | ||
| { name: 'Section', href: '/components/section', layer: 'Components' }, | ||
| { name: 'Button', href: '/components/button', layer: 'Components' }, | ||
| ]} | ||
| /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { UserProfileDeleteSectionView } from '@clerk/ui/mosaic/user-profile/user-profile-delete-section.view'; | ||
|
|
||
| import type { StoryMeta } from '@/lib/types'; | ||
|
|
||
| export { default as __source } from './user-profile-delete-section.stories?raw'; | ||
|
|
||
| export const meta: StoryMeta = { | ||
| group: 'User', | ||
| title: 'UserProfileDeleteSection', | ||
| source: 'packages/ui/src/mosaic/user-profile/user-profile-delete-section.view.tsx', | ||
| }; | ||
|
|
||
| export function Default() { | ||
| return <UserProfileDeleteSectionView onDelete={() => undefined} />; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import * as UserProfileProfilePanelStories from './user-profile-profile-panel.stories'; | ||
|
|
||
| # UserProfileProfilePanel | ||
|
|
||
| The Profile panel from UserProfile, composed without the surrounding navigation shell. The | ||
| presentational view uses `Section` for its account details, connected accounts, Web3 wallets, and danger zone | ||
| while preserving the existing resource props and callback seams. | ||
|
|
||
| ## Example | ||
|
|
||
| <Story | ||
| name='Default' | ||
| storyModule={UserProfileProfilePanelStories} | ||
| composition={[ | ||
| { name: 'Section', href: '/components/section', layer: 'Components' }, | ||
| { name: 'Avatar', href: '/components/avatar', layer: 'Components' }, | ||
| ]} | ||
| /> | ||
|
|
||
| ## Usage | ||
|
Comment on lines
+9
to
+20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Apply the required MDX structure to all user-profile pages.
As per path instructions: “Playground / Props / Usage are mandatory and always in this order.” 📍 Affects 4 files
🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| ```tsx | ||
| import { UserProfileProfilePanelView } from '@clerk/ui/mosaic/user-profile/user-profile-profile-panel.view'; | ||
|
|
||
| <UserProfileProfilePanelView | ||
| imageUrl={user.imageUrl} | ||
| name={user.fullName ?? ''} | ||
| username={user.username ?? ''} | ||
| emails={user.emailAddresses.map(email => ({ | ||
| id: email.id, | ||
| value: email.emailAddress, | ||
| isDefault: email.id === user.primaryEmailAddressId, | ||
| isVerified: email.verification.status === 'verified', | ||
| }))} | ||
| phones={user.phoneNumbers.map(phone => ({ | ||
| id: phone.id, | ||
| value: phone.phoneNumber, | ||
| isDefault: phone.id === user.primaryPhoneNumberId, | ||
| isVerified: phone.verification.status === 'verified', | ||
| }))} | ||
| connectedAccounts={user.externalAccounts.map(account => ({ | ||
| id: account.id, | ||
| provider: account.provider, | ||
| identifier: account.emailAddress, | ||
| connected: true, | ||
| }))} | ||
| web3Wallets={supportedWeb3Wallets.map(provider => { | ||
| const wallet = user.web3Wallets.find(wallet => wallet.provider === provider.id); | ||
|
|
||
| return { | ||
| id: provider.id, | ||
| provider: provider.name, | ||
| iconUrl: provider.iconUrl, | ||
| address: wallet?.web3Wallet, | ||
| connected: Boolean(wallet), | ||
| isPrimary: wallet?.id === user.primaryWeb3WalletId, | ||
| isVerified: wallet ? wallet.verification.status === 'verified' : undefined, | ||
| }; | ||
| })} | ||
| onVerifyEmail={verifyEmail} | ||
| onSetPrimaryEmail={setPrimaryEmail} | ||
| onRemoveEmail={removeEmail} | ||
| onVerifyPhone={verifyPhone} | ||
| onSetPrimaryPhone={setPrimaryPhone} | ||
| onRemovePhone={removePhone} | ||
| onRemoveConnectedAccount={removeConnectedAccount} | ||
| onConnectWeb3Wallet={connectWeb3Wallet} | ||
| onSetPrimaryWeb3Wallet={setPrimaryWeb3Wallet} | ||
| onRemoveWeb3Wallet={removeWeb3Wallet} | ||
| /> | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| import { UserProfileProfilePanelView } from '@clerk/ui/mosaic/user-profile/user-profile-profile-panel.view'; | ||
|
|
||
| import type { StoryMeta } from '@/lib/types'; | ||
|
|
||
| const providerIconUrl = (provider: string) => `https://img.clerk.com/static/${provider}.svg`; | ||
| const profileImageUrl = 'https://avatars.githubusercontent.com/u/51144033?v=4'; | ||
|
|
||
| export { default as __source } from './user-profile-profile-panel.stories?raw'; | ||
|
|
||
| export const meta: StoryMeta = { | ||
| group: 'User', | ||
| title: 'UserProfileProfilePanel', | ||
| source: 'packages/ui/src/mosaic/user-profile/user-profile-profile-panel.view.tsx', | ||
| }; | ||
|
|
||
| export function Default(_args: Record<string, unknown>) { | ||
| return ( | ||
| <UserProfileProfilePanelView | ||
| emails={[ | ||
| { id: 'email_1', value: 'item1@clerk.dev', isDefault: true, isVerified: true }, | ||
| { id: 'email_2', value: 'item2@clerk.dev', isVerified: true }, | ||
| ]} | ||
| connectedAccounts={[ | ||
| { | ||
| id: 'google', | ||
| provider: 'Google', | ||
| identifier: 'test@google.com', | ||
| iconUrl: providerIconUrl('google'), | ||
| connected: true, | ||
| }, | ||
| { id: 'apple', provider: 'Apple', iconUrl: providerIconUrl('apple'), connected: false }, | ||
| ]} | ||
| web3Wallets={[ | ||
| { | ||
| id: 'metamask', | ||
| address: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', | ||
| provider: 'MetaMask', | ||
| iconUrl: providerIconUrl('metamask'), | ||
| isPrimary: true, | ||
| isVerified: true, | ||
| }, | ||
| { | ||
| id: 'coinbase-wallet', | ||
| provider: 'Coinbase Wallet', | ||
| iconUrl: providerIconUrl('coinbase_wallet'), | ||
| connected: false, | ||
| }, | ||
| ]} | ||
| imageUrl={profileImageUrl} | ||
| name='Preston Booth' | ||
| phones={[{ id: 'phone_1', value: '+1 801-888-8181', isDefault: true, isVerified: true }]} | ||
| username='prestonxyz' | ||
| onAddEmail={() => undefined} | ||
| onAddPhone={() => undefined} | ||
| onConnectAccount={() => undefined} | ||
| onDeleteAccount={() => undefined} | ||
| onEditProfilePicture={() => undefined} | ||
| onRemoveConnectedAccount={() => undefined} | ||
| onRemoveEmail={() => undefined} | ||
| onRemovePhone={() => undefined} | ||
| onConnectWeb3Wallet={() => undefined} | ||
| onRemoveWeb3Wallet={() => undefined} | ||
| onSetPrimaryWeb3Wallet={() => undefined} | ||
| onSetPrimaryEmail={() => undefined} | ||
| onSetPrimaryPhone={() => undefined} | ||
| onVerifyEmail={() => undefined} | ||
| onVerifyPhone={() => undefined} | ||
| onNameChange={() => undefined} | ||
| onUsernameChange={() => undefined} | ||
| /> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import * as Stories from './user-profile-web3-wallets-section.stories'; | ||
|
|
||
| # UserProfileWeb3WalletsSection | ||
|
|
||
| Supported Web3 wallet providers with per-provider connection actions, connected identities, verification state, | ||
| primary status, and wallet actions. | ||
|
|
||
| <Story | ||
| name='Default' | ||
| storyModule={Stories} | ||
| composition={[ | ||
| { name: 'Section', href: '/components/section', layer: 'Components' }, | ||
| { name: 'Badge', href: '/components/badge', layer: 'Components' }, | ||
| ]} | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Use the canonical Web3 wallet slug.
The map key is
user-profile-web3wallets-section, but the imported module path isuser-profile-web3-wallets-section.mdx. The registry usestoSlug(meta.title)for route lookup. The current key can prevent the Web3 wallet documentation from loading and renderNo docs found.📝 Committable suggestion
🤖 Prompt for AI Agents