test(ui): a self-closing dialog leaves the focus in a menu that stays open

This commit is contained in:
2026-10-28 21:54:00 +01:00
parent c86ec7f7a5
commit 25ad2ee1ba
@@ -375,6 +375,36 @@ describe("Modal focus trap (offsetParent stubbed 'visible')", () => {
document.body.removeChild(portal); document.body.removeChild(portal);
}); });
it("does not pull the focus out of a menu that stays open when it closes on its own", () => {
// The other side of counting a menu row as held: a menu that is NOT this dialog's — a page's
// "…" left open while a dialog finishes in the background — stays standing, and a dialog that
// closes on its own (a write's success) must not take the focus from it to its trigger.
const trigger = document.createElement("button");
document.body.appendChild(trigger);
trigger.focus();
const portal = document.createElement("div");
portal.setAttribute("data-floating-ui-portal", "");
const row = document.createElement("button");
row.textContent = "page-menu-row";
portal.appendChild(row);
document.body.appendChild(portal);
function SelfClosing() {
const [open, setOpen] = useState(true);
return open ? (
<Modal initialFocus="close" title="t" onClose={() => {}}>
<button onClick={() => setOpen(false)}>finish</button>
</Modal>
) : null;
}
render(<SelfClosing />);
row.focus();
act(() => void screen.getByText("finish").click()); // `.click()` moves no focus
expect(screen.queryByText("finish")).toBeNull();
expect(document.activeElement).toBe(row);
document.body.removeChild(trigger);
document.body.removeChild(portal);
});
it("leaves the focus alone when the user moved it out of the dialog themselves", () => { it("leaves the focus alone when the user moved it out of the dialog themselves", () => {
// The other half of the same rule, and the branch nothing drove: taking focus back from where // The other half of the same rule, and the branch nothing drove: taking focus back from where
// the user deliberately put it is the rudest thing a closing dialog can do. // the user deliberately put it is the rudest thing a closing dialog can do.