Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 470
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
3c99147514824fff944123ce08ba44b7772b73694c5622c9a886d472ea033f576faade78a253230bf82aeb86e9ee33b63a984b738File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
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' }, | ||
| ]} | ||
| /> |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
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