Move settings out of help dropdown into dedicated sidebar button (#5272)
- Pulls the Settings option out of the Help dropdown menu and renders it as a standalone button directly in the sidebar. - Integrates the `useShortcutKeys` hook and `ShortcutKeyCombo` component to display the settings keyboard shortcut in the button's tooltip. - Updates associated tests to verify the new button's placement, ARIA attributes, and tooltip content.
This commit is contained in:
parent
0021d95796
commit
f027a1cb7b
|
|
@ -10,7 +10,7 @@ const mocks = vi.hoisted(() => ({
|
|||
appRestart: vi.fn(),
|
||||
updaterCheck: vi.fn(),
|
||||
shellOpenUrl: vi.fn(),
|
||||
useShortcutLabel: vi.fn(),
|
||||
useShortcutKeys: vi.fn(),
|
||||
setupProgress: {
|
||||
ready: true,
|
||||
coreDoneCount: 2,
|
||||
|
|
@ -32,7 +32,7 @@ vi.mock('@/store', () => ({
|
|||
}))
|
||||
|
||||
vi.mock('@/hooks/useShortcutLabel', () => ({
|
||||
useShortcutLabel: mocks.useShortcutLabel
|
||||
useShortcutKeys: mocks.useShortcutKeys
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/useMountedRef', () => ({
|
||||
|
|
@ -72,12 +72,14 @@ vi.mock('@/components/ui/tooltip', () => ({
|
|||
vi.mock('@/components/ui/button', () => ({
|
||||
Button: ({
|
||||
children,
|
||||
onClick
|
||||
onClick,
|
||||
'aria-label': ariaLabel
|
||||
}: {
|
||||
children: ReactNode
|
||||
onClick?: (event: React.MouseEvent) => void
|
||||
'aria-label'?: string
|
||||
}) => (
|
||||
<button data-testid="trigger-button" onClick={onClick}>
|
||||
<button data-testid="trigger-button" aria-label={ariaLabel} onClick={onClick}>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
|
|
@ -97,7 +99,7 @@ vi.mock('./SidebarFeedbackDialog', () => ({
|
|||
describe('SidebarSettingsHelpMenu', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.useShortcutLabel.mockReturnValue('⌘,')
|
||||
mocks.useShortcutKeys.mockReturnValue(['⌘', ','])
|
||||
updateStatus = { state: 'idle' }
|
||||
mocks.setupProgress = {
|
||||
ready: true,
|
||||
|
|
@ -112,9 +114,17 @@ describe('SidebarSettingsHelpMenu', () => {
|
|||
expect(html).toContain('Help')
|
||||
})
|
||||
|
||||
it('renders Settings menu item', () => {
|
||||
it('renders the settings button with correct aria-label', () => {
|
||||
const html = renderToStaticMarkup(<SidebarSettingsHelpMenu />)
|
||||
expect(html).toContain('Settings')
|
||||
expect(html).toContain('aria-label="Settings"')
|
||||
})
|
||||
|
||||
it('renders the settings button before the help button', () => {
|
||||
const html = renderToStaticMarkup(<SidebarSettingsHelpMenu />)
|
||||
const settingsIndex = html.indexOf('lucide-settings')
|
||||
const helpIndex = html.indexOf('lucide-circle-question-mark')
|
||||
expect(settingsIndex).toBeGreaterThanOrEqual(0)
|
||||
expect(helpIndex).toBeGreaterThan(settingsIndex)
|
||||
})
|
||||
|
||||
it('renders Send Feedback menu item', () => {
|
||||
|
|
@ -179,8 +189,9 @@ describe('SidebarSettingsHelpMenu', () => {
|
|||
expect(html).toContain('Check for Updates')
|
||||
})
|
||||
|
||||
it('renders shortcut label next to Settings', () => {
|
||||
it('renders shortcut keys in the settings tooltip', () => {
|
||||
const html = renderToStaticMarkup(<SidebarSettingsHelpMenu />)
|
||||
expect(html).toContain('⌘,')
|
||||
expect(html).toContain('⌘')
|
||||
expect(html).toContain('>,</span>')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ import {
|
|||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { useMountedRef } from '@/hooks/useMountedRef'
|
||||
import { useShortcutLabel } from '@/hooks/useShortcutLabel'
|
||||
import { useShortcutKeys } from '@/hooks/useShortcutLabel'
|
||||
import { ShortcutKeyCombo } from '@/components/ShortcutKeyCombo'
|
||||
import { showOnboardingFromRenderer } from '../onboarding/show-onboarding-event'
|
||||
import { SetupGuideProgressRing } from '../setup-guide/SetupGuideProgressRing'
|
||||
import { useSetupGuideProgress } from '../setup-guide/use-setup-guide-progress'
|
||||
|
|
@ -84,7 +85,7 @@ export function SidebarSettingsHelpMenu(): React.JSX.Element {
|
|||
const updateStatus = useAppStore((s) => s.updateStatus)
|
||||
const setupProgress = useSetupGuideProgress(true, false, false)
|
||||
|
||||
const settingsShortcut = useShortcutLabel('app.settings')
|
||||
const settingsShortcutKeys = useShortcutKeys('app.settings')
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const [feedbackOpen, setFeedbackOpen] = useState(false)
|
||||
const [showAdminOptions, setShowAdminOptions] = useState(false)
|
||||
|
|
@ -157,145 +158,173 @@ export function SidebarSettingsHelpMenu(): React.JSX.Element {
|
|||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu modal={false} open={menuOpen} onOpenChange={handleMenuOpenChange}>
|
||||
<div className="flex items-center gap-1">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
type="button"
|
||||
aria-label={translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.2991a0106c',
|
||||
'Help'
|
||||
)}
|
||||
className="text-muted-foreground"
|
||||
onPointerDown={(event) => revealAdminOptions(event.altKey)}
|
||||
onClick={(event) => revealAdminOptions(event.altKey)}
|
||||
>
|
||||
<CircleHelp className="size-3.5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
type="button"
|
||||
aria-label={translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.a428c25998',
|
||||
'Settings'
|
||||
)}
|
||||
className="text-muted-foreground"
|
||||
onClick={openSettingsPage}
|
||||
>
|
||||
<Settings className="size-3.5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" sideOffset={4}>
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.2991a0106c', 'Help')}
|
||||
<TooltipContent side="top" sideOffset={4} className="flex items-center gap-1.5">
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.a428c25998', 'Settings')}
|
||||
{settingsShortcutKeys.length > 0 ? (
|
||||
<ShortcutKeyCombo
|
||||
keys={settingsShortcutKeys}
|
||||
className="gap-0.5"
|
||||
keyCapClassName="min-w-0 border-background/20 bg-background/10 px-1 py-0 text-[10px] text-background shadow-none"
|
||||
separatorClassName="text-[10px] text-background/70"
|
||||
/>
|
||||
) : null}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<DropdownMenuContent side="top" align="start" sideOffset={8} className="w-52">
|
||||
<DropdownMenuItem onSelect={openSettingsPage}>
|
||||
<Settings className="size-3.5" />
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.a428c25998', 'Settings')}
|
||||
<span className="ml-auto text-xs tracking-wide opacity-60">{settingsShortcut}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={openShortcutsSettings}>
|
||||
<Keyboard className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.e565171a7c',
|
||||
'Keyboard Shortcuts'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onSelect={() => setFeedbackOpen(true)}>
|
||||
<MessageSquareText className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.4cf5b868d7',
|
||||
'Send Feedback'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
{showMilestones ? (
|
||||
<DropdownMenuItem onSelect={openMilestones}>
|
||||
<img
|
||||
src={logo}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="size-3.5 object-contain invert opacity-55 dark:invert-0"
|
||||
/>
|
||||
<DropdownMenu modal={false} open={menuOpen} onOpenChange={handleMenuOpenChange}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
type="button"
|
||||
aria-label={translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.2991a0106c',
|
||||
'Help'
|
||||
)}
|
||||
className="text-muted-foreground"
|
||||
onPointerDown={(event) => revealAdminOptions(event.altKey)}
|
||||
onClick={(event) => revealAdminOptions(event.altKey)}
|
||||
>
|
||||
<CircleHelp className="size-3.5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" sideOffset={4}>
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.2991a0106c', 'Help')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<DropdownMenuContent side="top" align="start" sideOffset={8} className="w-52">
|
||||
<DropdownMenuItem onSelect={openShortcutsSettings}>
|
||||
<Keyboard className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.f8a2c91d4e',
|
||||
'Milestones'
|
||||
)}
|
||||
<SetupGuideProgressRing
|
||||
done={setupProgress.coreDoneCount}
|
||||
total={setupProgress.coreTotal}
|
||||
sizeClassName="size-4"
|
||||
className="ml-auto"
|
||||
/>
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
{showAdminOptions ? (
|
||||
<DropdownMenuItem
|
||||
className="whitespace-nowrap"
|
||||
onClick={handleShowOnboarding}
|
||||
onSelect={handleShowOnboarding}
|
||||
>
|
||||
<School className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.b7e4d2a19c',
|
||||
'Onboarding'
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.e565171a7c',
|
||||
'Keyboard Shortcuts'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
<ExternalMenuItem
|
||||
label={translate('auto.components.sidebar.SidebarSettingsHelpMenu.cdc87f897e', 'Docs')}
|
||||
url={DOCS_URL}
|
||||
icon={<BookOpen className="size-3.5" />}
|
||||
/>
|
||||
<ExternalMenuItem
|
||||
label={translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.5f83d86d92',
|
||||
'Changelog'
|
||||
)}
|
||||
url={CHANGELOG_URL}
|
||||
icon={<ScrollText className="size-3.5" />}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<ExternalMenuItem
|
||||
label={translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.5687ab246a',
|
||||
'GitHub'
|
||||
)}
|
||||
url={GITHUB_URL}
|
||||
icon={<Github className="size-3.5" />}
|
||||
/>
|
||||
<DropdownMenuItem onSelect={() => openExternalUrl(DISCORD_URL)}>
|
||||
<DiscordIcon />
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.eb9884e55b', 'Discord')}
|
||||
<ExternalLink className="ml-auto size-3 text-muted-foreground" />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => openExternalUrl(X_URL)}>
|
||||
<XIcon />
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.c4f8e1b72a', 'X')}
|
||||
<ExternalLink className="ml-auto size-3 text-muted-foreground" />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
disabled={updateStatus.state === 'checking' || updateStatus.state === 'downloading'}
|
||||
onSelect={handleCheckForUpdates}
|
||||
>
|
||||
{updateStatus.state === 'checking' ? (
|
||||
<Loader2 className="size-3.5 animate-spin" />
|
||||
) : (
|
||||
<RefreshCw className="size-3.5" />
|
||||
)}
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.29c56f30ee',
|
||||
'Check for Updates'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
{showAdminOptions ? (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onSelect={handleRestartOrca} disabled={isRestartingOrca}>
|
||||
<RotateCw className="size-3.5" />
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onSelect={() => setFeedbackOpen(true)}>
|
||||
<MessageSquareText className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.4cf5b868d7',
|
||||
'Send Feedback'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
{showMilestones ? (
|
||||
<DropdownMenuItem onSelect={openMilestones}>
|
||||
<img
|
||||
src={logo}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="size-3.5 object-contain invert opacity-55 dark:invert-0"
|
||||
/>
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.ad3d3ed7f1',
|
||||
'Restart Orca'
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.f8a2c91d4e',
|
||||
'Milestones'
|
||||
)}
|
||||
<SetupGuideProgressRing
|
||||
done={setupProgress.coreDoneCount}
|
||||
total={setupProgress.coreTotal}
|
||||
sizeClassName="size-4"
|
||||
className="ml-auto"
|
||||
/>
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
{showAdminOptions ? (
|
||||
<DropdownMenuItem
|
||||
className="whitespace-nowrap"
|
||||
onClick={handleShowOnboarding}
|
||||
onSelect={handleShowOnboarding}
|
||||
>
|
||||
<School className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.b7e4d2a19c',
|
||||
'Onboarding'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
) : null}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : null}
|
||||
<ExternalMenuItem
|
||||
label={translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.cdc87f897e',
|
||||
'Docs'
|
||||
)}
|
||||
url={DOCS_URL}
|
||||
icon={<BookOpen className="size-3.5" />}
|
||||
/>
|
||||
<ExternalMenuItem
|
||||
label={translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.5f83d86d92',
|
||||
'Changelog'
|
||||
)}
|
||||
url={CHANGELOG_URL}
|
||||
icon={<ScrollText className="size-3.5" />}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<ExternalMenuItem
|
||||
label={translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.5687ab246a',
|
||||
'GitHub'
|
||||
)}
|
||||
url={GITHUB_URL}
|
||||
icon={<Github className="size-3.5" />}
|
||||
/>
|
||||
<DropdownMenuItem onSelect={() => openExternalUrl(DISCORD_URL)}>
|
||||
<DiscordIcon />
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.eb9884e55b', 'Discord')}
|
||||
<ExternalLink className="ml-auto size-3 text-muted-foreground" />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => openExternalUrl(X_URL)}>
|
||||
<XIcon />
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.c4f8e1b72a', 'X')}
|
||||
<ExternalLink className="ml-auto size-3 text-muted-foreground" />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
disabled={updateStatus.state === 'checking' || updateStatus.state === 'downloading'}
|
||||
onSelect={handleCheckForUpdates}
|
||||
>
|
||||
{updateStatus.state === 'checking' ? (
|
||||
<Loader2 className="size-3.5 animate-spin" />
|
||||
) : (
|
||||
<RefreshCw className="size-3.5" />
|
||||
)}
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.29c56f30ee',
|
||||
'Check for Updates'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
{showAdminOptions ? (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onSelect={handleRestartOrca} disabled={isRestartingOrca}>
|
||||
<RotateCw className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.ad3d3ed7f1',
|
||||
'Restart Orca'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
) : null}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<SidebarFeedbackDialog open={feedbackOpen} onOpenChange={setFeedbackOpen} />
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue