-
Notifications
You must be signed in to change notification settings - Fork 468
feat(ui,headless): switch accounts from a flyout in the Mosaic UserButton #9534
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
793a719
c03f45c
15db23a
34edb4d
041530b
38ba1e7
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,19 @@ | ||
| import { describe, expect, it } from 'vitest'; | ||
|
|
||
| import { resolveSideOffset } from './side-offset'; | ||
|
|
||
| describe('resolveSideOffset', () => { | ||
| it('takes one number for every placement', () => { | ||
| expect(resolveSideOffset(8, 'top-start')).toBe(8); | ||
| expect(resolveSideOffset(8, 'right')).toBe(8); | ||
| }); | ||
|
|
||
| it('takes x on a horizontal placement and y on a vertical one', () => { | ||
| const offset = { x: 16, y: 8 }; | ||
|
|
||
| expect(resolveSideOffset(offset, 'right-start')).toBe(16); | ||
| expect(resolveSideOffset(offset, 'left-end')).toBe(16); | ||
| expect(resolveSideOffset(offset, 'top-start')).toBe(8); | ||
| expect(resolveSideOffset(offset, 'bottom')).toBe(8); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import type { Placement } from '@floating-ui/react'; | ||
|
|
||
| /** | ||
| * The gap between a floating element and what it is anchored to, in px. One number covers every | ||
| * placement. `{ x, y }` gives the horizontal and vertical sides a gap each, which a surface that can | ||
| * flip between the two axes wants: what it has to clear sideways is not what it has to clear above. | ||
| */ | ||
| export type SideOffset = number | { x: number; y: number }; | ||
|
|
||
| /** Picks the gap the placement's own axis asks for. */ | ||
| export function resolveSideOffset(offset: SideOffset, placement: Placement): number { | ||
| if (typeof offset === 'number') { | ||
| return offset; | ||
| } | ||
| const side = placement.split('-')[0]; | ||
| return side === 'left' || side === 'right' ? offset.x : offset.y; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import { Avatar } from '@clerk/ui/mosaic/components/avatar'; | ||
| import { Icon } from '@clerk/ui/mosaic/components/icon'; | ||
| import { Menu } from '@clerk/ui/mosaic/components/menu'; | ||
|
|
||
|
|
@@ -17,23 +18,66 @@ export function Default() { | |
| return ( | ||
| <Menu.Root> | ||
| <Menu.Trigger /> | ||
| <Menu.Content> | ||
| <Menu.Popup> | ||
| <Menu.Item label='Add workspace'> | ||
| <Icon name='plus' /> | ||
| Add workspace | ||
| <Menu.Media> | ||
| <Icon name='plus' /> | ||
| </Menu.Media> | ||
| <Menu.Label>Add workspace</Menu.Label> | ||
| </Menu.Item> | ||
| <Menu.Item label='Sign out'> | ||
| <Icon name='log-out' /> | ||
| Sign out | ||
| <Menu.Media> | ||
| <Icon name='log-out' /> | ||
| </Menu.Media> | ||
| <Menu.Label>Sign out</Menu.Label> | ||
| </Menu.Item> | ||
| <Menu.Item | ||
| label='Delete user' | ||
| color='negative' | ||
| > | ||
| <Icon name='close' /> | ||
| Delete user | ||
| <Menu.Media> | ||
| <Icon name='close' /> | ||
| </Menu.Media> | ||
| <Menu.Label>Delete user</Menu.Label> | ||
| </Menu.Item> | ||
| </Menu.Content> | ||
| </Menu.Popup> | ||
| </Menu.Root> | ||
| ); | ||
| } | ||
|
|
||
| const accounts = [ | ||
| { active: true, identifier: 'colin@clerk.dev', initial: 'C' }, | ||
| { active: false, identifier: 'braden.wiggins@a-very-long-domain.example', initial: 'B' }, | ||
| ]; | ||
|
|
||
| export function Accounts() { | ||
| return ( | ||
| <Menu.Root> | ||
| <Menu.Trigger>Switch account</Menu.Trigger> | ||
| <Menu.Popup> | ||
| {accounts.map(account => ( | ||
| <Menu.Item | ||
| key={account.identifier} | ||
| label={account.identifier} | ||
| > | ||
| <Menu.Media> | ||
| <Avatar.Root | ||
| shape='circle' | ||
| size='fit' | ||
| > | ||
| <Avatar.Fallback>{account.initial}</Avatar.Fallback> | ||
| </Avatar.Root> | ||
| </Menu.Media> | ||
| <Menu.Label>{account.identifier}</Menu.Label> | ||
| {account.active ? ( | ||
| <Icon | ||
| name='check' | ||
| size='sm' | ||
| /> | ||
| ) : null} | ||
|
Comment on lines
+72
to
+77
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 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 6 'MenuItemProps|aria-(checked|current)|role=.*menuitem' \
packages/ui/src/mosaic/components/menu \
packages/headless/src/primitives/menuRepository: clerk/javascript Length of output: 11280 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- story ---'
sed -n '1,115p' packages/swingset/src/stories/menu.component.stories.tsx
printf '%s\n' '--- menu item implementation ---'
sed -n '1,180p' packages/headless/src/primitives/menu/menu-item.tsx
printf '%s\n' '--- menu story usages ---'
rg -n -C 4 'Menu\.Item|aria-selected|aria-current|Current account|active' packages/swingset/src/stories packages/ui/src/mosaic/components/menu packages/headless/src/primitives/menuRepository: clerk/javascript Length of output: 50373 🏁 Script executed: #!/bin/bash
set -euo pipefail
sed -n '1,95p' packages/ui/src/mosaic/components/icon/icon.tsx
printf '%s\n' '--- icon tests/usages for accessibility ---'
rg -n -C 3 'aria-hidden|Icon.*name=|getByRole.*menuitem|Current account' \
packages/ui/src/mosaic/components/icon packages/ui/src/mosaic/components/menu packages/swingset/src/stories/menu.component.stories.tsx \
| head -n 160Repository: clerk/javascript Length of output: 14791 Expose the current account to assistive technology. Add 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| </Menu.Item> | ||
| ))} | ||
| </Menu.Popup> | ||
| </Menu.Root> | ||
| ); | ||
| } | ||
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 | 🟡 Minor | ⚡ Quick win
Add the missing
Avatarimport to the example.The example uses
Avatar.Rootat Line 91, but the usage imports shown at Lines 20-22 define onlyIconandMenu. Copying the example can produce an unresolvedAvataridentifier. Add the correct import or use an imported component.🤖 Prompt for AI Agents