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} /> );