@samitouri / QOS-React / commits / 862e275a1c

[DOM] Handle blur on Fragments below `Document` (#37062)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

Sebastian "Sebbie" Silbermann committed Jul 21, 2026 at 10:26 UTC 862e275a1c150edcefab6d8ba3306192ffd6edfa
2 files changed +73 -2
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+5 -2
@@ -3232,8 +3232,11 @@ FragmentInstance.prototype.blur = function (this: FragmentInstanceType): void {
3232 const parentInstanceOrContainer = getInstanceFromHostFiber<
3233 Instance | Container,
3234 >(parentHostFiber);
3235 - // TODO: Handle parentInstanceOrContainer being a document
3236 - const activeElement = parentInstanceOrContainer.ownerDocument.activeElement;
3235 + // Instance is included in the Container type for DOM.
3236 + const ownerDocument = getOwnerDocumentFromRootContainer(
3237 + parentInstanceOrContainer,
3238 + );
3239 + const activeElement = ownerDocument.activeElement;
3240 if (
3241 activeElement === null ||
3242 !parentInstanceOrContainer.contains(activeElement)
packages/react-dom/src/__tests__/ReactDOMFragmentRefsDocument-test.js new
+68
@@ -0,0 +1,68 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails reactcore
8 + * @jest-environment node
9 + */
10 +
11 +'use strict';
12 +
13 +let JSDOM;
14 +let React;
15 +let ReactDOMClient;
16 +let act;
17 +let document;
18 +let Fragment;
19 +
20 +describe('FragmentRefs', () => {
21 + beforeEach(() => {
22 + jest.resetModules();
23 + JSDOM = require('jsdom');
24 + React = require('react');
25 + Fragment = React.Fragment;
26 + ReactDOMClient = require('react-dom/client');
27 + act = require('internal-test-utils').act;
28 +
29 + const jsdom = new JSDOM.JSDOM('');
30 + document = jsdom.window.document;
31 + global.window = jsdom.window;
32 + global.document = global.window.document;
33 + });
34 +
35 + describe('focus methods', () => {
36 + describe('blur()', () => {
37 + // @gate enableFragmentRefs
38 + it('throws when the nearest host parent is a Document container', async () => {
39 + const fragmentRef = React.createRef();
40 + const root = ReactDOMClient.createRoot(document);
41 +
42 + await act(() => {
43 + root.render(
44 + <Fragment ref={fragmentRef}>
45 + <html>
46 + <body>
47 + <a id="child-a" href="/">
48 + A
49 + </a>
50 + </body>
51 + </html>
52 + </Fragment>,
53 + );
54 + });
55 +
56 + await act(() => {
57 + fragmentRef.current.focus();
58 + });
59 + expect(document.activeElement.id).toEqual('child-a');
60 +
61 + await act(() => {
62 + fragmentRef.current.blur();
63 + });
64 + expect(document.activeElement).toEqual(document.body);
65 + });
66 + });
67 + });
68 +});