fix(ui): a membership menu re-homes focus a frame later, once its new rows are on the list
Rows that just mounted join the menu's navigation list a render after the status effect ran: a row focused that early was unknown to it, and the arrows did nothing from it (seen live after a keyboard Retry). The retry test now arrows on from the row it lands on.
This commit is contained in:
@@ -364,7 +364,9 @@ describe("a Plex title's menu — Add to collection", () => {
|
||||
// A menu owns its items only: the line about the list sits beside it, not in it.
|
||||
expect(document.querySelector("[role=menu]")).not.toBeNull();
|
||||
expect(alert.closest("[role=menu]")).toBeNull();
|
||||
list.mockResolvedValue({ collections: [coll("cB", "Beta", false)] });
|
||||
list.mockResolvedValue({
|
||||
collections: [coll("cB", "Beta", false), coll("cC", "Gamma", false)],
|
||||
});
|
||||
// Retry from the keyboard: the focused button goes as the list loads, and focus must not be left
|
||||
// on <body> with the menu open — it lands on the first row that came back.
|
||||
const retry = (await screen.findByText("common.retry")).closest("button")!;
|
||||
@@ -372,6 +374,9 @@ describe("a Plex title's menu — Add to collection", () => {
|
||||
fireEvent.click(retry);
|
||||
const beta = await checkRow("Beta");
|
||||
await vi.waitFor(() => expect(document.activeElement).toBe(beta));
|
||||
// …and the menu's own arrows carry on from there.
|
||||
fireEvent.keyDown(beta, { key: "ArrowDown" });
|
||||
expect(document.activeElement).toBe(await checkRow("Gamma"));
|
||||
});
|
||||
|
||||
it("clears the name when Plex made the collection and only the read after it failed", async () => {
|
||||
|
||||
@@ -79,6 +79,9 @@ export function MembershipMenu({
|
||||
useEffect(() => {
|
||||
if (shownStatus.current === status) return;
|
||||
shownStatus.current = status;
|
||||
// A frame later: rows that just mounted join the menu's list a render after this effect, and a
|
||||
// row focused before that is not known to it — the arrows then did nothing from it.
|
||||
const frame = requestAnimationFrame(() => {
|
||||
const root = rootRef.current;
|
||||
const at = document.activeElement;
|
||||
// …unless a name is being typed into it meanwhile.
|
||||
@@ -91,6 +94,8 @@ export function MembershipMenu({
|
||||
root.querySelector<HTMLElement>("button");
|
||||
target?.focus();
|
||||
placed.current = target;
|
||||
});
|
||||
return () => cancelAnimationFrame(frame);
|
||||
}, [status]);
|
||||
|
||||
async function submit() {
|
||||
|
||||
Reference in New Issue
Block a user