fix(ui): a caption never opens under a finger

On an iPad a tap's synthetic mouseenter opened the Tooltip; WebKit then took the
tap as a hover and swallowed its click, so every rail button needed two taps,
and with no mouseleave from a finger the caption stayed up. Tooltip now opens
for a mouse and a key's focus only (owner: no captions on touch).
This commit is contained in:
2026-10-08 02:45:48 +02:00
parent c9b8fe15a3
commit c3664826fe
4 changed files with 47 additions and 1 deletions
@@ -64,3 +64,41 @@ describe("an open tooltip", () => {
await waitFor(() => expect(screen.queryByRole("tooltip")).toBeNull());
});
});
// On an iPad a tap's synthetic mouseenter showed the caption; WebKit then took the tap as a hover and
// swallowed its click (every rail button needed two taps), and with no mouseleave the caption stayed.
describe("a tooltip under a finger", () => {
class PointerEventStub extends MouseEvent {
pointerType: string;
constructor(type: string, init: PointerEventInit = {}) {
super(type, init);
this.pointerType = init.pointerType ?? "";
}
}
const hover = (trigger: HTMLElement, pointerType: string) =>
act(() => {
trigger.dispatchEvent(new PointerEventStub("pointerdown", { bubbles: true, pointerType }));
trigger.dispatchEvent(new PointerEventStub("pointerenter", { pointerType }));
fireEvent.mouseEnter(trigger);
fireEvent.mouseMove(trigger);
});
const mount = () => {
render(
<Tooltip hint="A hint" always>
<button>icon</button>
</Tooltip>,
);
return screen.getByText("icon").parentElement as HTMLElement;
};
it("does not open from a tap", async () => {
hover(mount(), "touch");
await new Promise((r) => setTimeout(r, 50));
expect(screen.queryByRole("tooltip")).toBeNull();
});
it("still opens under a mouse", async () => {
hover(mount(), "mouse");
expect(await screen.findByRole("tooltip")).toBeTruthy();
});
});