Fix admin modal focus reset

This commit is contained in:
nianzhibai
2026-06-05 12:57:06 +00:00
parent fa7823ef3e
commit 78cfb0a9e5
2 changed files with 26 additions and 3 deletions
+9 -3
View File
@@ -12,8 +12,13 @@ type Props = {
export function Modal({ open, title, onClose, children, footer, className = "" }: Props) {
const dialogRef = useRef<HTMLDivElement>(null);
const onCloseRef = useRef(onClose);
const titleId = useId();
useEffect(() => {
onCloseRef.current = onClose;
}, [onClose]);
useEffect(() => {
if (!open) return;
const previousFocus =
@@ -25,7 +30,7 @@ export function Modal({ open, title, onClose, children, footer, className = "" }
if (e.key === "Escape") {
e.preventDefault();
onClose();
onCloseRef.current();
return;
}
@@ -51,7 +56,7 @@ export function Modal({ open, title, onClose, children, footer, className = "" }
}
}
window.setTimeout(() => {
const focusTimer = window.setTimeout(() => {
const dialog = dialogRef.current;
if (!dialog || !isTopDialog(dialog)) return;
const first = getFocusableElements(dialog)[0];
@@ -60,12 +65,13 @@ export function Modal({ open, title, onClose, children, footer, className = "" }
document.addEventListener("keydown", onKeyDown);
return () => {
window.clearTimeout(focusTimer);
document.removeEventListener("keydown", onKeyDown);
if (previousFocus?.isConnected) {
previousFocus.focus();
}
};
}, [open, onClose]);
}, [open]);
if (!open) return null;
return (
+17
View File
@@ -0,0 +1,17 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
const modalSource = readFileSync(
new URL("../src/admin/Modal.tsx", import.meta.url),
"utf8"
);
test("admin modal does not reset focus when close handler identity changes", () => {
assert.match(modalSource, /const onCloseRef = useRef\(onClose\);/);
assert.match(modalSource, /onCloseRef\.current = onClose;/);
assert.match(modalSource, /onCloseRef\.current\(\);/);
assert.match(modalSource, /window\.clearTimeout\(focusTimer\);/);
assert.match(modalSource, /\}, \[open\]\);/);
assert.doesNotMatch(modalSource, /\}, \[open, onClose\]\);/);
});