fix(web): keep focus in modals while typing (add-artist bug)

The Modal focus-trap effect depended on [open, onClose] and called
panelRef.focus() on every run. Callers that pass a fresh onClose each render
(the common case, e.g. artists-client's closeAdd) re-ran the effect on every
parent re-render — so typing one char into the add-artist search (setQuery →
re-render → new onClose) yanked focus off the <input> back onto the panel,
making the field untypable.

Hold the latest onClose in a ref and depend on [open] alone, so focus-on-open
runs once per open. Fixes every Modal caller, not just add-artist.

Verified live via Playwright: typing "radiohead" one key at a time keeps the
value and document.activeElement on the search input.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-14 10:54:31 +02:00
parent 446a4bc2d2
commit 5e10dc24a5
+8 -2
View File
@@ -22,6 +22,12 @@ export function Modal({
}) {
const panelRef = useRef<HTMLDivElement>(null);
const lastFocus = useRef<HTMLElement | null>(null);
// Keep the latest onClose in a ref so the setup effect can depend on [open] alone. If it
// depended on onClose, a caller passing a fresh callback each render (the common case)
// would re-run the effect on every parent re-render — and panelRef.focus() below would
// yank focus off a just-typed <input> back onto the panel (the add-artist modal bug).
const onCloseRef = useRef(onClose);
onCloseRef.current = onClose;
useEffect(() => {
if (!open) return;
@@ -32,7 +38,7 @@ export function Modal({
function onKey(e: KeyboardEvent) {
if (e.key === "Escape") {
onClose();
onCloseRef.current();
return;
}
if (e.key !== "Tab") return;
@@ -54,7 +60,7 @@ export function Modal({
document.body.style.overflow = prevOverflow;
lastFocus.current?.focus();
};
}, [open, onClose]);
}, [open]);
if (!open || typeof document === "undefined") return null;