From e24dd5876b4f32f87be85bdad3e685b4fe95d7c5 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Tue, 3 Nov 2026 09:22:17 +0100 Subject: [PATCH] fix: show folder names in the drawer when the sidebar was collapsed on a wider window --- .../sidebar-rail-peek-hover.test.tsx | 20 ++++++++++++++++++- components/layout/sidebar.tsx | 8 +++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/components/layout/__tests__/sidebar-rail-peek-hover.test.tsx b/components/layout/__tests__/sidebar-rail-peek-hover.test.tsx index 444f6de8c..5e65bfbc5 100644 --- a/components/layout/__tests__/sidebar-rail-peek-hover.test.tsx +++ b/components/layout/__tests__/sidebar-rail-peek-hover.test.tsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react'; +import { render, screen, cleanup } from '@testing-library/react'; import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import type { Mailbox } from '@/lib/jmap/types'; @@ -51,6 +51,24 @@ describe('Mountain View rail peek', () => { const peek = (container: HTMLElement) => container.querySelector('[data-mv-rail-peek]'); + it('shows folder names in the drawer of a narrow window, even when collapsed on a wider one', () => { + const matchMedia = window.matchMedia; + window.matchMedia = ((query: string) => ({ + matches: query.includes('64rem'), media: query, addEventListener() {}, removeEventListener() {}, + })) as never; + try { + render(); + expect(screen.getByText('News')).toBeTruthy(); + } finally { + window.matchMedia = matchMedia; + } + }); + + it('keeps the icon rail on a wide window', () => { + render(); + expect(screen.queryByText('News')).toBeNull(); + }); + it('peeks on hover by default', () => { const { container } = render(); fire.mouseEnter(container.firstElementChild!); diff --git a/components/layout/sidebar.tsx b/components/layout/sidebar.tsx index 027776414..e525f3737 100644 --- a/components/layout/sidebar.tsx +++ b/components/layout/sidebar.tsx @@ -61,6 +61,7 @@ import { Mailbox } from "@/lib/jmap/types"; import { useContextMenu } from "@/hooks/use-context-menu"; import { useLongPress } from "@/hooks/use-long-press"; import { useMediaQuery } from "@/hooks/use-media-query"; +import { usePaneSize } from "@/hooks/use-pane-size"; import { IconButton } from "@/components/ui/icon-button"; import { MailboxContextMenu, type MailboxContextTarget, type MailboxNotificationActions } from "./mailbox-context-menu"; import { useAccountStore } from '@/stores/account-store'; @@ -1166,7 +1167,12 @@ function SidebarImpl({ // list behind it. const [railHovered, setRailHovered] = useState(false); const hoverExpanded = mvShell && sidebarCollapsed && railHovered; - const isCollapsed = sidebarCollapsed && !hoverExpanded; + // The rail is a desktop thing. Below that the sidebar is a drawer, opened to + // read the folder names, whatever the collapse button last said on a wider window. + const paneWidth = usePaneSize(); + const narrowWindow = useMediaQuery('(width < 64rem)'); + const isDrawer = paneWidth !== null ? paneWidth < 1024 : narrowWindow; + const isCollapsed = sidebarCollapsed && !hoverExpanded && !isDrawer; const { primaryIdentity: _primaryIdentity, activeAccountId } = useAuthStore(useShallow((state) => ({ primaryIdentity: state.primaryIdentity, activeAccountId: state.activeAccountId }))); const [expandedFolders, setExpandedFolders] = useState>(new Set()); const [expandedTags, setExpandedTags] = useState>(new Set());