fix(player): a link that opens the cut text is scrolled back into view

Focus had scrolled the clamped box itself; opened, the link dropped by that much, out of a scrolling panel's sight. DESIGN states where Close sits in the stacked layout; a stale test comment goes.
This commit is contained in:
2026-10-09 23:46:40 +02:00
parent 2cff4ad954
commit 77638f7a0b
5 changed files with 21 additions and 7 deletions
@@ -65,8 +65,13 @@ describe("ClampedText", () => {
// Chrome: 45 px), so on screen the link sits inside the box: only the text's own top tells.
clamp.scrollTop = 240;
vi.spyOn(screen.getByText("cut"), "getBoundingClientRect").mockReturnValue(rect(130, 150));
// Opened, the link drops back to its place in the text, as far as the box had scrolled: in a
// scrolling panel that is out of sight, so it is brought back into view.
const reveal = vi.fn();
screen.getByText("cut").scrollIntoView = reveal;
act(() => screen.getByText("cut").focus());
expect(screen.getByText("Less").getAttribute("aria-expanded")).toBe("true");
expect(reveal).toHaveBeenCalledWith({ block: "nearest" });
} finally {
tall.mockRestore();
box.mockRestore();
+11 -1
View File
@@ -49,12 +49,22 @@ export function ClampedText({
}, [text, expanded]);
// Measured from the text's top, not against the box on screen: focus scrolls the clamped box itself
// to bring the link into view (overflow:hidden still scrolls), so on screen it looked "in sight".
const revealing = useRef<Element | null>(null);
const revealFocused = (e: FocusEvent<HTMLDivElement>) => {
const box = e.currentTarget;
if (expanded || e.target === box) return;
const top = box.getBoundingClientRect().top - box.scrollTop;
if (e.target.getBoundingClientRect().bottom - top > box.clientHeight + 1) setExpanded(true);
if (e.target.getBoundingClientRect().bottom - top <= box.clientHeight + 1) return;
revealing.current = e.target;
setExpanded(true);
};
// That scroll was the box's own: opened, the link drops back to its place in the text, as far below
// as the box had scrolled, and out of a scrolling panel's sight. Brought back into view.
useLayoutEffect(() => {
const el = revealing.current;
revealing.current = null;
if (expanded && el?.isConnected) el.scrollIntoView({ block: "nearest" });
}, [expanded]);
return (
<div className={className}>
<div