fix: align sidebar footer actions (#1288)

* fix: align sidebar footer actions

* test: cover sidebar footer spacing

* test: guard theme icon sizing

---------

Co-authored-by: Luis Novo <lfnovo@gmail.com>
This commit is contained in:
Sure 2026-09-04 10:30:41 +08:00 • committed by GitHub
parent eeb2e67820
commit 43a07fe28e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 24 additions and 7 deletions

View file

@ -25,12 +25,12 @@ export function ThemeToggle({ iconOnly = false }: ThemeToggleProps) {
<Button
variant={iconOnly ? "ghost" : "outline"}
size={iconOnly ? "icon" : "default"}
className={iconOnly ? "h-9 w-full sidebar-menu-item" : "w-full justify-start gap-2 sidebar-menu-item"}
className={iconOnly ? "h-9 w-full sidebar-menu-item" : "w-full justify-start gap-2 px-3 sidebar-menu-item"}
>
<div className="relative h-[1.2rem] w-[1.2rem]">
<Sun className="absolute inset-0 h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute inset-0 h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
</div>
<span className="relative size-4">
<Sun className="absolute inset-0 size-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute inset-0 size-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
</span>
{!iconOnly && <span>{t('common.theme')}</span>}
<span className="sr-only">{t('navigation.theme')}</span>
</Button>
@ -60,4 +60,4 @@ export function ThemeToggle({ iconOnly = false }: ThemeToggleProps) {
</DropdownMenuContent>
</DropdownMenu>
)
}
}

View file

@ -22,6 +22,23 @@ describe('AppSidebar', () => {
expect(screen.getByText('navigation.notebooks')).toBeDefined()
})
it('uses consistent spacing for expanded footer actions', () => {
render(<AppSidebar />)
const themeButton = screen.getByText('common.theme').closest('button')
const languageButton = screen.getByText('common.language').closest('button')
const signOutButton = screen.getByRole('button', { name: 'common.signOut' })
expect(themeButton?.className.split(/\s+/)).toContain('px-3')
for (const button of [themeButton, languageButton, signOutButton]) {
expect(button?.className.split(/\s+/)).toContain('gap-2')
}
expect(themeButton?.querySelector(':scope > span.relative.size-4')).not.toBeNull()
expect(signOutButton.className.split(/\s+/)).not.toContain('gap-3')
})
it('toggles collapse state when clicking handle', () => {
const toggleCollapse = vi.fn()
vi.mocked(useSidebarStore).mockReturnValue({

View file

@ -371,7 +371,7 @@ export function AppSidebar() {
) : (
<Button
variant="outline"
className="w-full justify-start gap-3 sidebar-menu-item"
className="w-full justify-start gap-2 sidebar-menu-item"
onClick={logout}
aria-label={t('common.signOut')}
>