From 1724e9ce0aa1db99bcbe2633304b6a0fee2e4e1e Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Wed, 29 Jul 2026 18:21:37 +0200 Subject: [PATCH] [DOM] Blur focused descendants in Fragment refs (#37125) ## Summary `FragmentInstance.blur()` only matched the active element against the first level of host children. If the focused element was nested inside one of those children, `focus()` could reach it but `blur()` would leave it focused. This treats an active element contained by a Fragment host child as part of the Fragment and blurs the active element itself. It also adds regression coverage for a nested input. Fixes #37124. ## How did you test this change? - `yarn test ReactDOMFragmentRefs-test --runInBand` (65 tests passed) - `yarn test --prod ReactDOMFragmentRefs-test --runInBand` (65 tests passed) - `yarn prettier` - `yarn linc` - `yarn flow dom-node` --- .../src/client/ReactFiberConfigDOM.js | 4 +-- .../__tests__/ReactDOMFragmentRefs-test.js | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index fb3e1abb4d5..f914752df06 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -3262,9 +3262,9 @@ function blurActiveElementWithinFragment( return false; } const instance = getInstanceFromHostFiber(child); - if (instance === activeElement) { + if (instance === activeElement || instance.contains(activeElement)) { // $FlowFixMe[prop-missing] - instance.blur(); + activeElement.blur(); return true; } return false; diff --git a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js index 628ebc85bf7..5c55ceffafe 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js @@ -454,6 +454,36 @@ describe('FragmentRefs', () => { expect(document.activeElement).toEqual(document.body); }); + // @gate enableFragmentRefs + it('removes focus from a nested element inside of the Fragment', async () => { + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test() { + return ( + +
+ +
+
+ ); + } + + await act(() => { + root.render(); + }); + + await act(() => { + fragmentRef.current.focus(); + }); + expect(document.activeElement.id).toEqual('nested-input'); + + await act(() => { + fragmentRef.current.blur(); + }); + expect(document.activeElement).toEqual(document.body); + }); + // @gate enableFragmentRefs it('does not remove focus from elements outside of the Fragment', async () => { const fragmentRefA = React.createRef();