- {/* Folder sidebar: in flow on desktop, a drawer on narrow panes */}
- {sidebar && isNarrow && narrowSidebarOpen && (
-
setNarrowSidebarOpen(false)} />
- )}
+ {/* Folder sidebar: in flow on the full desktop; below it a drawer from
+ the window's edge, over the app rail, as Mail's folders. */}
{sidebar && (isNarrow ? (
-
{sidebar}
-
+
) : (
<>
Date: Wed, 4 Nov 2026 17:20:53 +0100
Subject: [PATCH 3/4] fix: point the folder list's collapse arrows the right
way in right-to-left languages
---
.../layout/__tests__/sidebar-folder-drawer.test.tsx | 10 +++++++---
components/layout/sidebar.tsx | 4 ++--
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/components/layout/__tests__/sidebar-folder-drawer.test.tsx b/components/layout/__tests__/sidebar-folder-drawer.test.tsx
index 413d7316b..d6f603121 100644
--- a/components/layout/__tests__/sidebar-folder-drawer.test.tsx
+++ b/components/layout/__tests__/sidebar-folder-drawer.test.tsx
@@ -102,11 +102,15 @@ describe('the folded column\'s drawer (default layout)', () => {
expect(inDrawer).toBe(docked);
});
- it('mirrors the collapse arrows in RTL, where the drawer comes from the right', () => {
+ // RTL: globals.css mirrors the chevrons icons; a flip of their own as well would turn them back.
+ it('leaves the collapse arrows to the global RTL mirror, where the drawer comes from the right', () => {
+ const arrow = () => screen.getByTestId('sidebar-collapse').querySelector('svg')!.getAttribute('class') ?? '';
render();
- expect(screen.getByTestId('sidebar-collapse').querySelector('svg')!.getAttribute('class')).toContain('rtl:-scale-x-100');
+ expect(arrow()).toMatch(/tabler-icon-chevrons-(left|right)/);
+ expect(arrow()).not.toMatch(/rtl:/);
openFromAllFolders();
- expect(screen.getByTestId('sidebar-collapse').querySelector('svg')!.getAttribute('class')).toContain('rtl:-scale-x-100');
+ expect(arrow()).toMatch(/tabler-icon-chevrons-(left|right)/);
+ expect(arrow()).not.toMatch(/rtl:/);
});
it('keeps Tab and Shift+Tab inside while open', () => {
diff --git a/components/layout/sidebar.tsx b/components/layout/sidebar.tsx
index dbe7db61f..b4c10c384 100644
--- a/components/layout/sidebar.tsx
+++ b/components/layout/sidebar.tsx
@@ -2143,8 +2143,8 @@ function SidebarImpl({
data-testid="sidebar-collapse"
title={isCollapsed ? t("expand_tooltip") : t("collapse_tooltip")}
>
- {/* The arrows point the way the column goes: mirrored in RTL, where it sits at the right. */}
- {isCollapsed ? : }
+ {/* The arrows point the way the column goes; the app's global RTL mirror turns them in RTL. */}
+ {isCollapsed ? : }
{/* The account switcher heads the folders by default, with the
From 08cf8da229008539728ab1862e33023e57541f98 Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Wed, 4 Nov 2026 17:24:57 +0100
Subject: [PATCH 4/4] fix: put the focus in a drawer as soon as it opens, also
with animations on
---
components/layout/__tests__/edge-drawer.test.tsx | 12 ++++++++++++
components/layout/edge-drawer.tsx | 7 +++++--
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/components/layout/__tests__/edge-drawer.test.tsx b/components/layout/__tests__/edge-drawer.test.tsx
index 04ac3b893..2c060c035 100644
--- a/components/layout/__tests__/edge-drawer.test.tsx
+++ b/components/layout/__tests__/edge-drawer.test.tsx
@@ -33,6 +33,18 @@ describe('EdgeDrawer', () => {
expect(document.activeElement).toBe(menu);
});
+ // Seen in the desktop app: with visibility in the opening transition the
+ // panel is still hidden in its first frame and refuses the focus.
+ it('is visible from its first frame when it opens, and hidden only after sliding out', () => {
+ render();
+ const classes = () => screen.getByTestId('drawer').className.split(/\s+/);
+ expect(classes()).toContain('transition-[transform,visibility]');
+ fireEvent.click(screen.getByText('menu'));
+ expect(classes()).toContain('transition-transform');
+ expect(classes()).not.toContain('transition-[transform,visibility]');
+ expect(classes()).not.toContain('invisible');
+ });
+
it('leaves the focus where a pick moved it, outside the drawer', () => {
render( screen.getByLabelText('elsewhere').focus()} />);
const menu = screen.getByText('menu');
diff --git a/components/layout/edge-drawer.tsx b/components/layout/edge-drawer.tsx
index 616a6e30f..04a42fac0 100644
--- a/components/layout/edge-drawer.tsx
+++ b/components/layout/edge-drawer.tsx
@@ -61,8 +61,11 @@ export function EdgeDrawer({ open, onClose, contained, returnFocus, className, c
className={cn(
"z-50 w-72 max-w-[85%] flex flex-col",
contained ? "absolute inset-y-0 start-0" : "fixed start-0 top-[var(--drawer-top)] bottom-0",
- "transform transition-[transform,visibility] duration-300 ease-in-out",
- open ? "shadow-xl" : "invisible -translate-x-full rtl:translate-x-full",
+ "transform duration-300 ease-in-out",
+ // Visible at once on opening, so the focus can go in straight away
+ // (a visibility transition keeps it hidden for that first frame);
+ // hidden only once it has slid out on closing.
+ open ? "shadow-xl transition-transform" : "invisible -translate-x-full rtl:translate-x-full transition-[transform,visibility]",
className
)}
>