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 react-core
    
  8.  */
    
  9. 
    
  10. 'use strict';
    
  11. 
    
  12. import {createEventTarget, setPointerEvent} from 'dom-event-testing-library';
    
  13. 
    
  14. let React;
    
  15. let ReactFeatureFlags;
    
  16. let ReactDOM;
    
  17. let ReactDOMClient;
    
  18. let useFocusWithin;
    
  19. let act;
    
  20. 
    
  21. function initializeModules(hasPointerEvents) {
    
  22.   setPointerEvent(hasPointerEvents);
    
  23.   jest.resetModules();
    
  24.   ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  25.   ReactFeatureFlags.enableScopeAPI = true;
    
  26.   ReactFeatureFlags.enableCreateEventHandleAPI = true;
    
  27.   React = require('react');
    
  28.   ReactDOM = require('react-dom');
    
  29.   ReactDOMClient = require('react-dom/client');
    
  30.   act = require('internal-test-utils').act;
    
  31. 
    
  32.   // TODO: This import throws outside of experimental mode. Figure out better
    
  33.   // strategy for gated imports.
    
  34.   if (__EXPERIMENTAL__ || global.__WWW__) {
    
  35.     useFocusWithin = require('react-interactions/events/focus').useFocusWithin;
    
  36.   }
    
  37. }
    
  38. 
    
  39. const forcePointerEvents = true;
    
  40. const table = [[forcePointerEvents], [!forcePointerEvents]];
    
  41. 
    
  42. describe.each(table)(`useFocus`, hasPointerEvents => {
    
  43.   let container;
    
  44.   let container2;
    
  45. 
    
  46.   beforeEach(() => {
    
  47.     initializeModules(hasPointerEvents);
    
  48.     container = document.createElement('div');
    
  49.     document.body.appendChild(container);
    
  50.     container2 = document.createElement('div');
    
  51.     document.body.appendChild(container2);
    
  52.   });
    
  53. 
    
  54.   afterEach(() => {
    
  55.     ReactDOM.render(null, container);
    
  56.     document.body.removeChild(container);
    
  57.     document.body.removeChild(container2);
    
  58.     container = null;
    
  59.     container2 = null;
    
  60.   });
    
  61. 
    
  62.   describe('disabled', () => {
    
  63.     let onFocusWithinChange, onFocusWithinVisibleChange, ref;
    
  64. 
    
  65.     const componentInit = async () => {
    
  66.       onFocusWithinChange = jest.fn();
    
  67.       onFocusWithinVisibleChange = jest.fn();
    
  68.       ref = React.createRef();
    
  69.       const Component = () => {
    
  70.         const focusWithinRef = useFocusWithin(ref, {
    
  71.           disabled: true,
    
  72.           onFocusWithinChange,
    
  73.           onFocusWithinVisibleChange,
    
  74.         });
    
  75.         return <div ref={focusWithinRef} />;
    
  76.       };
    
  77.       await act(() => {
    
  78.         ReactDOM.render(<Component />, container);
    
  79.       });
    
  80.     };
    
  81. 
    
  82.     // @gate www
    
  83.     it('prevents custom events being dispatched', async () => {
    
  84.       await componentInit();
    
  85.       const target = createEventTarget(ref.current);
    
  86.       target.focus();
    
  87.       target.blur();
    
  88.       expect(onFocusWithinChange).not.toBeCalled();
    
  89.       expect(onFocusWithinVisibleChange).not.toBeCalled();
    
  90.     });
    
  91.   });
    
  92. 
    
  93.   describe('onFocusWithinChange', () => {
    
  94.     let onFocusWithinChange, ref, innerRef, innerRef2;
    
  95. 
    
  96.     const Component = ({show}) => {
    
  97.       const focusWithinRef = useFocusWithin(ref, {
    
  98.         onFocusWithinChange,
    
  99.       });
    
  100.       return (
    
  101.         <div ref={focusWithinRef}>
    
  102.           {show && <input ref={innerRef} />}
    
  103.           <div ref={innerRef2} />
    
  104.         </div>
    
  105.       );
    
  106.     };
    
  107. 
    
  108.     const componentInit = async () => {
    
  109.       onFocusWithinChange = jest.fn();
    
  110.       ref = React.createRef();
    
  111.       innerRef = React.createRef();
    
  112.       innerRef2 = React.createRef();
    
  113.       await act(() => {
    
  114.         ReactDOM.render(<Component show={true} />, container);
    
  115.       });
    
  116.     };
    
  117. 
    
  118.     // @gate www
    
  119.     it('is called after "blur" and "focus" events on focus target', async () => {
    
  120.       await componentInit();
    
  121.       const target = createEventTarget(ref.current);
    
  122.       target.focus();
    
  123.       expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
    
  124.       expect(onFocusWithinChange).toHaveBeenCalledWith(true);
    
  125.       target.blur({relatedTarget: container});
    
  126.       expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
    
  127.       expect(onFocusWithinChange).toHaveBeenCalledWith(false);
    
  128.     });
    
  129. 
    
  130.     // @gate www
    
  131.     it('is called after "blur" and "focus" events on descendants', async () => {
    
  132.       await componentInit();
    
  133.       const target = createEventTarget(innerRef.current);
    
  134.       target.focus();
    
  135.       expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
    
  136.       expect(onFocusWithinChange).toHaveBeenCalledWith(true);
    
  137.       target.blur({relatedTarget: container});
    
  138.       expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
    
  139.       expect(onFocusWithinChange).toHaveBeenCalledWith(false);
    
  140.     });
    
  141. 
    
  142.     // @gate www
    
  143.     it('is only called once when focus moves within and outside the subtree', async () => {
    
  144.       await componentInit();
    
  145.       const node = ref.current;
    
  146.       const innerNode1 = innerRef.current;
    
  147.       const innerNode2 = innerRef.current;
    
  148.       const target = createEventTarget(node);
    
  149.       const innerTarget1 = createEventTarget(innerNode1);
    
  150.       const innerTarget2 = createEventTarget(innerNode2);
    
  151. 
    
  152.       // focus shifts into subtree
    
  153.       innerTarget1.focus();
    
  154.       expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
    
  155.       expect(onFocusWithinChange).toHaveBeenCalledWith(true);
    
  156.       // focus moves around subtree
    
  157.       innerTarget1.blur({relatedTarget: innerNode2});
    
  158.       innerTarget2.focus();
    
  159.       innerTarget2.blur({relatedTarget: node});
    
  160.       target.focus();
    
  161.       target.blur({relatedTarget: innerNode1});
    
  162.       expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
    
  163.       // focus shifts outside subtree
    
  164.       innerTarget1.blur({relatedTarget: container});
    
  165.       expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
    
  166.       expect(onFocusWithinChange).toHaveBeenCalledWith(false);
    
  167.     });
    
  168.   });
    
  169. 
    
  170.   describe('onFocusWithinVisibleChange', () => {
    
  171.     let onFocusWithinVisibleChange, ref, innerRef, innerRef2;
    
  172. 
    
  173.     const Component = ({show}) => {
    
  174.       const focusWithinRef = useFocusWithin(ref, {
    
  175.         onFocusWithinVisibleChange,
    
  176.       });
    
  177.       return (
    
  178.         <div ref={focusWithinRef}>
    
  179.           {show && <input ref={innerRef} />}
    
  180.           <div ref={innerRef2} />
    
  181.         </div>
    
  182.       );
    
  183.     };
    
  184. 
    
  185.     const componentInit = async () => {
    
  186.       onFocusWithinVisibleChange = jest.fn();
    
  187.       ref = React.createRef();
    
  188.       innerRef = React.createRef();
    
  189.       innerRef2 = React.createRef();
    
  190.       await act(() => {
    
  191.         ReactDOM.render(<Component show={true} />, container);
    
  192.       });
    
  193.     };
    
  194. 
    
  195.     // @gate www
    
  196.     it('is called after "focus" and "blur" on focus target if keyboard was used', async () => {
    
  197.       await componentInit();
    
  198.       const target = createEventTarget(ref.current);
    
  199.       const containerTarget = createEventTarget(container);
    
  200.       // use keyboard first
    
  201.       containerTarget.keydown({key: 'Tab'});
    
  202.       target.focus();
    
  203.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
    
  204.       expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
    
  205.       target.blur({relatedTarget: container});
    
  206.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
    
  207.       expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
    
  208.     });
    
  209. 
    
  210.     // @gate www
    
  211.     it('is called after "focus" and "blur" on descendants if keyboard was used', async () => {
    
  212.       await componentInit();
    
  213.       const innerTarget = createEventTarget(innerRef.current);
    
  214.       const containerTarget = createEventTarget(container);
    
  215.       // use keyboard first
    
  216.       containerTarget.keydown({key: 'Tab'});
    
  217.       innerTarget.focus();
    
  218.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
    
  219.       expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
    
  220.       innerTarget.blur({relatedTarget: container});
    
  221.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
    
  222.       expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
    
  223.     });
    
  224. 
    
  225.     // @gate www
    
  226.     it('is called if non-keyboard event is dispatched on target previously focused with keyboard', async () => {
    
  227.       await componentInit();
    
  228.       const node = ref.current;
    
  229.       const innerNode1 = innerRef.current;
    
  230.       const innerNode2 = innerRef2.current;
    
  231. 
    
  232.       const target = createEventTarget(node);
    
  233.       const innerTarget1 = createEventTarget(innerNode1);
    
  234.       const innerTarget2 = createEventTarget(innerNode2);
    
  235.       // use keyboard first
    
  236.       target.focus();
    
  237.       target.keydown({key: 'Tab'});
    
  238.       target.blur({relatedTarget: innerNode1});
    
  239.       innerTarget1.focus();
    
  240.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
    
  241.       expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
    
  242.       // then use pointer on the next target, focus should no longer be visible
    
  243.       innerTarget2.pointerdown();
    
  244.       innerTarget1.blur({relatedTarget: innerNode2});
    
  245.       innerTarget2.focus();
    
  246.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
    
  247.       expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
    
  248.       // then use keyboard again
    
  249.       innerTarget2.keydown({key: 'Tab', shiftKey: true});
    
  250.       innerTarget2.blur({relatedTarget: innerNode1});
    
  251.       innerTarget1.focus();
    
  252.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(3);
    
  253.       expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
    
  254.       // then use pointer on the target, focus should no longer be visible
    
  255.       innerTarget1.pointerdown();
    
  256.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
    
  257.       expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
    
  258.       // onFocusVisibleChange should not be called again
    
  259.       innerTarget1.blur({relatedTarget: container});
    
  260.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
    
  261.     });
    
  262. 
    
  263.     // @gate www
    
  264.     it('is not called after "focus" and "blur" events without keyboard', async () => {
    
  265.       await componentInit();
    
  266.       const innerTarget = createEventTarget(innerRef.current);
    
  267.       innerTarget.pointerdown();
    
  268.       innerTarget.pointerup();
    
  269.       innerTarget.blur({relatedTarget: container});
    
  270.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(0);
    
  271.     });
    
  272. 
    
  273.     // @gate www
    
  274.     it('is only called once when focus moves within and outside the subtree', async () => {
    
  275.       await componentInit();
    
  276.       const node = ref.current;
    
  277.       const innerNode1 = innerRef.current;
    
  278.       const innerNode2 = innerRef2.current;
    
  279.       const target = createEventTarget(node);
    
  280.       const innerTarget1 = createEventTarget(innerNode1);
    
  281.       const innerTarget2 = createEventTarget(innerNode2);
    
  282. 
    
  283.       // focus shifts into subtree
    
  284.       innerTarget1.focus();
    
  285.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
    
  286.       expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
    
  287.       // focus moves around subtree
    
  288.       innerTarget1.blur({relatedTarget: innerNode2});
    
  289.       innerTarget2.focus();
    
  290.       innerTarget2.blur({relatedTarget: node});
    
  291.       target.focus();
    
  292.       target.blur({relatedTarget: innerNode1});
    
  293.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
    
  294.       // focus shifts outside subtree
    
  295.       innerTarget1.blur({relatedTarget: container});
    
  296.       expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
    
  297.       expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
    
  298.     });
    
  299.   });
    
  300. 
    
  301.   // @gate www
    
  302.   it('should correctly handle focus visibility when typing into an input', async () => {
    
  303.     const onFocusWithinVisibleChange = jest.fn();
    
  304.     const ref = React.createRef();
    
  305.     const inputRef = React.createRef();
    
  306.     const Component = () => {
    
  307.       const focusWithinRef = useFocusWithin(ref, {
    
  308.         onFocusWithinVisibleChange,
    
  309.       });
    
  310.       return (
    
  311.         <div ref={focusWithinRef}>
    
  312.           <input ref={inputRef} type="text" />
    
  313.         </div>
    
  314.       );
    
  315.     };
    
  316.     await act(() => {
    
  317.       ReactDOM.render(<Component />, container);
    
  318.     });
    
  319. 
    
  320.     const target = createEventTarget(inputRef.current);
    
  321.     // focus the target
    
  322.     target.pointerdown();
    
  323.     target.focus();
    
  324.     target.keydown({key: 'a'});
    
  325.     expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(0);
    
  326.   });
    
  327. 
    
  328.   describe('onBeforeBlurWithin', () => {
    
  329.     let onBeforeBlurWithin, onAfterBlurWithin, ref, innerRef, innerRef2;
    
  330. 
    
  331.     beforeEach(() => {
    
  332.       onBeforeBlurWithin = jest.fn();
    
  333.       onAfterBlurWithin = jest.fn(e => {
    
  334.         e.persist();
    
  335.       });
    
  336.       ref = React.createRef();
    
  337.       innerRef = React.createRef();
    
  338.       innerRef2 = React.createRef();
    
  339.     });
    
  340. 
    
  341.     // @gate www
    
  342.     it('is called after a focused element is unmounted', async () => {
    
  343.       const Component = ({show}) => {
    
  344.         const focusWithinRef = useFocusWithin(ref, {
    
  345.           onBeforeBlurWithin,
    
  346.           onAfterBlurWithin,
    
  347.         });
    
  348.         return (
    
  349.           <div ref={focusWithinRef}>
    
  350.             {show && <input ref={innerRef} />}
    
  351.             <div ref={innerRef2} />
    
  352.           </div>
    
  353.         );
    
  354.       };
    
  355. 
    
  356.       await act(() => {
    
  357.         ReactDOM.render(<Component show={true} />, container);
    
  358.       });
    
  359. 
    
  360.       const inner = innerRef.current;
    
  361.       const target = createEventTarget(inner);
    
  362.       target.keydown({key: 'Tab'});
    
  363.       target.focus();
    
  364.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
    
  365.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
    
  366.       ReactDOM.render(<Component show={false} />, container);
    
  367.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
    
  368.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
    
  369.       expect(onAfterBlurWithin).toHaveBeenCalledWith(
    
  370.         expect.objectContaining({relatedTarget: inner}),
    
  371.       );
    
  372.     });
    
  373. 
    
  374.     // @gate www
    
  375.     it('is called after a nested focused element is unmounted', async () => {
    
  376.       const Component = ({show}) => {
    
  377.         const focusWithinRef = useFocusWithin(ref, {
    
  378.           onBeforeBlurWithin,
    
  379.           onAfterBlurWithin,
    
  380.         });
    
  381.         return (
    
  382.           <div ref={focusWithinRef}>
    
  383.             {show && (
    
  384.               <div>
    
  385.                 <input ref={innerRef} />
    
  386.               </div>
    
  387.             )}
    
  388.             <div ref={innerRef2} />
    
  389.           </div>
    
  390.         );
    
  391.       };
    
  392. 
    
  393.       await act(() => {
    
  394.         ReactDOM.render(<Component show={true} />, container);
    
  395.       });
    
  396. 
    
  397.       const inner = innerRef.current;
    
  398.       const target = createEventTarget(inner);
    
  399.       target.keydown({key: 'Tab'});
    
  400.       target.focus();
    
  401.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
    
  402.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
    
  403.       ReactDOM.render(<Component show={false} />, container);
    
  404.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
    
  405.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
    
  406.       expect(onAfterBlurWithin).toHaveBeenCalledWith(
    
  407.         expect.objectContaining({relatedTarget: inner}),
    
  408.       );
    
  409.     });
    
  410. 
    
  411.     // @gate www
    
  412.     it('is called after many elements are unmounted', async () => {
    
  413.       const buttonRef = React.createRef();
    
  414.       const inputRef = React.createRef();
    
  415. 
    
  416.       const Component = ({show}) => {
    
  417.         const focusWithinRef = useFocusWithin(ref, {
    
  418.           onBeforeBlurWithin,
    
  419.           onAfterBlurWithin,
    
  420.         });
    
  421.         return (
    
  422.           <div ref={focusWithinRef}>
    
  423.             {show && <button>Press me!</button>}
    
  424.             {show && <button>Press me!</button>}
    
  425.             {show && <input ref={inputRef} />}
    
  426.             {show && <button>Press me!</button>}
    
  427.             {!show && <button ref={buttonRef}>Press me!</button>}
    
  428.             {show && <button>Press me!</button>}
    
  429.             <button>Press me!</button>
    
  430.             <button>Press me!</button>
    
  431.           </div>
    
  432.         );
    
  433.       };
    
  434. 
    
  435.       await act(() => {
    
  436.         ReactDOM.render(<Component show={true} />, container);
    
  437.       });
    
  438. 
    
  439.       inputRef.current.focus();
    
  440.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
    
  441.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
    
  442.       ReactDOM.render(<Component show={false} />, container);
    
  443.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
    
  444.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
    
  445.     });
    
  446. 
    
  447.     // @gate www
    
  448.     it('is called after a nested focused element is unmounted (with scope query)', async () => {
    
  449.       const TestScope = React.unstable_Scope;
    
  450.       const testScopeQuery = (type, props) => true;
    
  451.       let targetNodes;
    
  452.       let targetNode;
    
  453. 
    
  454.       const Component = ({show}) => {
    
  455.         const scopeRef = React.useRef(null);
    
  456.         const focusWithinRef = useFocusWithin(scopeRef, {
    
  457.           onBeforeBlurWithin(event) {
    
  458.             const scope = scopeRef.current;
    
  459.             targetNode = innerRef.current;
    
  460.             targetNodes = scope.DO_NOT_USE_queryAllNodes(testScopeQuery);
    
  461.           },
    
  462.         });
    
  463. 
    
  464.         return (
    
  465.           <TestScope ref={focusWithinRef}>
    
  466.             {show && <input ref={innerRef} />}
    
  467.           </TestScope>
    
  468.         );
    
  469.       };
    
  470. 
    
  471.       await act(() => {
    
  472.         ReactDOM.render(<Component show={true} />, container);
    
  473.       });
    
  474. 
    
  475.       const inner = innerRef.current;
    
  476.       const target = createEventTarget(inner);
    
  477.       target.keydown({key: 'Tab'});
    
  478.       target.focus();
    
  479.       await act(() => {
    
  480.         ReactDOM.render(<Component show={false} />, container);
    
  481.       });
    
  482.       expect(targetNodes).toEqual([targetNode]);
    
  483.     });
    
  484. 
    
  485.     // @gate www
    
  486.     it('is called after a focused suspended element is hidden', async () => {
    
  487.       const Suspense = React.Suspense;
    
  488.       let suspend = false;
    
  489.       let resolve;
    
  490.       const promise = new Promise(resolvePromise => (resolve = resolvePromise));
    
  491. 
    
  492.       function Child() {
    
  493.         if (suspend) {
    
  494.           throw promise;
    
  495.         } else {
    
  496.           return <input ref={innerRef} />;
    
  497.         }
    
  498.       }
    
  499. 
    
  500.       const Component = ({show}) => {
    
  501.         const focusWithinRef = useFocusWithin(ref, {
    
  502.           onBeforeBlurWithin,
    
  503.           onAfterBlurWithin,
    
  504.         });
    
  505. 
    
  506.         return (
    
  507.           <div ref={focusWithinRef}>
    
  508.             <Suspense fallback="Loading...">
    
  509.               <Child />
    
  510.             </Suspense>
    
  511.           </div>
    
  512.         );
    
  513.       };
    
  514. 
    
  515.       const root = ReactDOMClient.createRoot(container2);
    
  516. 
    
  517.       await act(() => {
    
  518.         root.render(<Component />);
    
  519.       });
    
  520.       expect(container2.innerHTML).toBe('<div><input></div>');
    
  521. 
    
  522.       const inner = innerRef.current;
    
  523.       const target = createEventTarget(inner);
    
  524.       target.keydown({key: 'Tab'});
    
  525.       target.focus();
    
  526.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
    
  527.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
    
  528. 
    
  529.       suspend = true;
    
  530.       await act(() => {
    
  531.         root.render(<Component />);
    
  532.       });
    
  533.       expect(container2.innerHTML).toBe(
    
  534.         '<div><input style="display: none;">Loading...</div>',
    
  535.       );
    
  536.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
    
  537.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
    
  538.       resolve();
    
  539.     });
    
  540. 
    
  541.     // @gate www
    
  542.     it('is called after a focused suspended element is hidden then shown', async () => {
    
  543.       const Suspense = React.Suspense;
    
  544.       let suspend = false;
    
  545.       let resolve;
    
  546.       const promise = new Promise(resolvePromise => (resolve = resolvePromise));
    
  547.       const buttonRef = React.createRef();
    
  548. 
    
  549.       function Child() {
    
  550.         if (suspend) {
    
  551.           throw promise;
    
  552.         } else {
    
  553.           return <input ref={innerRef} />;
    
  554.         }
    
  555.       }
    
  556. 
    
  557.       const Component = ({show}) => {
    
  558.         const focusWithinRef = useFocusWithin(ref, {
    
  559.           onBeforeBlurWithin,
    
  560.           onAfterBlurWithin,
    
  561.         });
    
  562. 
    
  563.         return (
    
  564.           <div ref={focusWithinRef}>
    
  565.             <Suspense fallback={<button ref={buttonRef}>Loading...</button>}>
    
  566.               <Child />
    
  567.             </Suspense>
    
  568.           </div>
    
  569.         );
    
  570.       };
    
  571. 
    
  572.       const root = ReactDOMClient.createRoot(container2);
    
  573. 
    
  574.       await act(() => {
    
  575.         root.render(<Component />);
    
  576.       });
    
  577. 
    
  578.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
    
  579.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
    
  580. 
    
  581.       suspend = true;
    
  582.       await act(() => {
    
  583.         root.render(<Component />);
    
  584.       });
    
  585.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
    
  586.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
    
  587. 
    
  588.       await act(() => {
    
  589.         root.render(<Component />);
    
  590.       });
    
  591.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
    
  592.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
    
  593. 
    
  594.       buttonRef.current.focus();
    
  595.       suspend = false;
    
  596.       await act(() => {
    
  597.         root.render(<Component />);
    
  598.       });
    
  599.       expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
    
  600.       expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
    
  601. 
    
  602.       resolve();
    
  603.     });
    
  604.   });
    
  605. });