Deep-review of E11 (19 raised, 14 confirmed; 2 refuted, 3 latent test-hardening notes dropped). - Back from a sub-view (a Plex show page, a Messages thread) restores the item it was opened from even when focus was still inside the view: the first attempt now waits one task, past the sub-view's own popstate listener (measured in Chrome: it left focus on <body>). - The restore listener is installed at load, so the first Back after a reload finds it; a push cancels a restore still waiting for the last landing's lists. - Navigating over an overlay asks it again, a render apart, while its entry stays: the Plex player peels one panel per Back step, so Shift+W over info + stats closed one panel and dropped the navigation. - j/k: a native <button> item keeps its own Tab stop; landing twice on the same item no longer leaks its tabindex; after the focused item is removed (a hidden card) the next j goes on from its slot when that slot is on screen. - An abandoned `g` no longer swallows a modifier chord (g, then Mod+K opened the browser search). - A hover card above a dialog no longer switches its Tab trap off (player, Modal, wizard, Lightbox): new layerStack `anyBlockingLayerAbove`. - "Manage views…" closes its menu before opening the dialog, through a shared `closeThenAct` that ActionMenu now uses too. - Copy: the EN welcome says "Feed"; the EN unreachable reason no longer claims a change; stale comments about PlayerModal's scroll lock and dialog contract.
51 lines
1.7 KiB
TypeScript
51 lines
1.7 KiB
TypeScript
// @vitest-environment jsdom
|
|
//
|
|
// The stamp `rememberNavPosition` leaves in history.state survives a reload, but module state does not:
|
|
// the listener that reads it was installed by the first push, so after F5 the first Back onto a
|
|
// stamped entry restored nothing (deep-review E11 round 1). Its own file, because the point is a
|
|
// module that has made NO push yet — any other test in history.navPosition would have made one.
|
|
import { useRef } from "react";
|
|
import { afterEach, expect, it } from "vitest";
|
|
import { act, cleanup, render, waitFor } from "@testing-library/react";
|
|
import "./history";
|
|
import { navItemAttrs, useListNav } from "./listNav";
|
|
|
|
function List({ id, count }: { id: string; count: number }) {
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
useListNav(ref, { enabled: true });
|
|
return (
|
|
<div ref={ref}>
|
|
{Array.from({ length: count }, (_, i) => (
|
|
<div key={i} data-testid={`${id}${i}`} {...navItemAttrs(i)}>
|
|
{id}
|
|
{i}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
window.history.replaceState(null, "");
|
|
});
|
|
|
|
it("restores on a landing before this load has pushed anything", async () => {
|
|
// An entry stamped before the reload: list 1 of 2, item 1.
|
|
const state = { sfPage: "home", sfNav: { list: 1, lists: 2, index: 1 } };
|
|
window.history.replaceState(state, "");
|
|
render(
|
|
<>
|
|
<List id="a" count={2} />
|
|
<List id="b" count={3} />
|
|
</>,
|
|
);
|
|
act(() => {
|
|
window.dispatchEvent(new PopStateEvent("popstate", { state }));
|
|
});
|
|
await waitFor(() =>
|
|
expect((document.activeElement as HTMLElement | null)?.dataset.testid).toBe("b1"),
|
|
);
|
|
expect(window.history.state.sfNav).toBeUndefined();
|
|
});
|