diff --git a/frontend/src/modules/files/UploadDialog.dom.test.tsx b/frontend/src/modules/files/UploadDialog.dom.test.tsx index 2ecb2dfd..60a24805 100644 --- a/frontend/src/modules/files/UploadDialog.dom.test.tsx +++ b/frontend/src/modules/files/UploadDialog.dom.test.tsx @@ -218,8 +218,9 @@ describe("UploadDialog · where the file goes", () => { await waitFor(() => expect(h.createFolder).toHaveBeenCalledWith("Trips")); }); - it("says a new folder is being made while Upload waits for it", async () => { - h.createFolder.mockReturnValue(new Promise(() => {})); + it("says a new folder is being made, then gives the keyboard back the list", async () => { + let made: (f: unknown) => void = () => {}; + h.createFolder.mockReturnValue(new Promise((res) => (made = res))); wrap(); fireEvent.click(await target()); fireEvent.click(await screen.findByTestId("upload-target-new")); @@ -227,6 +228,8 @@ describe("UploadDialog · where the file goes", () => { fireEvent.change(field, { target: { value: "Trips" } }); fireEvent.keyDown(field, { key: "Enter" }); expect(await screen.findByText("downloads.upload.makingFolder")).toBeTruthy(); + made({ ...FOLDERS[1], id: 7, name: "Trips" }); + await waitFor(async () => expect(document.activeElement).toBe(await target())); }); // A failed refetch keeps the list it had: the target the file goes to stays shown. diff --git a/frontend/src/modules/files/UploadDialog.tsx b/frontend/src/modules/files/UploadDialog.tsx index 0a3e54f9..91f622ce 100644 --- a/frontend/src/modules/files/UploadDialog.tsx +++ b/frontend/src/modules/files/UploadDialog.tsx @@ -81,13 +81,7 @@ export default function UploadDialog({ const foldersQ = useQuery({ queryKey: qk.fileFolders(), queryFn: api.fileFolders }); const [naming, setNaming] = useState(false); const targetBox = useRef(null); - // A new folder's name ended by a key leaves the keyboard's focus nowhere: back on the list. const refocusTarget = useRef(false); - useEffect(() => { - if (naming || !refocusTarget.current) return; - refocusTarget.current = false; - targetBox.current?.querySelector("button")?.focus(); - }, [naming]); // A press on Cancel, the ✕ or the backdrop blurs the name field before the dialog closes, and the // field saves on blur: that press is "never mind", not "make it". The press is seen before the // blur it causes, in every browser (which control takes the focus, if any, differs: Safari @@ -112,6 +106,15 @@ export default function UploadDialog({ }, meta: { failTitle: "errors.writeFailed.folder.create" }, }); + // A new folder's name ended by a key leaves the keyboard's focus nowhere: back on the list — once + // it is back, as the folder is made (or fails): until then its place says it is being made. + useEffect(() => { + if (naming || !refocusTarget.current) return; + const list = targetBox.current?.querySelector("button"); + if (!list) return; + refocusTarget.current = false; + list.focus(); + }, [naming, makeFolder.isPending]); // Not while a folder is being named or made: the file would go to the last target, not that one. const targetSettling = naming || makeFolder.isPending; const targets = (foldersQ.data ?? []).filter(takesUploads);