From 308e154978ecb7a424ea16ab185afea372ce16bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Mon, 5 Oct 2026 07:51:38 +0200 Subject: [PATCH] fix(coach): move focus to the composer before hiding the jump button Jumping to the latest message pinned the thread first, which hides the button behind aria-hidden, and only then moved focus to the composer. Focus now leaves the button before its state changes. --- .../coach-panel/__tests__/scroll-to-bottom-button.test.tsx | 4 +++- src/components/insights/coach-panel/message-thread.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/insights/coach-panel/__tests__/scroll-to-bottom-button.test.tsx b/src/components/insights/coach-panel/__tests__/scroll-to-bottom-button.test.tsx index 8472ec2f6..f5a0fcc2a 100644 --- a/src/components/insights/coach-panel/__tests__/scroll-to-bottom-button.test.tsx +++ b/src/components/insights/coach-panel/__tests__/scroll-to-bottom-button.test.tsx @@ -57,8 +57,10 @@ describe("", () => { expect(src).toContain( "", ); + // Focus leaves the button BEFORE the state that hides it changes: a + // focused control under aria-hidden is announced as nothing. expect(src).toMatch( - /scrollToLatest = useCallback\([\s\S]*?wasPinnedRef\.current = true;[\s\S]*?focusCoachComposer\(\);/, + /scrollToLatest = useCallback\(\(\) => \{[^}]*?focusCoachComposer\(\);[^}]*?setPinned\(true\);/, ); }); }); diff --git a/src/components/insights/coach-panel/message-thread.tsx b/src/components/insights/coach-panel/message-thread.tsx index c80672533..3d2981f04 100644 --- a/src/components/insights/coach-panel/message-thread.tsx +++ b/src/components/insights/coach-panel/message-thread.tsx @@ -394,10 +394,12 @@ export function MessageThread({ const scrollToLatest = useCallback(() => { const el = scrollerRef.current; if (!el) return; + // Focus first: pinning hides this button (aria-hidden), and focus must + // not sit on a control that has left the accessibility tree. + focusCoachComposer(); wasPinnedRef.current = true; setPinned(true); el.scrollTo({ top: el.scrollHeight, behavior: scrollBehaviorForUser() }); - focusCoachComposer(); }, []); // Auto-scroll on new messages OR streaming-content growth, but only