From fdc3535c7b63fa378b7ca0cebdeb8f500d01614d Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Wed, 28 Oct 2026 02:43:58 +0100
Subject: [PATCH] fix: close the Contacts and Files drawer when you pick
something in it
---
.../contacts/__tests__/contacts-app.test.tsx | 13 ++++++-
components/contacts/contacts-app.tsx | 39 +++++++++++--------
components/files/file-browser.tsx | 25 +++++++-----
3 files changed, 50 insertions(+), 27 deletions(-)
diff --git a/components/contacts/__tests__/contacts-app.test.tsx b/components/contacts/__tests__/contacts-app.test.tsx
index 27749049f..90027175e 100644
--- a/components/contacts/__tests__/contacts-app.test.tsx
+++ b/components/contacts/__tests__/contacts-app.test.tsx
@@ -74,7 +74,7 @@ describe('ContactsApp', () => {
});
});
- it('takes the closed drawer out of reach, so it can\'t cover the nav rail or catch Tab', () => {
+ it("takes the closed drawer out of reach, so it can't cover the nav rail or catch Tab", () => {
renderContacts();
expect(drawer()).toHaveAttribute('inert');
expect(drawer().className).toContain('invisible');
@@ -83,4 +83,15 @@ describe('ContactsApp', () => {
expect(drawer()).not.toHaveAttribute('inert');
expect(within(drawer()).getByText('tabs.all')).toBeInTheDocument();
});
+
+ it("closes the drawer when something in it is picked, so it doesn't stay over the form", () => {
+ renderContacts();
+ fireEvent.click(screen.getAllByRole('button', { name: 'open_categories' })[0]);
+ expect(drawer()).not.toHaveAttribute('inert');
+
+ // The header's "+" menu, then "New contact".
+ fireEvent.click(within(drawer()).getAllByRole('button')[0]);
+ fireEvent.click(within(drawer()).getByText('create_new'));
+ expect(drawer()).toHaveAttribute('inert');
+ });
});
diff --git a/components/contacts/contacts-app.tsx b/components/contacts/contacts-app.tsx
index 30ad9c366..7e4dd7b52 100644
--- a/components/contacts/contacts-app.tsx
+++ b/components/contacts/contacts-app.tsx
@@ -151,6 +151,13 @@ function ContactsAppContent({ linkSegments: routeSegments }: ContactsAppProps =
const isNarrow = !isDesktop;
const [narrowSidebarOpen, setNarrowSidebarOpen] = useState(false);
useEffect(() => { if (!isNarrow) setNarrowSidebarOpen(false); }, [isNarrow]);
+ // A new view (a form, a group, a contact) replaces whatever the drawer was for.
+ useEffect(() => { setNarrowSidebarOpen(false); }, [view]);
+ // Anything picked in the drawer closes it (R7).
+ const fromDrawer = (action: (...args: A) => R) => (...args: A): R => {
+ setNarrowSidebarOpen(false);
+ return action(...args);
+ };
// Panel resize state - sidebar (categories)
const [sidebarWidth, setSidebarWidth] = useState(() => {
@@ -1013,31 +1020,31 @@ function ContactsAppContent({ linkSegments: routeSegments }: ContactsAppProps =
addressBooks={addressBooks}
activeCategory={activeCategory}
onSelectCategory={handleSelectCategory}
- onCreateGroup={handleCreateGroup}
- onCreateContact={handleCreateNew}
- onCreateAddressBook={client ? () => setCreatingAddressBook(true) : undefined}
- onImport={() => setShowImportDialog(true)}
- onEditGroup={handleEditGroupFromSidebar}
- onDeleteGroup={handleDeleteGroupFromSidebar}
- onComposeGroup={handleComposeGroupFromSidebar}
+ onCreateGroup={fromDrawer(handleCreateGroup)}
+ onCreateContact={fromDrawer(handleCreateNew)}
+ onCreateAddressBook={client ? fromDrawer(() => setCreatingAddressBook(true)) : undefined}
+ onImport={fromDrawer(() => setShowImportDialog(true))}
+ onEditGroup={fromDrawer(handleEditGroupFromSidebar)}
+ onDeleteGroup={fromDrawer(handleDeleteGroupFromSidebar)}
+ onComposeGroup={fromDrawer(handleComposeGroupFromSidebar)}
onDropContacts={handleDropContacts}
onDropContactsToCategory={handleDropContactsToCategory}
- onRenameAddressBook={client ? (book) => setRenamingAddressBook(book) : undefined}
- onShareAddressBook={client ? (book) => setSharingAddressBookId(book.id) : undefined}
- onSetDefaultAddressBook={client ? async (book) => {
+ onRenameAddressBook={client ? fromDrawer((book: AddressBook) => setRenamingAddressBook(book)) : undefined}
+ onShareAddressBook={client ? fromDrawer((book: AddressBook) => setSharingAddressBookId(book.id)) : undefined}
+ onSetDefaultAddressBook={client ? fromDrawer(async (book: AddressBook) => {
try {
await setDefaultAddressBook(client, book);
toast.success(t("address_books.default_updated"));
} catch {
toast.error(t("address_books.set_default_failed"));
}
- } : undefined}
- onCreateContactInBook={(book) => {
+ }) : undefined}
+ onCreateContactInBook={fromDrawer((book: AddressBook) => {
setDefaultBookIdForCreate(book.id);
setSelectedContact(null);
setView("create");
- }}
- onDeleteAddressBook={client ? async (book) => {
+ })}
+ onDeleteAddressBook={client ? fromDrawer(async (book: AddressBook) => {
const ok = await confirmDialog({
title: t("address_books.delete"),
message: t("address_books.confirm_delete", { name: book.name }),
@@ -1051,8 +1058,8 @@ function ContactsAppContent({ linkSegments: routeSegments }: ContactsAppProps =
} catch {
toast.error(t("address_books.delete_failed"));
}
- } : undefined}
- onRenameKeyword={(kw) => setRenamingKeyword(kw)}
+ }) : undefined}
+ onRenameKeyword={fromDrawer((kw: string) => setRenamingKeyword(kw))}
directoryCount={showDirectory ? directoryPrincipals.length : undefined}
multiAccountMode={multiAccountEnabled && accountClients.length > 1}
/>
diff --git a/components/files/file-browser.tsx b/components/files/file-browser.tsx
index 29f750a38..2af526003 100644
--- a/components/files/file-browser.tsx
+++ b/components/files/file-browser.tsx
@@ -242,6 +242,11 @@ export function FileBrowser({
const [narrowSidebarOpen, setNarrowSidebarOpen] = useState(false);
useEffect(() => { if (!isNarrow) setNarrowSidebarOpen(false); }, [isNarrow]);
useEffect(() => { setNarrowSidebarOpen(false); }, [currentPath]);
+ // Anything picked in the drawer closes it (R7).
+ const fromDrawer = (action: (...args: A) => R) => (...args: A): R => {
+ setNarrowSidebarOpen(false);
+ return action(...args);
+ };
const [sidebarWidth, setSidebarWidth] = useState(() => {
if (typeof window !== "undefined") {
const saved = Number(localStorage.getItem("files-sidebar-width"));
@@ -891,19 +896,19 @@ export function FileBrowser({
canCreate={canCreate}
onNavigate={onNavigate}
onOpenRecent={onOpenRecent}
- onUploadFiles={() => fileInputRef.current?.click()}
- onUploadFolder={() => folderInputRef.current?.click()}
- onNewFolder={() => setShowNewFolder(true)}
- onNewTextFile={() => setShowNewTextFile(true)}
+ onUploadFiles={fromDrawer(() => fileInputRef.current?.click())}
+ onUploadFolder={fromDrawer(() => folderInputRef.current?.click())}
+ onNewFolder={fromDrawer(() => setShowNewFolder(true))}
+ onNewTextFile={fromDrawer(() => setShowNewTextFile(true))}
onToggleFavorite={onToggleFavorite}
onDropItems={moveNames}
canShare={canShare}
- onShare={(r) => setShareTargetId(r.id)}
- onCreateIn={createIn}
- onRenameFolder={setRenameFolder}
- onDeleteFolder={(folder) => void onDeleteNode(folder.id, folder.name)}
- onRevealRecent={(id) => void onRevealNode(id)}
- onDownloadRecent={(id) => void onDownloadNode(id)}
+ onShare={fromDrawer((r: FileResource) => setShareTargetId(r.id))}
+ onCreateIn={fromDrawer(createIn)}
+ onRenameFolder={fromDrawer(setRenameFolder)}
+ onDeleteFolder={fromDrawer((folder: FileResource) => void onDeleteNode(folder.id, folder.name))}
+ onRevealRecent={fromDrawer((id: string) => void onRevealNode(id))}
+ onDownloadRecent={fromDrawer((id: string) => void onDownloadNode(id))}
onRemoveRecent={onRemoveRecent}
/>
);