fix(player): a held key on faded bands stays one blind press; a play asked for mid-restart is the new session's
The repeats of a Space/Enter that landed on faded bands pressed the control once the bands were up; they are swallowed with it. A play or pause while a session restart is out now sets that session's intent instead of playing the outgoing stream. Tests: Skip intro outside the bands keeps its keys, every restore takeover (wheel, finger, scrollbar, key) is covered, and the chip handoff test tells the chip before from the row's first control.
This commit is contained in:
@@ -71,10 +71,10 @@ describe("Chip", () => {
|
||||
});
|
||||
|
||||
// Round 3: the neighbour it hands the focus to can go in the same removal — the "Clear all" that
|
||||
// only shows beside two filters. The focus then goes to the chip before, not to <body>.
|
||||
// only shows beside enough filters. The focus then goes to the chip before, not to <body>.
|
||||
it("a removing chip whose neighbour goes with it hands the focus to the chip before", async () => {
|
||||
function Row() {
|
||||
const [chips, setChips] = useState(["Music", "News"]);
|
||||
const [chips, setChips] = useState(["Music", "News", "Sport"]);
|
||||
return (
|
||||
<div>
|
||||
{chips.map((c) => (
|
||||
@@ -87,17 +87,18 @@ describe("Chip", () => {
|
||||
{c}
|
||||
</Chip>
|
||||
))}
|
||||
{chips.length > 1 && <button onClick={() => setChips([])}>Clear all</button>}
|
||||
{chips.length > 2 && <button onClick={() => setChips([])}>Clear all</button>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
render(<Row />);
|
||||
const news = screen.getByRole("button", { name: "Remove News" });
|
||||
act(() => news.focus());
|
||||
fireEvent.click(news, { detail: 0 });
|
||||
const sport = screen.getByRole("button", { name: "Remove Sport" });
|
||||
act(() => sport.focus());
|
||||
fireEvent.click(sport, { detail: 0 });
|
||||
expect(screen.queryByRole("button", { name: "Clear all" })).toBeNull();
|
||||
// the chip right before it — not the row's first control, which Music also is
|
||||
await waitFor(() =>
|
||||
expect(document.activeElement).toBe(screen.getByRole("button", { name: "Remove Music" })),
|
||||
expect(document.activeElement).toBe(screen.getByRole("button", { name: "Remove News" })),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -155,7 +155,14 @@ describe("PageScroller scroll memory", () => {
|
||||
});
|
||||
|
||||
// Round 3: the give-up put the page at its end 1.5 s in, wherever the reader had scrolled to.
|
||||
it("stops where the reader takes the page over, even for a spot it has not reached", () => {
|
||||
// Every way a reader takes the page: a wheel, a finger, the scrollbar (a pointer), a key.
|
||||
it.each([
|
||||
["a wheel", (main: HTMLElement) => fireEvent.wheel(main)],
|
||||
["a finger", (main: HTMLElement) => fireEvent.touchStart(main)],
|
||||
["the scrollbar", (main: HTMLElement) => fireEvent.pointerDown(main)],
|
||||
["a key", () => fireEvent.keyDown(document.body, { key: "ArrowUp" })],
|
||||
])("stops where %s takes the page over, even for a spot it has not reached", (_, take) => {
|
||||
const removed = vi.spyOn(document, "removeEventListener");
|
||||
let frames: FrameRequestCallback[] = [];
|
||||
vi.stubGlobal("requestAnimationFrame", (cb: FrameRequestCallback) => frames.push(cb));
|
||||
const flush = (n: number) => {
|
||||
@@ -177,7 +184,9 @@ describe("PageScroller scroll memory", () => {
|
||||
setEntry(1);
|
||||
notifyNav();
|
||||
flush(5);
|
||||
fireEvent.wheel(main);
|
||||
take(main);
|
||||
// and it let go of the page-wide key listener with it
|
||||
expect(removed).toHaveBeenCalledWith("keydown", expect.any(Function), true);
|
||||
main.scrollTop = 30; // where the reader scrolled
|
||||
flush(100);
|
||||
expect(main.scrollTop).toBe(30);
|
||||
|
||||
Reference in New Issue
Block a user