diff --git a/packages/react/src/TreeView/TreeView.test.tsx b/packages/react/src/TreeView/TreeView.test.tsx index 2252c9d339a..26f5f3eeefe 100644 --- a/packages/react/src/TreeView/TreeView.test.tsx +++ b/packages/react/src/TreeView/TreeView.test.tsx @@ -14,7 +14,12 @@ function renderWithTheme( ui: Parameters[0], options?: Parameters[1], ): ReturnType { - return render(ui, options) + const result = render(ui, options) + // Flush deferred focus zone initialization (requestAnimationFrame) + act(() => { + vi.advanceTimersByTime(16) + }) + return result } // Mock `scrollIntoView` because it's not implemented in JSDOM diff --git a/packages/react/src/hooks/useFocusZone.ts b/packages/react/src/hooks/useFocusZone.ts index 94901937564..f1d1a291e24 100644 --- a/packages/react/src/hooks/useFocusZone.ts +++ b/packages/react/src/hooks/useFocusZone.ts @@ -58,8 +58,15 @@ export function useFocusZone( ...settings, activeDescendantControl: activeDescendantControlRef.current ?? undefined, } - abortController.current = focusZone(containerRef.current, vanillaSettings) + const container = containerRef.current + const rafId = requestAnimationFrame(() => { + // Defer focus zone initialization to avoid blocking paint. + // Setting tabindex on focusable elements is invisible to the user, + // and keyboard navigation activating one frame later is imperceptible. + abortController.current = focusZone(container, vanillaSettings) + }) return () => { + cancelAnimationFrame(rafId) abortController.current?.abort() } } else {