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} from 'dom-event-testing-library';
    
  13. 
    
  14. let React;
    
  15. let ReactFeatureFlags;
    
  16. let ReactDOM;
    
  17. let ReactDOMClient;
    
  18. let ReactDOMServer;
    
  19. let Scheduler;
    
  20. let act;
    
  21. let waitForAll;
    
  22. let waitFor;
    
  23. 
    
  24. function dispatchEvent(element, type) {
    
  25.   const event = document.createEvent('Event');
    
  26.   event.initEvent(type, true, true);
    
  27.   element.dispatchEvent(event);
    
  28. }
    
  29. 
    
  30. function dispatchClickEvent(element) {
    
  31.   dispatchEvent(element, 'click');
    
  32. }
    
  33. 
    
  34. const eventListenersToClear = [];
    
  35. 
    
  36. function startNativeEventListenerClearDown() {
    
  37.   const nativeWindowEventListener = window.addEventListener;
    
  38.   window.addEventListener = function (...params) {
    
  39.     eventListenersToClear.push({target: window, params});
    
  40.     return nativeWindowEventListener.apply(this, params);
    
  41.   };
    
  42.   const nativeDocumentEventListener = document.addEventListener;
    
  43.   document.addEventListener = function (...params) {
    
  44.     eventListenersToClear.push({target: document, params});
    
  45.     return nativeDocumentEventListener.apply(this, params);
    
  46.   };
    
  47. }
    
  48. 
    
  49. function endNativeEventListenerClearDown() {
    
  50.   eventListenersToClear.forEach(({target, params}) => {
    
  51.     target.removeEventListener(...params);
    
  52.   });
    
  53. }
    
  54. 
    
  55. describe('DOMPluginEventSystem', () => {
    
  56.   let container;
    
  57. 
    
  58.   function withEnableLegacyFBSupport(enableLegacyFBSupport) {
    
  59.     describe(
    
  60.       'enableLegacyFBSupport ' +
    
  61.         (enableLegacyFBSupport ? 'enabled' : 'disabled'),
    
  62.       () => {
    
  63.         beforeAll(() => {
    
  64.           // These tests are run twice, once with legacyFBSupport enabled and once disabled.
    
  65.           // The document needs to be cleaned up a bit before the second pass otherwise it is
    
  66.           // operating in a non pristine environment
    
  67.           document.removeChild(document.documentElement);
    
  68.           document.appendChild(document.createElement('html'));
    
  69.           document.documentElement.appendChild(document.createElement('head'));
    
  70.           document.documentElement.appendChild(document.createElement('body'));
    
  71.         });
    
  72. 
    
  73.         beforeEach(() => {
    
  74.           jest.resetModules();
    
  75.           ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  76.           ReactFeatureFlags.enableLegacyFBSupport = enableLegacyFBSupport;
    
  77. 
    
  78.           React = require('react');
    
  79.           ReactDOM = require('react-dom');
    
  80.           ReactDOMClient = require('react-dom/client');
    
  81.           Scheduler = require('scheduler');
    
  82.           ReactDOMServer = require('react-dom/server');
    
  83. 
    
  84.           const InternalTestUtils = require('internal-test-utils');
    
  85.           waitForAll = InternalTestUtils.waitForAll;
    
  86.           waitFor = InternalTestUtils.waitFor;
    
  87.           act = InternalTestUtils.act;
    
  88. 
    
  89.           container = document.createElement('div');
    
  90.           document.body.appendChild(container);
    
  91.           startNativeEventListenerClearDown();
    
  92.         });
    
  93. 
    
  94.         afterEach(() => {
    
  95.           document.body.removeChild(container);
    
  96.           container = null;
    
  97.           endNativeEventListenerClearDown();
    
  98.         });
    
  99. 
    
  100.         it('does not pool events', () => {
    
  101.           const buttonRef = React.createRef();
    
  102.           const log = [];
    
  103.           const onClick = jest.fn(e => log.push(e));
    
  104. 
    
  105.           function Test() {
    
  106.             return <button ref={buttonRef} onClick={onClick} />;
    
  107.           }
    
  108. 
    
  109.           ReactDOM.render(<Test />, container);
    
  110. 
    
  111.           const buttonElement = buttonRef.current;
    
  112.           dispatchClickEvent(buttonElement);
    
  113.           expect(onClick).toHaveBeenCalledTimes(1);
    
  114.           dispatchClickEvent(buttonElement);
    
  115.           expect(onClick).toHaveBeenCalledTimes(2);
    
  116.           expect(log[0]).not.toBe(log[1]);
    
  117.           expect(log[0].type).toBe('click');
    
  118.           expect(log[1].type).toBe('click');
    
  119.         });
    
  120. 
    
  121.         it('handle propagation of click events', () => {
    
  122.           const buttonRef = React.createRef();
    
  123.           const divRef = React.createRef();
    
  124.           const log = [];
    
  125.           const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  126.           const onClickCapture = jest.fn(e =>
    
  127.             log.push(['capture', e.currentTarget]),
    
  128.           );
    
  129. 
    
  130.           function Test() {
    
  131.             return (
    
  132.               <button
    
  133.                 ref={buttonRef}
    
  134.                 onClick={onClick}
    
  135.                 onClickCapture={onClickCapture}>
    
  136.                 <div
    
  137.                   ref={divRef}
    
  138.                   onClick={onClick}
    
  139.                   onClickCapture={onClickCapture}>
    
  140.                   Click me!
    
  141.                 </div>
    
  142.               </button>
    
  143.             );
    
  144.           }
    
  145. 
    
  146.           ReactDOM.render(<Test />, container);
    
  147. 
    
  148.           const buttonElement = buttonRef.current;
    
  149.           dispatchClickEvent(buttonElement);
    
  150. 
    
  151.           expect(onClick).toHaveBeenCalledTimes(1);
    
  152.           expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  153.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  154.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  155. 
    
  156.           const divElement = divRef.current;
    
  157.           dispatchClickEvent(divElement);
    
  158.           expect(onClick).toHaveBeenCalledTimes(3);
    
  159.           expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  160.           expect(log[2]).toEqual(['capture', buttonElement]);
    
  161.           expect(log[3]).toEqual(['capture', divElement]);
    
  162.           expect(log[4]).toEqual(['bubble', divElement]);
    
  163.           expect(log[5]).toEqual(['bubble', buttonElement]);
    
  164.         });
    
  165. 
    
  166.         it('handle propagation of click events combined with sync clicks', () => {
    
  167.           const buttonRef = React.createRef();
    
  168.           let clicks = 0;
    
  169. 
    
  170.           function Test() {
    
  171.             const inputRef = React.useRef(null);
    
  172.             return (
    
  173.               <div>
    
  174.                 <button
    
  175.                   ref={buttonRef}
    
  176.                   onClick={() => {
    
  177.                     // Sync click
    
  178.                     inputRef.current.click();
    
  179.                   }}
    
  180.                 />
    
  181.                 <input
    
  182.                   ref={inputRef}
    
  183.                   onClick={() => {
    
  184.                     clicks++;
    
  185.                   }}
    
  186.                 />
    
  187.               </div>
    
  188.             );
    
  189.           }
    
  190. 
    
  191.           ReactDOM.render(<Test />, container);
    
  192. 
    
  193.           const buttonElement = buttonRef.current;
    
  194.           dispatchClickEvent(buttonElement);
    
  195. 
    
  196.           expect(clicks).toBe(1);
    
  197.         });
    
  198. 
    
  199.         it('handle propagation of click events between roots', () => {
    
  200.           const buttonRef = React.createRef();
    
  201.           const divRef = React.createRef();
    
  202.           const childRef = React.createRef();
    
  203.           const log = [];
    
  204.           const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  205.           const onClickCapture = jest.fn(e =>
    
  206.             log.push(['capture', e.currentTarget]),
    
  207.           );
    
  208. 
    
  209.           function Child() {
    
  210.             return (
    
  211.               <div
    
  212.                 ref={divRef}
    
  213.                 onClick={onClick}
    
  214.                 onClickCapture={onClickCapture}>
    
  215.                 Click me!
    
  216.               </div>
    
  217.             );
    
  218.           }
    
  219. 
    
  220.           function Parent() {
    
  221.             return (
    
  222.               <button
    
  223.                 ref={buttonRef}
    
  224.                 onClick={onClick}
    
  225.                 onClickCapture={onClickCapture}>
    
  226.                 <div ref={childRef} />
    
  227.               </button>
    
  228.             );
    
  229.           }
    
  230. 
    
  231.           ReactDOM.render(<Parent />, container);
    
  232.           ReactDOM.render(<Child />, childRef.current);
    
  233. 
    
  234.           const buttonElement = buttonRef.current;
    
  235.           dispatchClickEvent(buttonElement);
    
  236.           expect(onClick).toHaveBeenCalledTimes(1);
    
  237.           expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  238.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  239.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  240. 
    
  241.           const divElement = divRef.current;
    
  242.           dispatchClickEvent(divElement);
    
  243.           expect(onClick).toHaveBeenCalledTimes(3);
    
  244.           expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  245.           expect(log[2]).toEqual(['capture', buttonElement]);
    
  246.           expect(log[3]).toEqual(['capture', divElement]);
    
  247.           expect(log[4]).toEqual(['bubble', divElement]);
    
  248.           expect(log[5]).toEqual(['bubble', buttonElement]);
    
  249.         });
    
  250. 
    
  251.         it('handle propagation of click events between disjointed roots', () => {
    
  252.           const buttonRef = React.createRef();
    
  253.           const divRef = React.createRef();
    
  254.           const log = [];
    
  255.           const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  256.           const onClickCapture = jest.fn(e =>
    
  257.             log.push(['capture', e.currentTarget]),
    
  258.           );
    
  259. 
    
  260.           function Child() {
    
  261.             return (
    
  262.               <div
    
  263.                 ref={divRef}
    
  264.                 onClick={onClick}
    
  265.                 onClickCapture={onClickCapture}>
    
  266.                 Click me!
    
  267.               </div>
    
  268.             );
    
  269.           }
    
  270. 
    
  271.           function Parent() {
    
  272.             return (
    
  273.               <button
    
  274.                 ref={buttonRef}
    
  275.                 onClick={onClick}
    
  276.                 onClickCapture={onClickCapture}
    
  277.               />
    
  278.             );
    
  279.           }
    
  280. 
    
  281.           const disjointedNode = document.createElement('div');
    
  282.           ReactDOM.render(<Parent />, container);
    
  283.           buttonRef.current.appendChild(disjointedNode);
    
  284.           ReactDOM.render(<Child />, disjointedNode);
    
  285. 
    
  286.           const buttonElement = buttonRef.current;
    
  287.           dispatchClickEvent(buttonElement);
    
  288.           expect(onClick).toHaveBeenCalledTimes(1);
    
  289.           expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  290.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  291.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  292. 
    
  293.           const divElement = divRef.current;
    
  294.           dispatchClickEvent(divElement);
    
  295.           expect(onClick).toHaveBeenCalledTimes(3);
    
  296.           expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  297.           expect(log[2]).toEqual(['capture', buttonElement]);
    
  298.           expect(log[3]).toEqual(['capture', divElement]);
    
  299.           expect(log[4]).toEqual(['bubble', divElement]);
    
  300.           expect(log[5]).toEqual(['bubble', buttonElement]);
    
  301.         });
    
  302. 
    
  303.         it('handle propagation of click events between disjointed roots #2', () => {
    
  304.           const buttonRef = React.createRef();
    
  305.           const button2Ref = React.createRef();
    
  306.           const divRef = React.createRef();
    
  307.           const spanRef = React.createRef();
    
  308.           const log = [];
    
  309.           const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  310.           const onClickCapture = jest.fn(e =>
    
  311.             log.push(['capture', e.currentTarget]),
    
  312.           );
    
  313. 
    
  314.           function Child() {
    
  315.             return (
    
  316.               <div
    
  317.                 ref={divRef}
    
  318.                 onClick={onClick}
    
  319.                 onClickCapture={onClickCapture}>
    
  320.                 Click me!
    
  321.               </div>
    
  322.             );
    
  323.           }
    
  324. 
    
  325.           function Parent() {
    
  326.             return (
    
  327.               <button
    
  328.                 ref={button2Ref}
    
  329.                 onClick={onClick}
    
  330.                 onClickCapture={onClickCapture}
    
  331.               />
    
  332.             );
    
  333.           }
    
  334. 
    
  335.           function GrandParent() {
    
  336.             return (
    
  337.               <button
    
  338.                 ref={buttonRef}
    
  339.                 onClick={onClick}
    
  340.                 onClickCapture={onClickCapture}>
    
  341.                 <span ref={spanRef} />
    
  342.               </button>
    
  343.             );
    
  344.           }
    
  345. 
    
  346.           // We make a wrapper with an inner container that we
    
  347.           // render to. So it looks like <div><span></span></div>
    
  348.           // We then render to all three:
    
  349.           // - container
    
  350.           // - parentContainer
    
  351.           // - childContainer
    
  352. 
    
  353.           const parentContainer = document.createElement('div');
    
  354.           const childContainer = document.createElement('div');
    
  355. 
    
  356.           ReactDOM.render(<GrandParent />, container);
    
  357.           ReactDOM.render(<Parent />, parentContainer);
    
  358.           ReactDOM.render(<Child />, childContainer);
    
  359. 
    
  360.           parentContainer.appendChild(childContainer);
    
  361.           spanRef.current.appendChild(parentContainer);
    
  362. 
    
  363.           // Inside <GrandParent />
    
  364.           const buttonElement = buttonRef.current;
    
  365.           dispatchClickEvent(buttonElement);
    
  366.           expect(onClick).toHaveBeenCalledTimes(1);
    
  367.           expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  368.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  369.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  370. 
    
  371.           // Inside <Child />
    
  372.           const divElement = divRef.current;
    
  373.           dispatchClickEvent(divElement);
    
  374.           expect(onClick).toHaveBeenCalledTimes(3);
    
  375.           expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  376.           expect(log[2]).toEqual(['capture', buttonElement]);
    
  377.           expect(log[3]).toEqual(['capture', divElement]);
    
  378.           expect(log[4]).toEqual(['bubble', divElement]);
    
  379.           expect(log[5]).toEqual(['bubble', buttonElement]);
    
  380. 
    
  381.           // Inside <Parent />
    
  382.           const buttonElement2 = button2Ref.current;
    
  383.           dispatchClickEvent(buttonElement2);
    
  384.           expect(onClick).toHaveBeenCalledTimes(5);
    
  385.           expect(onClickCapture).toHaveBeenCalledTimes(5);
    
  386.           expect(log[6]).toEqual(['capture', buttonElement]);
    
  387.           expect(log[7]).toEqual(['capture', buttonElement2]);
    
  388.           expect(log[8]).toEqual(['bubble', buttonElement2]);
    
  389.           expect(log[9]).toEqual(['bubble', buttonElement]);
    
  390.         });
    
  391. 
    
  392.         it('handle propagation of click events between disjointed comment roots', () => {
    
  393.           const buttonRef = React.createRef();
    
  394.           const divRef = React.createRef();
    
  395.           const log = [];
    
  396.           const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  397.           const onClickCapture = jest.fn(e =>
    
  398.             log.push(['capture', e.currentTarget]),
    
  399.           );
    
  400. 
    
  401.           function Child() {
    
  402.             return (
    
  403.               <div
    
  404.                 ref={divRef}
    
  405.                 onClick={onClick}
    
  406.                 onClickCapture={onClickCapture}>
    
  407.                 Click me!
    
  408.               </div>
    
  409.             );
    
  410.           }
    
  411. 
    
  412.           function Parent() {
    
  413.             return (
    
  414.               <button
    
  415.                 ref={buttonRef}
    
  416.                 onClick={onClick}
    
  417.                 onClickCapture={onClickCapture}
    
  418.               />
    
  419.             );
    
  420.           }
    
  421. 
    
  422.           // We use a comment node here, then mount to it
    
  423.           const disjointedNode = document.createComment(
    
  424.             ' react-mount-point-unstable ',
    
  425.           );
    
  426.           ReactDOM.render(<Parent />, container);
    
  427.           buttonRef.current.appendChild(disjointedNode);
    
  428.           ReactDOM.render(<Child />, disjointedNode);
    
  429. 
    
  430.           const buttonElement = buttonRef.current;
    
  431.           dispatchClickEvent(buttonElement);
    
  432.           expect(onClick).toHaveBeenCalledTimes(1);
    
  433.           expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  434.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  435.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  436. 
    
  437.           const divElement = divRef.current;
    
  438.           dispatchClickEvent(divElement);
    
  439.           expect(onClick).toHaveBeenCalledTimes(3);
    
  440.           expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  441.           expect(log[2]).toEqual(['capture', buttonElement]);
    
  442.           expect(log[3]).toEqual(['capture', divElement]);
    
  443.           expect(log[4]).toEqual(['bubble', divElement]);
    
  444.           expect(log[5]).toEqual(['bubble', buttonElement]);
    
  445.         });
    
  446. 
    
  447.         it('handle propagation of click events between disjointed comment roots #2', () => {
    
  448.           const buttonRef = React.createRef();
    
  449.           const divRef = React.createRef();
    
  450.           const spanRef = React.createRef();
    
  451.           const log = [];
    
  452.           const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  453.           const onClickCapture = jest.fn(e =>
    
  454.             log.push(['capture', e.currentTarget]),
    
  455.           );
    
  456. 
    
  457.           function Child() {
    
  458.             return (
    
  459.               <div
    
  460.                 ref={divRef}
    
  461.                 onClick={onClick}
    
  462.                 onClickCapture={onClickCapture}>
    
  463.                 Click me!
    
  464.               </div>
    
  465.             );
    
  466.           }
    
  467. 
    
  468.           function Parent() {
    
  469.             return (
    
  470.               <button
    
  471.                 ref={buttonRef}
    
  472.                 onClick={onClick}
    
  473.                 onClickCapture={onClickCapture}>
    
  474.                 <span ref={spanRef} />
    
  475.               </button>
    
  476.             );
    
  477.           }
    
  478. 
    
  479.           // We use a comment node here, then mount to it
    
  480.           const disjointedNode = document.createComment(
    
  481.             ' react-mount-point-unstable ',
    
  482.           );
    
  483.           ReactDOM.render(<Parent />, container);
    
  484.           spanRef.current.appendChild(disjointedNode);
    
  485.           ReactDOM.render(<Child />, disjointedNode);
    
  486. 
    
  487.           const buttonElement = buttonRef.current;
    
  488.           dispatchClickEvent(buttonElement);
    
  489.           expect(onClick).toHaveBeenCalledTimes(1);
    
  490.           expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  491.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  492.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  493. 
    
  494.           const divElement = divRef.current;
    
  495.           dispatchClickEvent(divElement);
    
  496.           expect(onClick).toHaveBeenCalledTimes(3);
    
  497.           expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  498.           expect(log[2]).toEqual(['capture', buttonElement]);
    
  499.           expect(log[3]).toEqual(['capture', divElement]);
    
  500.           expect(log[4]).toEqual(['bubble', divElement]);
    
  501.           expect(log[5]).toEqual(['bubble', buttonElement]);
    
  502.         });
    
  503. 
    
  504.         it('handle propagation of click events between portals', () => {
    
  505.           const buttonRef = React.createRef();
    
  506.           const divRef = React.createRef();
    
  507.           const log = [];
    
  508.           const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  509.           const onClickCapture = jest.fn(e =>
    
  510.             log.push(['capture', e.currentTarget]),
    
  511.           );
    
  512. 
    
  513.           const portalElement = document.createElement('div');
    
  514.           document.body.appendChild(portalElement);
    
  515. 
    
  516.           function Child() {
    
  517.             return (
    
  518.               <div
    
  519.                 ref={divRef}
    
  520.                 onClick={onClick}
    
  521.                 onClickCapture={onClickCapture}>
    
  522.                 Click me!
    
  523.               </div>
    
  524.             );
    
  525.           }
    
  526. 
    
  527.           function Parent() {
    
  528.             return (
    
  529.               <button
    
  530.                 ref={buttonRef}
    
  531.                 onClick={onClick}
    
  532.                 onClickCapture={onClickCapture}>
    
  533.                 {ReactDOM.createPortal(<Child />, portalElement)}
    
  534.               </button>
    
  535.             );
    
  536.           }
    
  537. 
    
  538.           ReactDOM.render(<Parent />, container);
    
  539. 
    
  540.           const buttonElement = buttonRef.current;
    
  541.           dispatchClickEvent(buttonElement);
    
  542.           expect(onClick).toHaveBeenCalledTimes(1);
    
  543.           expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  544.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  545.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  546. 
    
  547.           const divElement = divRef.current;
    
  548.           dispatchClickEvent(divElement);
    
  549.           expect(onClick).toHaveBeenCalledTimes(3);
    
  550.           expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  551.           expect(log[2]).toEqual(['capture', buttonElement]);
    
  552.           expect(log[3]).toEqual(['capture', divElement]);
    
  553.           expect(log[4]).toEqual(['bubble', divElement]);
    
  554.           expect(log[5]).toEqual(['bubble', buttonElement]);
    
  555. 
    
  556.           document.body.removeChild(portalElement);
    
  557.         });
    
  558. 
    
  559.         it('handle click events on document.body portals', () => {
    
  560.           const log = [];
    
  561. 
    
  562.           function Child({label}) {
    
  563.             return <div onClick={() => log.push(label)}>{label}</div>;
    
  564.           }
    
  565. 
    
  566.           function Parent() {
    
  567.             return (
    
  568.               <>
    
  569.                 {ReactDOM.createPortal(
    
  570.                   <Child label={'first'} />,
    
  571.                   document.body,
    
  572.                 )}
    
  573.                 {ReactDOM.createPortal(
    
  574.                   <Child label={'second'} />,
    
  575.                   document.body,
    
  576.                 )}
    
  577.               </>
    
  578.             );
    
  579.           }
    
  580. 
    
  581.           ReactDOM.render(<Parent />, container);
    
  582.           const second = document.body.lastChild;
    
  583.           expect(second.textContent).toEqual('second');
    
  584.           dispatchClickEvent(second);
    
  585. 
    
  586.           expect(log).toEqual(['second']);
    
  587. 
    
  588.           const first = second.previousSibling;
    
  589.           expect(first.textContent).toEqual('first');
    
  590.           dispatchClickEvent(first);
    
  591. 
    
  592.           expect(log).toEqual(['second', 'first']);
    
  593.         });
    
  594. 
    
  595.         it('does not invoke an event on a parent tree when a subtree is dehydrated', async () => {
    
  596.           let suspend = false;
    
  597.           let resolve;
    
  598.           const promise = new Promise(
    
  599.             resolvePromise => (resolve = resolvePromise),
    
  600.           );
    
  601. 
    
  602.           let clicks = 0;
    
  603.           const childSlotRef = React.createRef();
    
  604. 
    
  605.           function Parent() {
    
  606.             return <div onClick={() => clicks++} ref={childSlotRef} />;
    
  607.           }
    
  608. 
    
  609.           function Child({text}) {
    
  610.             if (suspend) {
    
  611.               throw promise;
    
  612.             } else {
    
  613.               return <a>Click me</a>;
    
  614.             }
    
  615.           }
    
  616. 
    
  617.           function App() {
    
  618.             // The root is a Suspense boundary.
    
  619.             return (
    
  620.               <React.Suspense fallback="Loading...">
    
  621.                 <Child />
    
  622.               </React.Suspense>
    
  623.             );
    
  624.           }
    
  625. 
    
  626.           suspend = false;
    
  627.           const finalHTML = ReactDOMServer.renderToString(<App />);
    
  628. 
    
  629.           const parentContainer = document.createElement('div');
    
  630.           const childContainer = document.createElement('div');
    
  631. 
    
  632.           // We need this to be in the document since we'll dispatch events on it.
    
  633.           document.body.appendChild(parentContainer);
    
  634. 
    
  635.           // We're going to use a different root as a parent.
    
  636.           // This lets us detect whether an event goes through React's event system.
    
  637.           const parentRoot = ReactDOMClient.createRoot(parentContainer);
    
  638.           await act(() => {
    
  639.             parentRoot.render(<Parent />);
    
  640.           });
    
  641. 
    
  642.           childSlotRef.current.appendChild(childContainer);
    
  643. 
    
  644.           childContainer.innerHTML = finalHTML;
    
  645. 
    
  646.           const a = childContainer.getElementsByTagName('a')[0];
    
  647. 
    
  648.           suspend = true;
    
  649. 
    
  650.           // Hydrate asynchronously.
    
  651.           await act(() => {
    
  652.             ReactDOMClient.hydrateRoot(childContainer, <App />);
    
  653.           });
    
  654. 
    
  655.           // The Suspense boundary is not yet hydrated.
    
  656.           await act(() => {
    
  657.             a.click();
    
  658.           });
    
  659.           expect(clicks).toBe(0);
    
  660. 
    
  661.           // Resolving the promise so that rendering can complete.
    
  662.           await act(async () => {
    
  663.             suspend = false;
    
  664.             resolve();
    
  665.             await promise;
    
  666.           });
    
  667. 
    
  668.           // We're now full hydrated.
    
  669.           expect(clicks).toBe(0);
    
  670.           document.body.removeChild(parentContainer);
    
  671.         });
    
  672. 
    
  673.         it('handle click events on dynamic portals', async () => {
    
  674.           const log = [];
    
  675. 
    
  676.           function Parent() {
    
  677.             const ref = React.useRef(null);
    
  678.             const [portal, setPortal] = React.useState(null);
    
  679. 
    
  680.             React.useEffect(() => {
    
  681.               setPortal(
    
  682.                 ReactDOM.createPortal(
    
  683.                   <span onClick={() => log.push('child')} id="child" />,
    
  684.                   ref.current,
    
  685.                 ),
    
  686.               );
    
  687.             }, []);
    
  688. 
    
  689.             return (
    
  690.               <div ref={ref} onClick={() => log.push('parent')} id="parent">
    
  691.                 {portal}
    
  692.               </div>
    
  693.             );
    
  694.           }
    
  695. 
    
  696.           await act(() => {
    
  697.             ReactDOM.render(<Parent />, container);
    
  698.           });
    
  699. 
    
  700.           const parent = container.lastChild;
    
  701.           expect(parent.id).toEqual('parent');
    
  702. 
    
  703.           await act(() => {
    
  704.             dispatchClickEvent(parent);
    
  705.           });
    
  706. 
    
  707.           expect(log).toEqual(['parent']);
    
  708. 
    
  709.           const child = parent.lastChild;
    
  710.           expect(child.id).toEqual('child');
    
  711. 
    
  712.           await act(() => {
    
  713.             dispatchClickEvent(child);
    
  714.           });
    
  715. 
    
  716.           // we add both 'child' and 'parent' due to bubbling
    
  717.           expect(log).toEqual(['parent', 'child', 'parent']);
    
  718.         });
    
  719. 
    
  720.         // Slight alteration to the last test, to catch
    
  721.         // a subtle difference in traversal.
    
  722.         it('handle click events on dynamic portals #2', async () => {
    
  723.           const log = [];
    
  724. 
    
  725.           function Parent() {
    
  726.             const ref = React.useRef(null);
    
  727.             const [portal, setPortal] = React.useState(null);
    
  728. 
    
  729.             React.useEffect(() => {
    
  730.               setPortal(
    
  731.                 ReactDOM.createPortal(
    
  732.                   <span onClick={() => log.push('child')} id="child" />,
    
  733.                   ref.current,
    
  734.                 ),
    
  735.               );
    
  736.             }, []);
    
  737. 
    
  738.             return (
    
  739.               <div ref={ref} onClick={() => log.push('parent')} id="parent">
    
  740.                 <div>{portal}</div>
    
  741.               </div>
    
  742.             );
    
  743.           }
    
  744. 
    
  745.           await act(() => {
    
  746.             ReactDOM.render(<Parent />, container);
    
  747.           });
    
  748. 
    
  749.           const parent = container.lastChild;
    
  750.           expect(parent.id).toEqual('parent');
    
  751. 
    
  752.           await act(() => {
    
  753.             dispatchClickEvent(parent);
    
  754.           });
    
  755. 
    
  756.           expect(log).toEqual(['parent']);
    
  757. 
    
  758.           const child = parent.lastChild;
    
  759.           expect(child.id).toEqual('child');
    
  760. 
    
  761.           await act(() => {
    
  762.             dispatchClickEvent(child);
    
  763.           });
    
  764. 
    
  765.           // we add both 'child' and 'parent' due to bubbling
    
  766.           expect(log).toEqual(['parent', 'child', 'parent']);
    
  767.         });
    
  768. 
    
  769.         it('native stopPropagation on click events between portals', () => {
    
  770.           const buttonRef = React.createRef();
    
  771.           const divRef = React.createRef();
    
  772.           const middleDivRef = React.createRef();
    
  773.           const log = [];
    
  774.           const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  775.           const onClickCapture = jest.fn(e =>
    
  776.             log.push(['capture', e.currentTarget]),
    
  777.           );
    
  778. 
    
  779.           const portalElement = document.createElement('div');
    
  780.           document.body.appendChild(portalElement);
    
  781. 
    
  782.           function Child() {
    
  783.             return (
    
  784.               <div ref={middleDivRef}>
    
  785.                 <div
    
  786.                   ref={divRef}
    
  787.                   onClick={onClick}
    
  788.                   onClickCapture={onClickCapture}>
    
  789.                   Click me!
    
  790.                 </div>
    
  791.               </div>
    
  792.             );
    
  793.           }
    
  794. 
    
  795.           function Parent() {
    
  796.             React.useLayoutEffect(() => {
    
  797.               // This should prevent the portalElement listeners from
    
  798.               // capturing the events in the bubble phase.
    
  799.               middleDivRef.current.addEventListener('click', e => {
    
  800.                 e.stopPropagation();
    
  801.               });
    
  802.             });
    
  803. 
    
  804.             return (
    
  805.               <button
    
  806.                 ref={buttonRef}
    
  807.                 onClick={onClick}
    
  808.                 onClickCapture={onClickCapture}>
    
  809.                 {ReactDOM.createPortal(<Child />, portalElement)}
    
  810.               </button>
    
  811.             );
    
  812.           }
    
  813. 
    
  814.           ReactDOM.render(<Parent />, container);
    
  815. 
    
  816.           const buttonElement = buttonRef.current;
    
  817.           dispatchClickEvent(buttonElement);
    
  818.           expect(onClick).toHaveBeenCalledTimes(1);
    
  819.           expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  820.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  821.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  822. 
    
  823.           const divElement = divRef.current;
    
  824.           dispatchClickEvent(divElement);
    
  825.           expect(onClick).toHaveBeenCalledTimes(1);
    
  826.           expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  827. 
    
  828.           document.body.removeChild(portalElement);
    
  829.         });
    
  830. 
    
  831.         it('handle propagation of focus events', () => {
    
  832.           const buttonRef = React.createRef();
    
  833.           const divRef = React.createRef();
    
  834.           const log = [];
    
  835.           const onFocus = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  836.           const onFocusCapture = jest.fn(e =>
    
  837.             log.push(['capture', e.currentTarget]),
    
  838.           );
    
  839. 
    
  840.           function Test() {
    
  841.             return (
    
  842.               <button
    
  843.                 ref={buttonRef}
    
  844.                 onFocus={onFocus}
    
  845.                 onFocusCapture={onFocusCapture}>
    
  846.                 <div
    
  847.                   ref={divRef}
    
  848.                   onFocus={onFocus}
    
  849.                   onFocusCapture={onFocusCapture}
    
  850.                   tabIndex={0}>
    
  851.                   Click me!
    
  852.                 </div>
    
  853.               </button>
    
  854.             );
    
  855.           }
    
  856. 
    
  857.           ReactDOM.render(<Test />, container);
    
  858. 
    
  859.           const buttonElement = buttonRef.current;
    
  860.           buttonElement.focus();
    
  861.           expect(onFocus).toHaveBeenCalledTimes(1);
    
  862.           expect(onFocusCapture).toHaveBeenCalledTimes(1);
    
  863.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  864.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  865. 
    
  866.           const divElement = divRef.current;
    
  867.           divElement.focus();
    
  868.           expect(onFocus).toHaveBeenCalledTimes(3);
    
  869.           expect(onFocusCapture).toHaveBeenCalledTimes(3);
    
  870.           expect(log[2]).toEqual(['capture', buttonElement]);
    
  871.           expect(log[3]).toEqual(['capture', divElement]);
    
  872.           expect(log[4]).toEqual(['bubble', divElement]);
    
  873.           expect(log[5]).toEqual(['bubble', buttonElement]);
    
  874.         });
    
  875. 
    
  876.         it('handle propagation of focus events between roots', () => {
    
  877.           const buttonRef = React.createRef();
    
  878.           const divRef = React.createRef();
    
  879.           const childRef = React.createRef();
    
  880.           const log = [];
    
  881.           const onFocus = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  882.           const onFocusCapture = jest.fn(e =>
    
  883.             log.push(['capture', e.currentTarget]),
    
  884.           );
    
  885. 
    
  886.           function Child() {
    
  887.             return (
    
  888.               <div
    
  889.                 ref={divRef}
    
  890.                 onFocus={onFocus}
    
  891.                 onFocusCapture={onFocusCapture}
    
  892.                 tabIndex={0}>
    
  893.                 Click me!
    
  894.               </div>
    
  895.             );
    
  896.           }
    
  897. 
    
  898.           function Parent() {
    
  899.             return (
    
  900.               <button
    
  901.                 ref={buttonRef}
    
  902.                 onFocus={onFocus}
    
  903.                 onFocusCapture={onFocusCapture}>
    
  904.                 <div ref={childRef} />
    
  905.               </button>
    
  906.             );
    
  907.           }
    
  908. 
    
  909.           ReactDOM.render(<Parent />, container);
    
  910.           ReactDOM.render(<Child />, childRef.current);
    
  911. 
    
  912.           const buttonElement = buttonRef.current;
    
  913.           buttonElement.focus();
    
  914.           expect(onFocus).toHaveBeenCalledTimes(1);
    
  915.           expect(onFocusCapture).toHaveBeenCalledTimes(1);
    
  916.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  917.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  918. 
    
  919.           const divElement = divRef.current;
    
  920.           divElement.focus();
    
  921.           expect(onFocus).toHaveBeenCalledTimes(3);
    
  922.           expect(onFocusCapture).toHaveBeenCalledTimes(3);
    
  923.           expect(log[2]).toEqual(['capture', buttonElement]);
    
  924.           expect(log[3]).toEqual(['capture', divElement]);
    
  925.           expect(log[4]).toEqual(['bubble', divElement]);
    
  926.           expect(log[5]).toEqual(['bubble', buttonElement]);
    
  927.         });
    
  928. 
    
  929.         it('handle propagation of focus events between portals', () => {
    
  930.           const buttonRef = React.createRef();
    
  931.           const divRef = React.createRef();
    
  932.           const log = [];
    
  933.           const onFocus = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  934.           const onFocusCapture = jest.fn(e =>
    
  935.             log.push(['capture', e.currentTarget]),
    
  936.           );
    
  937. 
    
  938.           const portalElement = document.createElement('div');
    
  939.           document.body.appendChild(portalElement);
    
  940. 
    
  941.           function Child() {
    
  942.             return (
    
  943.               <div
    
  944.                 ref={divRef}
    
  945.                 onFocus={onFocus}
    
  946.                 onFocusCapture={onFocusCapture}
    
  947.                 tabIndex={0}>
    
  948.                 Click me!
    
  949.               </div>
    
  950.             );
    
  951.           }
    
  952. 
    
  953.           function Parent() {
    
  954.             return (
    
  955.               <button
    
  956.                 ref={buttonRef}
    
  957.                 onFocus={onFocus}
    
  958.                 onFocusCapture={onFocusCapture}>
    
  959.                 {ReactDOM.createPortal(<Child />, portalElement)}
    
  960.               </button>
    
  961.             );
    
  962.           }
    
  963. 
    
  964.           ReactDOM.render(<Parent />, container);
    
  965. 
    
  966.           const buttonElement = buttonRef.current;
    
  967.           buttonElement.focus();
    
  968.           expect(onFocus).toHaveBeenCalledTimes(1);
    
  969.           expect(onFocusCapture).toHaveBeenCalledTimes(1);
    
  970.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  971.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  972. 
    
  973.           const divElement = divRef.current;
    
  974.           divElement.focus();
    
  975.           expect(onFocus).toHaveBeenCalledTimes(3);
    
  976.           expect(onFocusCapture).toHaveBeenCalledTimes(3);
    
  977.           expect(log[2]).toEqual(['capture', buttonElement]);
    
  978.           expect(log[3]).toEqual(['capture', divElement]);
    
  979.           expect(log[4]).toEqual(['bubble', divElement]);
    
  980.           expect(log[5]).toEqual(['bubble', buttonElement]);
    
  981. 
    
  982.           document.body.removeChild(portalElement);
    
  983.         });
    
  984. 
    
  985.         it('native stopPropagation on focus events between portals', () => {
    
  986.           const buttonRef = React.createRef();
    
  987.           const divRef = React.createRef();
    
  988.           const middleDivRef = React.createRef();
    
  989.           const log = [];
    
  990.           const onFocus = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  991.           const onFocusCapture = jest.fn(e =>
    
  992.             log.push(['capture', e.currentTarget]),
    
  993.           );
    
  994. 
    
  995.           const portalElement = document.createElement('div');
    
  996.           document.body.appendChild(portalElement);
    
  997. 
    
  998.           function Child() {
    
  999.             return (
    
  1000.               <div ref={middleDivRef}>
    
  1001.                 <div
    
  1002.                   ref={divRef}
    
  1003.                   onFocus={onFocus}
    
  1004.                   onFocusCapture={onFocusCapture}
    
  1005.                   tabIndex={0}>
    
  1006.                   Click me!
    
  1007.                 </div>
    
  1008.               </div>
    
  1009.             );
    
  1010.           }
    
  1011. 
    
  1012.           function Parent() {
    
  1013.             React.useLayoutEffect(() => {
    
  1014.               // This should prevent the portalElement listeners from
    
  1015.               // capturing the events in the bubble phase.
    
  1016.               middleDivRef.current.addEventListener('focusin', e => {
    
  1017.                 e.stopPropagation();
    
  1018.               });
    
  1019.             });
    
  1020. 
    
  1021.             return (
    
  1022.               <button
    
  1023.                 ref={buttonRef}
    
  1024.                 onFocus={onFocus}
    
  1025.                 onFocusCapture={onFocusCapture}>
    
  1026.                 {ReactDOM.createPortal(<Child />, portalElement)}
    
  1027.               </button>
    
  1028.             );
    
  1029.           }
    
  1030. 
    
  1031.           ReactDOM.render(<Parent />, container);
    
  1032. 
    
  1033.           const buttonElement = buttonRef.current;
    
  1034.           buttonElement.focus();
    
  1035.           expect(onFocus).toHaveBeenCalledTimes(1);
    
  1036.           expect(onFocusCapture).toHaveBeenCalledTimes(1);
    
  1037.           expect(log[0]).toEqual(['capture', buttonElement]);
    
  1038.           expect(log[1]).toEqual(['bubble', buttonElement]);
    
  1039. 
    
  1040.           const divElement = divRef.current;
    
  1041.           divElement.focus();
    
  1042.           expect(onFocus).toHaveBeenCalledTimes(1);
    
  1043.           expect(onFocusCapture).toHaveBeenCalledTimes(3);
    
  1044. 
    
  1045.           document.body.removeChild(portalElement);
    
  1046.         });
    
  1047. 
    
  1048.         it('handle propagation of enter and leave events between portals', () => {
    
  1049.           const buttonRef = React.createRef();
    
  1050.           const divRef = React.createRef();
    
  1051.           const log = [];
    
  1052.           const onMouseEnter = jest.fn(e => log.push(e.currentTarget));
    
  1053.           const onMouseLeave = jest.fn(e => log.push(e.currentTarget));
    
  1054. 
    
  1055.           const portalElement = document.createElement('div');
    
  1056.           document.body.appendChild(portalElement);
    
  1057. 
    
  1058.           function Child() {
    
  1059.             return (
    
  1060.               <div
    
  1061.                 ref={divRef}
    
  1062.                 onMouseEnter={onMouseEnter}
    
  1063.                 onMouseLeave={onMouseLeave}
    
  1064.               />
    
  1065.             );
    
  1066.           }
    
  1067. 
    
  1068.           function Parent() {
    
  1069.             return (
    
  1070.               <button
    
  1071.                 ref={buttonRef}
    
  1072.                 onMouseEnter={onMouseEnter}
    
  1073.                 onMouseLeave={onMouseLeave}>
    
  1074.                 {ReactDOM.createPortal(<Child />, portalElement)}
    
  1075.               </button>
    
  1076.             );
    
  1077.           }
    
  1078. 
    
  1079.           ReactDOM.render(<Parent />, container);
    
  1080. 
    
  1081.           const buttonElement = buttonRef.current;
    
  1082.           buttonElement.dispatchEvent(
    
  1083.             new MouseEvent('mouseover', {
    
  1084.               bubbles: true,
    
  1085.               cancelable: true,
    
  1086.               relatedTarget: null,
    
  1087.             }),
    
  1088.           );
    
  1089.           expect(onMouseEnter).toHaveBeenCalledTimes(1);
    
  1090.           expect(onMouseLeave).toHaveBeenCalledTimes(0);
    
  1091.           expect(log[0]).toEqual(buttonElement);
    
  1092. 
    
  1093.           const divElement = divRef.current;
    
  1094.           buttonElement.dispatchEvent(
    
  1095.             new MouseEvent('mouseout', {
    
  1096.               bubbles: true,
    
  1097.               cancelable: true,
    
  1098.               relatedTarget: divElement,
    
  1099.             }),
    
  1100.           );
    
  1101.           divElement.dispatchEvent(
    
  1102.             new MouseEvent('mouseover', {
    
  1103.               bubbles: true,
    
  1104.               cancelable: true,
    
  1105.               relatedTarget: buttonElement,
    
  1106.             }),
    
  1107.           );
    
  1108.           expect(onMouseEnter).toHaveBeenCalledTimes(2);
    
  1109.           expect(onMouseLeave).toHaveBeenCalledTimes(0);
    
  1110.           expect(log[1]).toEqual(divElement);
    
  1111. 
    
  1112.           document.body.removeChild(portalElement);
    
  1113.         });
    
  1114. 
    
  1115.         it('handle propagation of enter and leave events between portals #2', () => {
    
  1116.           const buttonRef = React.createRef();
    
  1117.           const divRef = React.createRef();
    
  1118.           const portalRef = React.createRef();
    
  1119.           const log = [];
    
  1120.           const onMouseEnter = jest.fn(e => log.push(e.currentTarget));
    
  1121.           const onMouseLeave = jest.fn(e => log.push(e.currentTarget));
    
  1122. 
    
  1123.           function Child() {
    
  1124.             return (
    
  1125.               <div
    
  1126.                 ref={divRef}
    
  1127.                 onMouseEnter={onMouseEnter}
    
  1128.                 onMouseLeave={onMouseLeave}
    
  1129.               />
    
  1130.             );
    
  1131.           }
    
  1132. 
    
  1133.           function Parent() {
    
  1134.             const [portal, setPortal] = React.useState(null);
    
  1135. 
    
  1136.             React.useLayoutEffect(() => {
    
  1137.               setPortal(ReactDOM.createPortal(<Child />, portalRef.current));
    
  1138.             }, []);
    
  1139. 
    
  1140.             return (
    
  1141.               <button
    
  1142.                 ref={buttonRef}
    
  1143.                 onMouseEnter={onMouseEnter}
    
  1144.                 onMouseLeave={onMouseLeave}>
    
  1145.                 <div ref={portalRef}>{portal}</div>
    
  1146.               </button>
    
  1147.             );
    
  1148.           }
    
  1149. 
    
  1150.           ReactDOM.render(<Parent />, container);
    
  1151. 
    
  1152.           const buttonElement = buttonRef.current;
    
  1153.           buttonElement.dispatchEvent(
    
  1154.             new MouseEvent('mouseover', {
    
  1155.               bubbles: true,
    
  1156.               cancelable: true,
    
  1157.               relatedTarget: null,
    
  1158.             }),
    
  1159.           );
    
  1160.           expect(onMouseEnter).toHaveBeenCalledTimes(1);
    
  1161.           expect(onMouseLeave).toHaveBeenCalledTimes(0);
    
  1162.           expect(log[0]).toEqual(buttonElement);
    
  1163. 
    
  1164.           const divElement = divRef.current;
    
  1165.           buttonElement.dispatchEvent(
    
  1166.             new MouseEvent('mouseout', {
    
  1167.               bubbles: true,
    
  1168.               cancelable: true,
    
  1169.               relatedTarget: divElement,
    
  1170.             }),
    
  1171.           );
    
  1172.           divElement.dispatchEvent(
    
  1173.             new MouseEvent('mouseover', {
    
  1174.               bubbles: true,
    
  1175.               cancelable: true,
    
  1176.               relatedTarget: buttonElement,
    
  1177.             }),
    
  1178.           );
    
  1179.           expect(onMouseEnter).toHaveBeenCalledTimes(2);
    
  1180.           expect(onMouseLeave).toHaveBeenCalledTimes(0);
    
  1181.           expect(log[1]).toEqual(divElement);
    
  1182.         });
    
  1183. 
    
  1184.         it('should preserve bubble/capture order between roots and nested portals', () => {
    
  1185.           const targetRef = React.createRef();
    
  1186.           let log = [];
    
  1187.           const onClickRoot = jest.fn(e => log.push('bubble root'));
    
  1188.           const onClickCaptureRoot = jest.fn(e => log.push('capture root'));
    
  1189.           const onClickPortal = jest.fn(e => log.push('bubble portal'));
    
  1190.           const onClickCapturePortal = jest.fn(e => log.push('capture portal'));
    
  1191. 
    
  1192.           function Portal() {
    
  1193.             return (
    
  1194.               <div
    
  1195.                 onClick={onClickPortal}
    
  1196.                 onClickCapture={onClickCapturePortal}
    
  1197.                 ref={targetRef}>
    
  1198.                 Click me!
    
  1199.               </div>
    
  1200.             );
    
  1201.           }
    
  1202. 
    
  1203.           const portalContainer = document.createElement('div');
    
  1204. 
    
  1205.           let shouldStopPropagation = false;
    
  1206.           portalContainer.addEventListener(
    
  1207.             'click',
    
  1208.             e => {
    
  1209.               if (shouldStopPropagation) {
    
  1210.                 e.stopPropagation();
    
  1211.               }
    
  1212.             },
    
  1213.             false,
    
  1214.           );
    
  1215. 
    
  1216.           function Root() {
    
  1217.             const portalTargetRef = React.useRef(null);
    
  1218.             React.useLayoutEffect(() => {
    
  1219.               portalTargetRef.current.appendChild(portalContainer);
    
  1220.             });
    
  1221.             return (
    
  1222.               <div onClick={onClickRoot} onClickCapture={onClickCaptureRoot}>
    
  1223.                 <div ref={portalTargetRef} />
    
  1224.                 {ReactDOM.createPortal(<Portal />, portalContainer)}
    
  1225.               </div>
    
  1226.             );
    
  1227.           }
    
  1228. 
    
  1229.           ReactDOM.render(<Root />, container);
    
  1230. 
    
  1231.           const divElement = targetRef.current;
    
  1232.           dispatchClickEvent(divElement);
    
  1233.           expect(log).toEqual([
    
  1234.             'capture root',
    
  1235.             'capture portal',
    
  1236.             'bubble portal',
    
  1237.             'bubble root',
    
  1238.           ]);
    
  1239. 
    
  1240.           log = [];
    
  1241. 
    
  1242.           shouldStopPropagation = true;
    
  1243.           dispatchClickEvent(divElement);
    
  1244. 
    
  1245.           if (enableLegacyFBSupport) {
    
  1246.             // We aren't using roots with legacyFBSupport, we put clicks on the document, so we exbit the previous
    
  1247.             // behavior.
    
  1248.             expect(log).toEqual(['capture root', 'capture portal']);
    
  1249.           } else {
    
  1250.             expect(log).toEqual([
    
  1251.               // The events on root probably shouldn't fire if a non-React intermediated. but current behavior is that they do.
    
  1252.               'capture root',
    
  1253.               'capture portal',
    
  1254.               'bubble portal',
    
  1255.               'bubble root',
    
  1256.             ]);
    
  1257.           }
    
  1258.         });
    
  1259. 
    
  1260.         describe('ReactDOM.createEventHandle', () => {
    
  1261.           beforeEach(() => {
    
  1262.             jest.resetModules();
    
  1263.             ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  1264.             ReactFeatureFlags.enableLegacyFBSupport = enableLegacyFBSupport;
    
  1265.             ReactFeatureFlags.enableCreateEventHandleAPI = true;
    
  1266. 
    
  1267.             React = require('react');
    
  1268.             ReactDOM = require('react-dom');
    
  1269.             ReactDOMClient = require('react-dom/client');
    
  1270.             Scheduler = require('scheduler');
    
  1271.             ReactDOMServer = require('react-dom/server');
    
  1272.             act = require('internal-test-utils').act;
    
  1273. 
    
  1274.             const InternalTestUtils = require('internal-test-utils');
    
  1275.             waitForAll = InternalTestUtils.waitForAll;
    
  1276.             waitFor = InternalTestUtils.waitFor;
    
  1277.           });
    
  1278. 
    
  1279.           // @gate www
    
  1280.           it('can render correctly with the ReactDOMServer', () => {
    
  1281.             const clickEvent = jest.fn();
    
  1282.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  1283. 
    
  1284.             function Test() {
    
  1285.               const divRef = React.useRef(null);
    
  1286. 
    
  1287.               React.useEffect(() => {
    
  1288.                 return setClick(divRef.current, clickEvent);
    
  1289.               });
    
  1290. 
    
  1291.               return <div ref={divRef}>Hello world</div>;
    
  1292.             }
    
  1293.             const output = ReactDOMServer.renderToString(<Test />);
    
  1294.             expect(output).toBe(`<div>Hello world</div>`);
    
  1295.           });
    
  1296. 
    
  1297.           // @gate www
    
  1298.           it('can render correctly with the ReactDOMServer hydration', async () => {
    
  1299.             const clickEvent = jest.fn();
    
  1300.             const spanRef = React.createRef();
    
  1301.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  1302. 
    
  1303.             function Test() {
    
  1304.               React.useEffect(() => {
    
  1305.                 return setClick(spanRef.current, clickEvent);
    
  1306.               });
    
  1307. 
    
  1308.               return (
    
  1309.                 <div>
    
  1310.                   <span ref={spanRef}>Hello world</span>
    
  1311.                 </div>
    
  1312.               );
    
  1313.             }
    
  1314.             const output = ReactDOMServer.renderToString(<Test />);
    
  1315.             expect(output).toBe(`<div><span>Hello world</span></div>`);
    
  1316.             container.innerHTML = output;
    
  1317.             await act(() => {
    
  1318.               ReactDOM.hydrate(<Test />, container);
    
  1319.             });
    
  1320.             dispatchClickEvent(spanRef.current);
    
  1321.             expect(clickEvent).toHaveBeenCalledTimes(1);
    
  1322.           });
    
  1323. 
    
  1324.           // @gate www
    
  1325.           it('should correctly work for a basic "click" listener', async () => {
    
  1326.             let log = [];
    
  1327.             const clickEvent = jest.fn(event => {
    
  1328.               log.push({
    
  1329.                 eventPhase: event.eventPhase,
    
  1330.                 type: event.type,
    
  1331.                 currentTarget: event.currentTarget,
    
  1332.                 target: event.target,
    
  1333.               });
    
  1334.             });
    
  1335.             const divRef = React.createRef();
    
  1336.             const buttonRef = React.createRef();
    
  1337.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  1338. 
    
  1339.             function Test() {
    
  1340.               React.useEffect(() => {
    
  1341.                 return setClick(buttonRef.current, clickEvent);
    
  1342.               });
    
  1343. 
    
  1344.               return (
    
  1345.                 <button ref={buttonRef}>
    
  1346.                   <div ref={divRef}>Click me!</div>
    
  1347.                 </button>
    
  1348.               );
    
  1349.             }
    
  1350. 
    
  1351.             await act(() => {
    
  1352.               ReactDOM.render(<Test />, container);
    
  1353.             });
    
  1354. 
    
  1355.             expect(container.innerHTML).toBe(
    
  1356.               '<button><div>Click me!</div></button>',
    
  1357.             );
    
  1358. 
    
  1359.             // Clicking the button should trigger the event callback
    
  1360.             let divElement = divRef.current;
    
  1361.             dispatchClickEvent(divElement);
    
  1362.             expect(log).toEqual([
    
  1363.               {
    
  1364.                 eventPhase: 3,
    
  1365.                 type: 'click',
    
  1366.                 currentTarget: buttonRef.current,
    
  1367.                 target: divRef.current,
    
  1368.               },
    
  1369.             ]);
    
  1370.             expect(clickEvent).toBeCalledTimes(1);
    
  1371. 
    
  1372.             // Unmounting the container and clicking should not work
    
  1373.             await act(() => {
    
  1374.               ReactDOM.render(null, container);
    
  1375.             });
    
  1376. 
    
  1377.             dispatchClickEvent(divElement);
    
  1378.             expect(clickEvent).toBeCalledTimes(1);
    
  1379. 
    
  1380.             // Re-rendering the container and clicking should work
    
  1381.             await act(() => {
    
  1382.               ReactDOM.render(<Test />, container);
    
  1383.             });
    
  1384. 
    
  1385.             divElement = divRef.current;
    
  1386.             dispatchClickEvent(divElement);
    
  1387.             expect(clickEvent).toBeCalledTimes(2);
    
  1388. 
    
  1389.             log = [];
    
  1390. 
    
  1391.             // Clicking the button should also work
    
  1392.             const buttonElement = buttonRef.current;
    
  1393.             dispatchClickEvent(buttonElement);
    
  1394.             expect(log).toEqual([
    
  1395.               {
    
  1396.                 eventPhase: 3,
    
  1397.                 type: 'click',
    
  1398.                 currentTarget: buttonRef.current,
    
  1399.                 target: buttonRef.current,
    
  1400.               },
    
  1401.             ]);
    
  1402. 
    
  1403.             const setClick2 = ReactDOM.unstable_createEventHandle('click');
    
  1404. 
    
  1405.             function Test2({clickEvent2}) {
    
  1406.               React.useEffect(() => {
    
  1407.                 return setClick2(buttonRef.current, clickEvent2);
    
  1408.               });
    
  1409. 
    
  1410.               return (
    
  1411.                 <button ref={buttonRef}>
    
  1412.                   <div ref={divRef}>Click me!</div>
    
  1413.                 </button>
    
  1414.               );
    
  1415.             }
    
  1416. 
    
  1417.             let clickEvent2 = jest.fn();
    
  1418.             await act(() => {
    
  1419.               ReactDOM.render(<Test2 clickEvent2={clickEvent2} />, container);
    
  1420.             });
    
  1421. 
    
  1422.             divElement = divRef.current;
    
  1423.             dispatchClickEvent(divElement);
    
  1424.             expect(clickEvent2).toBeCalledTimes(1);
    
  1425. 
    
  1426.             // Reset the function we pass in, so it's different
    
  1427.             clickEvent2 = jest.fn();
    
  1428.             await act(() => {
    
  1429.               ReactDOM.render(<Test2 clickEvent2={clickEvent2} />, container);
    
  1430.             });
    
  1431. 
    
  1432.             divElement = divRef.current;
    
  1433.             dispatchClickEvent(divElement);
    
  1434.             expect(clickEvent2).toBeCalledTimes(1);
    
  1435.           });
    
  1436. 
    
  1437.           // @gate www
    
  1438.           it('should correctly work for setting and clearing a basic "click" listener', async () => {
    
  1439.             const clickEvent = jest.fn();
    
  1440.             const divRef = React.createRef();
    
  1441.             const buttonRef = React.createRef();
    
  1442.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  1443. 
    
  1444.             function Test({off}) {
    
  1445.               React.useEffect(() => {
    
  1446.                 const clear = setClick(buttonRef.current, clickEvent);
    
  1447.                 if (off) {
    
  1448.                   clear();
    
  1449.                 }
    
  1450.                 return clear;
    
  1451.               });
    
  1452. 
    
  1453.               return (
    
  1454.                 <button ref={buttonRef}>
    
  1455.                   <div ref={divRef}>Click me!</div>
    
  1456.                 </button>
    
  1457.               );
    
  1458.             }
    
  1459. 
    
  1460.             await act(() => {
    
  1461.               ReactDOM.render(<Test off={false} />, container);
    
  1462.             });
    
  1463. 
    
  1464.             let divElement = divRef.current;
    
  1465.             dispatchClickEvent(divElement);
    
  1466.             expect(clickEvent).toBeCalledTimes(1);
    
  1467. 
    
  1468.             // The listener should get unmounted
    
  1469.             await act(() => {
    
  1470.               ReactDOM.render(<Test off={true} />, container);
    
  1471.             });
    
  1472. 
    
  1473.             clickEvent.mockClear();
    
  1474. 
    
  1475.             divElement = divRef.current;
    
  1476.             dispatchClickEvent(divElement);
    
  1477.             expect(clickEvent).toBeCalledTimes(0);
    
  1478.           });
    
  1479. 
    
  1480.           // @gate www
    
  1481.           it('should handle the target being a text node', async () => {
    
  1482.             const clickEvent = jest.fn();
    
  1483.             const buttonRef = React.createRef();
    
  1484.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  1485. 
    
  1486.             function Test() {
    
  1487.               React.useEffect(() => {
    
  1488.                 return setClick(buttonRef.current, clickEvent);
    
  1489.               });
    
  1490. 
    
  1491.               return <button ref={buttonRef}>Click me!</button>;
    
  1492.             }
    
  1493. 
    
  1494.             await act(() => {
    
  1495.               ReactDOM.render(<Test />, container);
    
  1496.             });
    
  1497. 
    
  1498.             const textNode = buttonRef.current.firstChild;
    
  1499.             dispatchClickEvent(textNode);
    
  1500.             expect(clickEvent).toBeCalledTimes(1);
    
  1501.           });
    
  1502. 
    
  1503.           // @gate www
    
  1504.           it('handle propagation of click events', async () => {
    
  1505.             const buttonRef = React.createRef();
    
  1506.             const divRef = React.createRef();
    
  1507.             const log = [];
    
  1508.             const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  1509.             const onClickCapture = jest.fn(e =>
    
  1510.               log.push(['capture', e.currentTarget]),
    
  1511.             );
    
  1512.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  1513.             const setCaptureClick = ReactDOM.unstable_createEventHandle(
    
  1514.               'click',
    
  1515.               {
    
  1516.                 capture: true,
    
  1517.               },
    
  1518.             );
    
  1519. 
    
  1520.             function Test() {
    
  1521.               React.useEffect(() => {
    
  1522.                 const clearClick1 = setClick(buttonRef.current, onClick);
    
  1523.                 const clearCaptureClick1 = setCaptureClick(
    
  1524.                   buttonRef.current,
    
  1525.                   onClickCapture,
    
  1526.                 );
    
  1527.                 const clearClick2 = setClick(divRef.current, onClick);
    
  1528.                 const clearCaptureClick2 = setCaptureClick(
    
  1529.                   divRef.current,
    
  1530.                   onClickCapture,
    
  1531.                 );
    
  1532. 
    
  1533.                 return () => {
    
  1534.                   clearClick1();
    
  1535.                   clearCaptureClick1();
    
  1536.                   clearClick2();
    
  1537.                   clearCaptureClick2();
    
  1538.                 };
    
  1539.               });
    
  1540. 
    
  1541.               return (
    
  1542.                 <button ref={buttonRef}>
    
  1543.                   <div ref={divRef}>Click me!</div>
    
  1544.                 </button>
    
  1545.               );
    
  1546.             }
    
  1547. 
    
  1548.             await act(() => {
    
  1549.               ReactDOM.render(<Test />, container);
    
  1550.             });
    
  1551. 
    
  1552.             const buttonElement = buttonRef.current;
    
  1553.             dispatchClickEvent(buttonElement);
    
  1554.             expect(onClick).toHaveBeenCalledTimes(1);
    
  1555.             expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  1556.             expect(log[0]).toEqual(['capture', buttonElement]);
    
  1557.             expect(log[1]).toEqual(['bubble', buttonElement]);
    
  1558. 
    
  1559.             log.length = 0;
    
  1560.             onClick.mockClear();
    
  1561.             onClickCapture.mockClear();
    
  1562. 
    
  1563.             const divElement = divRef.current;
    
  1564.             dispatchClickEvent(divElement);
    
  1565.             expect(onClick).toHaveBeenCalledTimes(2);
    
  1566.             expect(onClickCapture).toHaveBeenCalledTimes(2);
    
  1567.             expect(log[0]).toEqual(['capture', buttonElement]);
    
  1568.             expect(log[1]).toEqual(['capture', divElement]);
    
  1569.             expect(log[2]).toEqual(['bubble', divElement]);
    
  1570.             expect(log[3]).toEqual(['bubble', buttonElement]);
    
  1571.           });
    
  1572. 
    
  1573.           // @gate www
    
  1574.           it('handle propagation of click events mixed with onClick events', async () => {
    
  1575.             const buttonRef = React.createRef();
    
  1576.             const divRef = React.createRef();
    
  1577.             const log = [];
    
  1578.             const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  1579.             const onClickCapture = jest.fn(e =>
    
  1580.               log.push(['capture', e.currentTarget]),
    
  1581.             );
    
  1582.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  1583.             const setClickCapture = ReactDOM.unstable_createEventHandle(
    
  1584.               'click',
    
  1585.               {
    
  1586.                 capture: true,
    
  1587.               },
    
  1588.             );
    
  1589. 
    
  1590.             function Test() {
    
  1591.               React.useEffect(() => {
    
  1592.                 setClick(buttonRef.current, onClick);
    
  1593.                 setClickCapture(buttonRef.current, onClickCapture);
    
  1594. 
    
  1595.                 return () => {
    
  1596.                   setClick();
    
  1597.                   setClickCapture();
    
  1598.                 };
    
  1599.               });
    
  1600. 
    
  1601.               return (
    
  1602.                 <button ref={buttonRef}>
    
  1603.                   <div
    
  1604.                     ref={divRef}
    
  1605.                     onClick={onClick}
    
  1606.                     onClickCapture={onClickCapture}>
    
  1607.                     Click me!
    
  1608.                   </div>
    
  1609.                 </button>
    
  1610.               );
    
  1611.             }
    
  1612. 
    
  1613.             await act(() => {
    
  1614.               ReactDOM.render(<Test />, container);
    
  1615.             });
    
  1616. 
    
  1617.             const buttonElement = buttonRef.current;
    
  1618.             dispatchClickEvent(buttonElement);
    
  1619.             expect(onClick).toHaveBeenCalledTimes(1);
    
  1620.             expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  1621.             expect(log[0]).toEqual(['capture', buttonElement]);
    
  1622.             expect(log[1]).toEqual(['bubble', buttonElement]);
    
  1623. 
    
  1624.             const divElement = divRef.current;
    
  1625.             dispatchClickEvent(divElement);
    
  1626.             expect(onClick).toHaveBeenCalledTimes(3);
    
  1627.             expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  1628.             expect(log[2]).toEqual(['capture', buttonElement]);
    
  1629.             expect(log[3]).toEqual(['capture', divElement]);
    
  1630.             expect(log[4]).toEqual(['bubble', divElement]);
    
  1631.             expect(log[5]).toEqual(['bubble', buttonElement]);
    
  1632.           });
    
  1633. 
    
  1634.           // @gate www
    
  1635.           it('should correctly work for a basic "click" listener on the outer target', async () => {
    
  1636.             const log = [];
    
  1637.             const clickEvent = jest.fn(event => {
    
  1638.               log.push({
    
  1639.                 eventPhase: event.eventPhase,
    
  1640.                 type: event.type,
    
  1641.                 currentTarget: event.currentTarget,
    
  1642.                 target: event.target,
    
  1643.               });
    
  1644.             });
    
  1645.             const divRef = React.createRef();
    
  1646.             const buttonRef = React.createRef();
    
  1647.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  1648. 
    
  1649.             function Test() {
    
  1650.               React.useEffect(() => {
    
  1651.                 return setClick(divRef.current, clickEvent);
    
  1652.               });
    
  1653. 
    
  1654.               return (
    
  1655.                 <button ref={buttonRef}>
    
  1656.                   <div ref={divRef}>Click me!</div>
    
  1657.                 </button>
    
  1658.               );
    
  1659.             }
    
  1660. 
    
  1661.             await act(() => {
    
  1662.               ReactDOM.render(<Test />, container);
    
  1663.             });
    
  1664. 
    
  1665.             expect(container.innerHTML).toBe(
    
  1666.               '<button><div>Click me!</div></button>',
    
  1667.             );
    
  1668. 
    
  1669.             // Clicking the button should trigger the event callback
    
  1670.             let divElement = divRef.current;
    
  1671.             dispatchClickEvent(divElement);
    
  1672.             expect(log).toEqual([
    
  1673.               {
    
  1674.                 eventPhase: 3,
    
  1675.                 type: 'click',
    
  1676.                 currentTarget: divRef.current,
    
  1677.                 target: divRef.current,
    
  1678.               },
    
  1679.             ]);
    
  1680. 
    
  1681.             // Unmounting the container and clicking should not work
    
  1682.             ReactDOM.render(null, container);
    
  1683.             dispatchClickEvent(divElement);
    
  1684.             expect(clickEvent).toBeCalledTimes(1);
    
  1685. 
    
  1686.             // Re-rendering the container and clicking should work
    
  1687.             await act(() => {
    
  1688.               ReactDOM.render(<Test />, container);
    
  1689.             });
    
  1690. 
    
  1691.             divElement = divRef.current;
    
  1692.             dispatchClickEvent(divElement);
    
  1693.             expect(clickEvent).toBeCalledTimes(2);
    
  1694. 
    
  1695.             // Clicking the button should not work
    
  1696.             const buttonElement = buttonRef.current;
    
  1697.             dispatchClickEvent(buttonElement);
    
  1698.             expect(clickEvent).toBeCalledTimes(2);
    
  1699.           });
    
  1700. 
    
  1701.           // @gate www
    
  1702.           it('should correctly handle many nested target listeners', async () => {
    
  1703.             const buttonRef = React.createRef();
    
  1704.             const targetListener1 = jest.fn();
    
  1705.             const targetListener2 = jest.fn();
    
  1706.             const targetListener3 = jest.fn();
    
  1707.             const targetListener4 = jest.fn();
    
  1708.             let setClick1 = ReactDOM.unstable_createEventHandle('click', {
    
  1709.               capture: true,
    
  1710.             });
    
  1711.             let setClick2 = ReactDOM.unstable_createEventHandle('click', {
    
  1712.               capture: true,
    
  1713.             });
    
  1714.             let setClick3 = ReactDOM.unstable_createEventHandle('click');
    
  1715.             let setClick4 = ReactDOM.unstable_createEventHandle('click');
    
  1716. 
    
  1717.             function Test() {
    
  1718.               React.useEffect(() => {
    
  1719.                 const clearClick1 = setClick1(
    
  1720.                   buttonRef.current,
    
  1721.                   targetListener1,
    
  1722.                 );
    
  1723.                 const clearClick2 = setClick2(
    
  1724.                   buttonRef.current,
    
  1725.                   targetListener2,
    
  1726.                 );
    
  1727.                 const clearClick3 = setClick3(
    
  1728.                   buttonRef.current,
    
  1729.                   targetListener3,
    
  1730.                 );
    
  1731.                 const clearClick4 = setClick4(
    
  1732.                   buttonRef.current,
    
  1733.                   targetListener4,
    
  1734.                 );
    
  1735. 
    
  1736.                 return () => {
    
  1737.                   clearClick1();
    
  1738.                   clearClick2();
    
  1739.                   clearClick3();
    
  1740.                   clearClick4();
    
  1741.                 };
    
  1742.               });
    
  1743. 
    
  1744.               return <button ref={buttonRef}>Click me!</button>;
    
  1745.             }
    
  1746. 
    
  1747.             await act(() => {
    
  1748.               ReactDOM.render(<Test />, container);
    
  1749.             });
    
  1750. 
    
  1751.             let buttonElement = buttonRef.current;
    
  1752.             dispatchClickEvent(buttonElement);
    
  1753. 
    
  1754.             expect(targetListener1).toHaveBeenCalledTimes(1);
    
  1755.             expect(targetListener2).toHaveBeenCalledTimes(1);
    
  1756.             expect(targetListener3).toHaveBeenCalledTimes(1);
    
  1757.             expect(targetListener4).toHaveBeenCalledTimes(1);
    
  1758. 
    
  1759.             setClick1 = ReactDOM.unstable_createEventHandle('click');
    
  1760.             setClick2 = ReactDOM.unstable_createEventHandle('click');
    
  1761.             setClick3 = ReactDOM.unstable_createEventHandle('click');
    
  1762.             setClick4 = ReactDOM.unstable_createEventHandle('click');
    
  1763. 
    
  1764.             function Test2() {
    
  1765.               React.useEffect(() => {
    
  1766.                 const clearClick1 = setClick1(
    
  1767.                   buttonRef.current,
    
  1768.                   targetListener1,
    
  1769.                 );
    
  1770.                 const clearClick2 = setClick2(
    
  1771.                   buttonRef.current,
    
  1772.                   targetListener2,
    
  1773.                 );
    
  1774.                 const clearClick3 = setClick3(
    
  1775.                   buttonRef.current,
    
  1776.                   targetListener3,
    
  1777.                 );
    
  1778.                 const clearClick4 = setClick4(
    
  1779.                   buttonRef.current,
    
  1780.                   targetListener4,
    
  1781.                 );
    
  1782. 
    
  1783.                 return () => {
    
  1784.                   clearClick1();
    
  1785.                   clearClick2();
    
  1786.                   clearClick3();
    
  1787.                   clearClick4();
    
  1788.                 };
    
  1789.               });
    
  1790. 
    
  1791.               return <button ref={buttonRef}>Click me!</button>;
    
  1792.             }
    
  1793. 
    
  1794.             await act(() => {
    
  1795.               ReactDOM.render(<Test2 />, container);
    
  1796.             });
    
  1797. 
    
  1798.             buttonElement = buttonRef.current;
    
  1799.             dispatchClickEvent(buttonElement);
    
  1800.             expect(targetListener1).toHaveBeenCalledTimes(2);
    
  1801.             expect(targetListener2).toHaveBeenCalledTimes(2);
    
  1802.             expect(targetListener3).toHaveBeenCalledTimes(2);
    
  1803.             expect(targetListener4).toHaveBeenCalledTimes(2);
    
  1804.           });
    
  1805. 
    
  1806.           // @gate www
    
  1807.           it('should correctly handle stopPropagation correctly for target events', async () => {
    
  1808.             const buttonRef = React.createRef();
    
  1809.             const divRef = React.createRef();
    
  1810.             const clickEvent = jest.fn();
    
  1811.             const setClick1 = ReactDOM.unstable_createEventHandle('click', {
    
  1812.               bind: buttonRef,
    
  1813.             });
    
  1814.             const setClick2 = ReactDOM.unstable_createEventHandle('click');
    
  1815. 
    
  1816.             function Test() {
    
  1817.               React.useEffect(() => {
    
  1818.                 const clearClick1 = setClick1(buttonRef.current, clickEvent);
    
  1819.                 const clearClick2 = setClick2(divRef.current, e => {
    
  1820.                   e.stopPropagation();
    
  1821.                 });
    
  1822. 
    
  1823.                 return () => {
    
  1824.                   clearClick1();
    
  1825.                   clearClick2();
    
  1826.                 };
    
  1827.               });
    
  1828. 
    
  1829.               return (
    
  1830.                 <button ref={buttonRef}>
    
  1831.                   <div ref={divRef}>Click me!</div>
    
  1832.                 </button>
    
  1833.               );
    
  1834.             }
    
  1835. 
    
  1836.             await act(() => {
    
  1837.               ReactDOM.render(<Test />, container);
    
  1838.             });
    
  1839. 
    
  1840.             const divElement = divRef.current;
    
  1841.             dispatchClickEvent(divElement);
    
  1842.             expect(clickEvent).toHaveBeenCalledTimes(0);
    
  1843.           });
    
  1844. 
    
  1845.           // @gate www
    
  1846.           it('should correctly handle stopPropagation correctly for many target events', async () => {
    
  1847.             const buttonRef = React.createRef();
    
  1848.             const targetListener1 = jest.fn(e => e.stopPropagation());
    
  1849.             const targetListener2 = jest.fn(e => e.stopPropagation());
    
  1850.             const targetListener3 = jest.fn(e => e.stopPropagation());
    
  1851.             const targetListener4 = jest.fn(e => e.stopPropagation());
    
  1852.             const setClick1 = ReactDOM.unstable_createEventHandle('click');
    
  1853.             const setClick2 = ReactDOM.unstable_createEventHandle('click');
    
  1854.             const setClick3 = ReactDOM.unstable_createEventHandle('click');
    
  1855.             const setClick4 = ReactDOM.unstable_createEventHandle('click');
    
  1856. 
    
  1857.             function Test() {
    
  1858.               React.useEffect(() => {
    
  1859.                 const clearClick1 = setClick1(
    
  1860.                   buttonRef.current,
    
  1861.                   targetListener1,
    
  1862.                 );
    
  1863.                 const clearClick2 = setClick2(
    
  1864.                   buttonRef.current,
    
  1865.                   targetListener2,
    
  1866.                 );
    
  1867.                 const clearClick3 = setClick3(
    
  1868.                   buttonRef.current,
    
  1869.                   targetListener3,
    
  1870.                 );
    
  1871.                 const clearClick4 = setClick4(
    
  1872.                   buttonRef.current,
    
  1873.                   targetListener4,
    
  1874.                 );
    
  1875. 
    
  1876.                 return () => {
    
  1877.                   clearClick1();
    
  1878.                   clearClick2();
    
  1879.                   clearClick3();
    
  1880.                   clearClick4();
    
  1881.                 };
    
  1882.               });
    
  1883. 
    
  1884.               return <button ref={buttonRef}>Click me!</button>;
    
  1885.             }
    
  1886. 
    
  1887.             await act(() => {
    
  1888.               ReactDOM.render(<Test />, container);
    
  1889.             });
    
  1890. 
    
  1891.             const buttonElement = buttonRef.current;
    
  1892.             dispatchClickEvent(buttonElement);
    
  1893.             expect(targetListener1).toHaveBeenCalledTimes(1);
    
  1894.             expect(targetListener2).toHaveBeenCalledTimes(1);
    
  1895.             expect(targetListener3).toHaveBeenCalledTimes(1);
    
  1896.             expect(targetListener4).toHaveBeenCalledTimes(1);
    
  1897.           });
    
  1898. 
    
  1899.           // @gate www
    
  1900.           it('should correctly handle stopPropagation for mixed capture/bubbling target listeners', async () => {
    
  1901.             const buttonRef = React.createRef();
    
  1902.             const targetListener1 = jest.fn(e => e.stopPropagation());
    
  1903.             const targetListener2 = jest.fn(e => e.stopPropagation());
    
  1904.             const targetListener3 = jest.fn(e => e.stopPropagation());
    
  1905.             const targetListener4 = jest.fn(e => e.stopPropagation());
    
  1906.             const setClick1 = ReactDOM.unstable_createEventHandle('click', {
    
  1907.               capture: true,
    
  1908.             });
    
  1909.             const setClick2 = ReactDOM.unstable_createEventHandle('click', {
    
  1910.               capture: true,
    
  1911.             });
    
  1912.             const setClick3 = ReactDOM.unstable_createEventHandle('click');
    
  1913.             const setClick4 = ReactDOM.unstable_createEventHandle('click');
    
  1914. 
    
  1915.             function Test() {
    
  1916.               React.useEffect(() => {
    
  1917.                 const clearClick1 = setClick1(
    
  1918.                   buttonRef.current,
    
  1919.                   targetListener1,
    
  1920.                 );
    
  1921.                 const clearClick2 = setClick2(
    
  1922.                   buttonRef.current,
    
  1923.                   targetListener2,
    
  1924.                 );
    
  1925.                 const clearClick3 = setClick3(
    
  1926.                   buttonRef.current,
    
  1927.                   targetListener3,
    
  1928.                 );
    
  1929.                 const clearClick4 = setClick4(
    
  1930.                   buttonRef.current,
    
  1931.                   targetListener4,
    
  1932.                 );
    
  1933. 
    
  1934.                 return () => {
    
  1935.                   clearClick1();
    
  1936.                   clearClick2();
    
  1937.                   clearClick3();
    
  1938.                   clearClick4();
    
  1939.                 };
    
  1940.               });
    
  1941. 
    
  1942.               return <button ref={buttonRef}>Click me!</button>;
    
  1943.             }
    
  1944. 
    
  1945.             await act(() => {
    
  1946.               ReactDOM.render(<Test />, container);
    
  1947.             });
    
  1948. 
    
  1949.             const buttonElement = buttonRef.current;
    
  1950.             dispatchClickEvent(buttonElement);
    
  1951.             expect(targetListener1).toHaveBeenCalledTimes(1);
    
  1952.             expect(targetListener2).toHaveBeenCalledTimes(1);
    
  1953.             expect(targetListener3).toHaveBeenCalledTimes(0);
    
  1954.             expect(targetListener4).toHaveBeenCalledTimes(0);
    
  1955.           });
    
  1956. 
    
  1957.           // @gate www
    
  1958.           it('should work with concurrent mode updates', async () => {
    
  1959.             const log = [];
    
  1960.             const ref = React.createRef();
    
  1961.             const setClick1 = ReactDOM.unstable_createEventHandle('click');
    
  1962. 
    
  1963.             function Test({counter}) {
    
  1964.               React.useLayoutEffect(() => {
    
  1965.                 return setClick1(ref.current, () => {
    
  1966.                   log.push({counter});
    
  1967.                 });
    
  1968.               });
    
  1969. 
    
  1970.               Scheduler.log('Test');
    
  1971.               return <button ref={ref}>Press me</button>;
    
  1972.             }
    
  1973. 
    
  1974.             const root = ReactDOMClient.createRoot(container);
    
  1975.             root.render(<Test counter={0} />);
    
  1976. 
    
  1977.             await waitForAll(['Test']);
    
  1978. 
    
  1979.             // Click the button
    
  1980.             dispatchClickEvent(ref.current);
    
  1981.             expect(log).toEqual([{counter: 0}]);
    
  1982. 
    
  1983.             // Clear log
    
  1984.             log.length = 0;
    
  1985. 
    
  1986.             // Increase counter
    
  1987.             React.startTransition(() => {
    
  1988.               root.render(<Test counter={1} />);
    
  1989.             });
    
  1990.             // Yield before committing
    
  1991.             await waitFor(['Test']);
    
  1992. 
    
  1993.             // Click the button again
    
  1994.             dispatchClickEvent(ref.current);
    
  1995.             expect(log).toEqual([{counter: 0}]);
    
  1996. 
    
  1997.             // Clear log
    
  1998.             log.length = 0;
    
  1999. 
    
  2000.             // Commit
    
  2001.             await waitForAll([]);
    
  2002.             dispatchClickEvent(ref.current);
    
  2003.             expect(log).toEqual([{counter: 1}]);
    
  2004.           });
    
  2005. 
    
  2006.           // @gate www
    
  2007.           it('should correctly work for a basic "click" window listener', async () => {
    
  2008.             const log = [];
    
  2009.             const clickEvent = jest.fn(event => {
    
  2010.               log.push({
    
  2011.                 eventPhase: event.eventPhase,
    
  2012.                 type: event.type,
    
  2013.                 currentTarget: event.currentTarget,
    
  2014.                 target: event.target,
    
  2015.               });
    
  2016.             });
    
  2017.             const setClick1 = ReactDOM.unstable_createEventHandle('click');
    
  2018. 
    
  2019.             function Test() {
    
  2020.               React.useEffect(() => {
    
  2021.                 return setClick1(window, clickEvent);
    
  2022.               });
    
  2023. 
    
  2024.               return <button>Click anything!</button>;
    
  2025.             }
    
  2026.             await act(() => {
    
  2027.               ReactDOM.render(<Test />, container);
    
  2028.             });
    
  2029. 
    
  2030.             expect(container.innerHTML).toBe(
    
  2031.               '<button>Click anything!</button>',
    
  2032.             );
    
  2033. 
    
  2034.             // Clicking outside the button should trigger the event callback
    
  2035.             dispatchClickEvent(document.body);
    
  2036.             expect(log[0]).toEqual({
    
  2037.               eventPhase: 3,
    
  2038.               type: 'click',
    
  2039.               currentTarget: window,
    
  2040.               target: document.body,
    
  2041.             });
    
  2042. 
    
  2043.             // Unmounting the container and clicking should not work
    
  2044.             await act(() => {
    
  2045.               ReactDOM.render(null, container);
    
  2046.             });
    
  2047. 
    
  2048.             dispatchClickEvent(document.body);
    
  2049.             expect(clickEvent).toBeCalledTimes(1);
    
  2050. 
    
  2051.             // Re-rendering and clicking the body should work again
    
  2052.             await act(() => {
    
  2053.               ReactDOM.render(<Test />, container);
    
  2054.             });
    
  2055. 
    
  2056.             dispatchClickEvent(document.body);
    
  2057.             expect(clickEvent).toBeCalledTimes(2);
    
  2058.           });
    
  2059. 
    
  2060.           // @gate www
    
  2061.           it('handle propagation of click events on the window', async () => {
    
  2062.             const buttonRef = React.createRef();
    
  2063.             const divRef = React.createRef();
    
  2064.             const log = [];
    
  2065.             const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  2066.             const onClickCapture = jest.fn(e =>
    
  2067.               log.push(['capture', e.currentTarget]),
    
  2068.             );
    
  2069.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  2070.             const setClickCapture = ReactDOM.unstable_createEventHandle(
    
  2071.               'click',
    
  2072.               {
    
  2073.                 capture: true,
    
  2074.               },
    
  2075.             );
    
  2076. 
    
  2077.             function Test() {
    
  2078.               React.useEffect(() => {
    
  2079.                 const clearClick1 = setClick(window, onClick);
    
  2080.                 const clearClickCapture1 = setClickCapture(
    
  2081.                   window,
    
  2082.                   onClickCapture,
    
  2083.                 );
    
  2084.                 const clearClick2 = setClick(buttonRef.current, onClick);
    
  2085.                 const clearClickCapture2 = setClickCapture(
    
  2086.                   buttonRef.current,
    
  2087.                   onClickCapture,
    
  2088.                 );
    
  2089.                 const clearClick3 = setClick(divRef.current, onClick);
    
  2090.                 const clearClickCapture3 = setClickCapture(
    
  2091.                   divRef.current,
    
  2092.                   onClickCapture,
    
  2093.                 );
    
  2094. 
    
  2095.                 return () => {
    
  2096.                   clearClick1();
    
  2097.                   clearClickCapture1();
    
  2098.                   clearClick2();
    
  2099.                   clearClickCapture2();
    
  2100.                   clearClick3();
    
  2101.                   clearClickCapture3();
    
  2102.                 };
    
  2103.               });
    
  2104. 
    
  2105.               return (
    
  2106.                 <button ref={buttonRef}>
    
  2107.                   <div ref={divRef}>Click me!</div>
    
  2108.                 </button>
    
  2109.               );
    
  2110.             }
    
  2111. 
    
  2112.             await act(() => {
    
  2113.               ReactDOM.render(<Test />, container);
    
  2114.             });
    
  2115. 
    
  2116.             const buttonElement = buttonRef.current;
    
  2117.             dispatchClickEvent(buttonElement);
    
  2118.             expect(onClick).toHaveBeenCalledTimes(2);
    
  2119.             expect(onClickCapture).toHaveBeenCalledTimes(2);
    
  2120.             expect(log[0]).toEqual(['capture', window]);
    
  2121.             expect(log[1]).toEqual(['capture', buttonElement]);
    
  2122.             expect(log[2]).toEqual(['bubble', buttonElement]);
    
  2123.             expect(log[3]).toEqual(['bubble', window]);
    
  2124. 
    
  2125.             log.length = 0;
    
  2126.             onClick.mockClear();
    
  2127.             onClickCapture.mockClear();
    
  2128. 
    
  2129.             const divElement = divRef.current;
    
  2130.             dispatchClickEvent(divElement);
    
  2131.             expect(onClick).toHaveBeenCalledTimes(3);
    
  2132.             expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  2133.             expect(log[0]).toEqual(['capture', window]);
    
  2134.             expect(log[1]).toEqual(['capture', buttonElement]);
    
  2135.             expect(log[2]).toEqual(['capture', divElement]);
    
  2136.             expect(log[3]).toEqual(['bubble', divElement]);
    
  2137.             expect(log[4]).toEqual(['bubble', buttonElement]);
    
  2138.             expect(log[5]).toEqual(['bubble', window]);
    
  2139.           });
    
  2140. 
    
  2141.           // @gate www
    
  2142.           it('should correctly handle stopPropagation for mixed listeners', async () => {
    
  2143.             const buttonRef = React.createRef();
    
  2144.             const rootListener1 = jest.fn(e => e.stopPropagation());
    
  2145.             const rootListener2 = jest.fn();
    
  2146.             const targetListener1 = jest.fn();
    
  2147.             const targetListener2 = jest.fn();
    
  2148.             const setClick1 = ReactDOM.unstable_createEventHandle('click', {
    
  2149.               capture: true,
    
  2150.             });
    
  2151.             const setClick2 = ReactDOM.unstable_createEventHandle('click', {
    
  2152.               capture: true,
    
  2153.             });
    
  2154.             const setClick3 = ReactDOM.unstable_createEventHandle('click');
    
  2155.             const setClick4 = ReactDOM.unstable_createEventHandle('click');
    
  2156. 
    
  2157.             function Test() {
    
  2158.               React.useEffect(() => {
    
  2159.                 const clearClick1 = setClick1(window, rootListener1);
    
  2160.                 const clearClick2 = setClick2(
    
  2161.                   buttonRef.current,
    
  2162.                   targetListener1,
    
  2163.                 );
    
  2164.                 const clearClick3 = setClick3(window, rootListener2);
    
  2165.                 const clearClick4 = setClick4(
    
  2166.                   buttonRef.current,
    
  2167.                   targetListener2,
    
  2168.                 );
    
  2169. 
    
  2170.                 return () => {
    
  2171.                   clearClick1();
    
  2172.                   clearClick2();
    
  2173.                   clearClick3();
    
  2174.                   clearClick4();
    
  2175.                 };
    
  2176.               });
    
  2177. 
    
  2178.               return <button ref={buttonRef}>Click me!</button>;
    
  2179.             }
    
  2180. 
    
  2181.             await act(() => {
    
  2182.               ReactDOM.render(<Test />, container);
    
  2183.             });
    
  2184. 
    
  2185.             const buttonElement = buttonRef.current;
    
  2186.             dispatchClickEvent(buttonElement);
    
  2187.             expect(rootListener1).toHaveBeenCalledTimes(1);
    
  2188.             expect(targetListener1).toHaveBeenCalledTimes(0);
    
  2189.             expect(targetListener2).toHaveBeenCalledTimes(0);
    
  2190.             expect(rootListener2).toHaveBeenCalledTimes(0);
    
  2191.           });
    
  2192. 
    
  2193.           // @gate www
    
  2194.           it('should correctly handle stopPropagation for delegated listeners', async () => {
    
  2195.             const buttonRef = React.createRef();
    
  2196.             const rootListener1 = jest.fn(e => e.stopPropagation());
    
  2197.             const rootListener2 = jest.fn();
    
  2198.             const rootListener3 = jest.fn(e => e.stopPropagation());
    
  2199.             const rootListener4 = jest.fn();
    
  2200.             const setClick1 = ReactDOM.unstable_createEventHandle('click', {
    
  2201.               capture: true,
    
  2202.             });
    
  2203.             const setClick2 = ReactDOM.unstable_createEventHandle('click', {
    
  2204.               capture: true,
    
  2205.             });
    
  2206.             const setClick3 = ReactDOM.unstable_createEventHandle('click');
    
  2207.             const setClick4 = ReactDOM.unstable_createEventHandle('click');
    
  2208. 
    
  2209.             function Test() {
    
  2210.               React.useEffect(() => {
    
  2211.                 const clearClick1 = setClick1(window, rootListener1);
    
  2212.                 const clearClick2 = setClick2(window, rootListener2);
    
  2213.                 const clearClick3 = setClick3(window, rootListener3);
    
  2214.                 const clearClick4 = setClick4(window, rootListener4);
    
  2215. 
    
  2216.                 return () => {
    
  2217.                   clearClick1();
    
  2218.                   clearClick2();
    
  2219.                   clearClick3();
    
  2220.                   clearClick4();
    
  2221.                 };
    
  2222.               });
    
  2223. 
    
  2224.               return <button ref={buttonRef}>Click me!</button>;
    
  2225.             }
    
  2226. 
    
  2227.             await act(() => {
    
  2228.               ReactDOM.render(<Test />, container);
    
  2229.             });
    
  2230. 
    
  2231.             const buttonElement = buttonRef.current;
    
  2232.             dispatchClickEvent(buttonElement);
    
  2233.             expect(rootListener1).toHaveBeenCalledTimes(1);
    
  2234.             expect(rootListener2).toHaveBeenCalledTimes(1);
    
  2235.             expect(rootListener3).toHaveBeenCalledTimes(0);
    
  2236.             expect(rootListener4).toHaveBeenCalledTimes(0);
    
  2237.           });
    
  2238. 
    
  2239.           // @gate www
    
  2240.           it('handle propagation of click events on the window and document', async () => {
    
  2241.             const buttonRef = React.createRef();
    
  2242.             const divRef = React.createRef();
    
  2243.             const log = [];
    
  2244.             const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  2245.             const onClickCapture = jest.fn(e =>
    
  2246.               log.push(['capture', e.currentTarget]),
    
  2247.             );
    
  2248.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  2249.             const setClickCapture = ReactDOM.unstable_createEventHandle(
    
  2250.               'click',
    
  2251.               {
    
  2252.                 capture: true,
    
  2253.               },
    
  2254.             );
    
  2255. 
    
  2256.             function Test() {
    
  2257.               React.useEffect(() => {
    
  2258.                 const clearClick1 = setClick(window, onClick);
    
  2259.                 const clearClickCapture1 = setClickCapture(
    
  2260.                   window,
    
  2261.                   onClickCapture,
    
  2262.                 );
    
  2263.                 const clearClick2 = setClick(document, onClick);
    
  2264.                 const clearClickCapture2 = setClickCapture(
    
  2265.                   document,
    
  2266.                   onClickCapture,
    
  2267.                 );
    
  2268.                 const clearClick3 = setClick(buttonRef.current, onClick);
    
  2269.                 const clearClickCapture3 = setClickCapture(
    
  2270.                   buttonRef.current,
    
  2271.                   onClickCapture,
    
  2272.                 );
    
  2273.                 const clearClick4 = setClick(divRef.current, onClick);
    
  2274.                 const clearClickCapture4 = setClickCapture(
    
  2275.                   divRef.current,
    
  2276.                   onClickCapture,
    
  2277.                 );
    
  2278. 
    
  2279.                 return () => {
    
  2280.                   clearClick1();
    
  2281.                   clearClickCapture1();
    
  2282.                   clearClick2();
    
  2283.                   clearClickCapture2();
    
  2284.                   clearClick3();
    
  2285.                   clearClickCapture3();
    
  2286.                   clearClick4();
    
  2287.                   clearClickCapture4();
    
  2288.                 };
    
  2289.               });
    
  2290. 
    
  2291.               return (
    
  2292.                 <button ref={buttonRef}>
    
  2293.                   <div ref={divRef}>Click me!</div>
    
  2294.                 </button>
    
  2295.               );
    
  2296.             }
    
  2297. 
    
  2298.             await act(() => {
    
  2299.               ReactDOM.render(<Test />, container);
    
  2300.             });
    
  2301. 
    
  2302.             const buttonElement = buttonRef.current;
    
  2303.             dispatchClickEvent(buttonElement);
    
  2304.             expect(onClick).toHaveBeenCalledTimes(3);
    
  2305.             expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  2306. 
    
  2307.             if (enableLegacyFBSupport) {
    
  2308.               expect(log[0]).toEqual(['capture', window]);
    
  2309.               expect(log[1]).toEqual(['capture', document]);
    
  2310.               expect(log[2]).toEqual(['capture', buttonElement]);
    
  2311.               expect(log[3]).toEqual(['bubble', document]);
    
  2312.               expect(log[4]).toEqual(['bubble', buttonElement]);
    
  2313.               expect(log[5]).toEqual(['bubble', window]);
    
  2314.             } else {
    
  2315.               expect(log[0]).toEqual(['capture', window]);
    
  2316.               expect(log[1]).toEqual(['capture', document]);
    
  2317.               expect(log[2]).toEqual(['capture', buttonElement]);
    
  2318.               expect(log[3]).toEqual(['bubble', buttonElement]);
    
  2319.               expect(log[4]).toEqual(['bubble', document]);
    
  2320.               expect(log[5]).toEqual(['bubble', window]);
    
  2321.             }
    
  2322. 
    
  2323.             log.length = 0;
    
  2324.             onClick.mockClear();
    
  2325.             onClickCapture.mockClear();
    
  2326. 
    
  2327.             const divElement = divRef.current;
    
  2328.             dispatchClickEvent(divElement);
    
  2329.             expect(onClick).toHaveBeenCalledTimes(4);
    
  2330.             expect(onClickCapture).toHaveBeenCalledTimes(4);
    
  2331. 
    
  2332.             if (enableLegacyFBSupport) {
    
  2333.               expect(log[0]).toEqual(['capture', window]);
    
  2334.               expect(log[1]).toEqual(['capture', document]);
    
  2335.               expect(log[2]).toEqual(['capture', buttonElement]);
    
  2336.               expect(log[3]).toEqual(['capture', divElement]);
    
  2337.               expect(log[4]).toEqual(['bubble', document]);
    
  2338.               expect(log[5]).toEqual(['bubble', divElement]);
    
  2339.               expect(log[6]).toEqual(['bubble', buttonElement]);
    
  2340.               expect(log[7]).toEqual(['bubble', window]);
    
  2341.             } else {
    
  2342.               expect(log[0]).toEqual(['capture', window]);
    
  2343.               expect(log[1]).toEqual(['capture', document]);
    
  2344.               expect(log[2]).toEqual(['capture', buttonElement]);
    
  2345.               expect(log[3]).toEqual(['capture', divElement]);
    
  2346.               expect(log[4]).toEqual(['bubble', divElement]);
    
  2347.               expect(log[5]).toEqual(['bubble', buttonElement]);
    
  2348.               expect(log[6]).toEqual(['bubble', document]);
    
  2349.               expect(log[7]).toEqual(['bubble', window]);
    
  2350.             }
    
  2351.           });
    
  2352. 
    
  2353.           // @gate www
    
  2354.           it('does not support custom user events', () => {
    
  2355.             // With eager listeners, supporting custom events via this API doesn't make sense
    
  2356.             // because we can't know a full list of them ahead of time. Let's check we throw
    
  2357.             // since otherwise we'd end up with inconsistent behavior, like no portal bubbling.
    
  2358.             expect(() => {
    
  2359.               ReactDOM.unstable_createEventHandle('custom-event');
    
  2360.             }).toThrow(
    
  2361.               'Cannot call unstable_createEventHandle with "custom-event", as it is not an event known to React.',
    
  2362.             );
    
  2363.           });
    
  2364. 
    
  2365.           // @gate www
    
  2366.           it('beforeblur and afterblur are called after a focused element is unmounted', async () => {
    
  2367.             const log = [];
    
  2368.             // We have to persist here because we want to read relatedTarget later.
    
  2369.             const onAfterBlur = jest.fn(e => {
    
  2370.               e.persist();
    
  2371.               log.push(e.type);
    
  2372.             });
    
  2373.             const onBeforeBlur = jest.fn(e => log.push(e.type));
    
  2374.             const innerRef = React.createRef();
    
  2375.             const innerRef2 = React.createRef();
    
  2376.             const setAfterBlurHandle =
    
  2377.               ReactDOM.unstable_createEventHandle('afterblur');
    
  2378.             const setBeforeBlurHandle =
    
  2379.               ReactDOM.unstable_createEventHandle('beforeblur');
    
  2380. 
    
  2381.             const Component = ({show}) => {
    
  2382.               const ref = React.useRef(null);
    
  2383. 
    
  2384.               React.useEffect(() => {
    
  2385.                 const clear1 = setAfterBlurHandle(document, onAfterBlur);
    
  2386.                 const clear2 = setBeforeBlurHandle(ref.current, onBeforeBlur);
    
  2387. 
    
  2388.                 return () => {
    
  2389.                   clear1();
    
  2390.                   clear2();
    
  2391.                 };
    
  2392.               });
    
  2393. 
    
  2394.               return (
    
  2395.                 <div ref={ref}>
    
  2396.                   {show && <input ref={innerRef} />}
    
  2397.                   <div ref={innerRef2} />
    
  2398.                 </div>
    
  2399.               );
    
  2400.             };
    
  2401. 
    
  2402.             await act(() => {
    
  2403.               ReactDOM.render(<Component show={true} />, container);
    
  2404.             });
    
  2405. 
    
  2406.             const inner = innerRef.current;
    
  2407.             const target = createEventTarget(inner);
    
  2408.             target.focus();
    
  2409.             expect(onBeforeBlur).toHaveBeenCalledTimes(0);
    
  2410.             expect(onAfterBlur).toHaveBeenCalledTimes(0);
    
  2411. 
    
  2412.             await act(() => {
    
  2413.               ReactDOM.render(<Component show={false} />, container);
    
  2414.             });
    
  2415. 
    
  2416.             expect(onBeforeBlur).toHaveBeenCalledTimes(1);
    
  2417.             expect(onAfterBlur).toHaveBeenCalledTimes(1);
    
  2418.             expect(onAfterBlur).toHaveBeenCalledWith(
    
  2419.               expect.objectContaining({relatedTarget: inner}),
    
  2420.             );
    
  2421.             expect(log).toEqual(['beforeblur', 'afterblur']);
    
  2422.           });
    
  2423. 
    
  2424.           // @gate www
    
  2425.           it('beforeblur and afterblur are called after a nested focused element is unmounted', async () => {
    
  2426.             const log = [];
    
  2427.             // We have to persist here because we want to read relatedTarget later.
    
  2428.             const onAfterBlur = jest.fn(e => {
    
  2429.               e.persist();
    
  2430.               log.push(e.type);
    
  2431.             });
    
  2432.             const onBeforeBlur = jest.fn(e => log.push(e.type));
    
  2433.             const innerRef = React.createRef();
    
  2434.             const innerRef2 = React.createRef();
    
  2435.             const setAfterBlurHandle =
    
  2436.               ReactDOM.unstable_createEventHandle('afterblur');
    
  2437.             const setBeforeBlurHandle =
    
  2438.               ReactDOM.unstable_createEventHandle('beforeblur');
    
  2439. 
    
  2440.             const Component = ({show}) => {
    
  2441.               const ref = React.useRef(null);
    
  2442. 
    
  2443.               React.useEffect(() => {
    
  2444.                 const clear1 = setAfterBlurHandle(document, onAfterBlur);
    
  2445.                 const clear2 = setBeforeBlurHandle(ref.current, onBeforeBlur);
    
  2446. 
    
  2447.                 return () => {
    
  2448.                   clear1();
    
  2449.                   clear2();
    
  2450.                 };
    
  2451.               });
    
  2452. 
    
  2453.               return (
    
  2454.                 <div ref={ref}>
    
  2455.                   {show && (
    
  2456.                     <div>
    
  2457.                       <input ref={innerRef} />
    
  2458.                     </div>
    
  2459.                   )}
    
  2460.                   <div ref={innerRef2} />
    
  2461.                 </div>
    
  2462.               );
    
  2463.             };
    
  2464. 
    
  2465.             await act(() => {
    
  2466.               ReactDOM.render(<Component show={true} />, container);
    
  2467.             });
    
  2468. 
    
  2469.             const inner = innerRef.current;
    
  2470.             const target = createEventTarget(inner);
    
  2471.             target.focus();
    
  2472.             expect(onBeforeBlur).toHaveBeenCalledTimes(0);
    
  2473.             expect(onAfterBlur).toHaveBeenCalledTimes(0);
    
  2474. 
    
  2475.             await act(() => {
    
  2476.               ReactDOM.render(<Component show={false} />, container);
    
  2477.             });
    
  2478. 
    
  2479.             expect(onBeforeBlur).toHaveBeenCalledTimes(1);
    
  2480.             expect(onAfterBlur).toHaveBeenCalledTimes(1);
    
  2481.             expect(onAfterBlur).toHaveBeenCalledWith(
    
  2482.               expect.objectContaining({relatedTarget: inner}),
    
  2483.             );
    
  2484.             expect(log).toEqual(['beforeblur', 'afterblur']);
    
  2485.           });
    
  2486. 
    
  2487.           // @gate www
    
  2488.           it('beforeblur should skip handlers from a deleted subtree after the focused element is unmounted', async () => {
    
  2489.             const onBeforeBlur = jest.fn();
    
  2490.             const innerRef = React.createRef();
    
  2491.             const innerRef2 = React.createRef();
    
  2492.             const setBeforeBlurHandle =
    
  2493.               ReactDOM.unstable_createEventHandle('beforeblur');
    
  2494.             const ref2 = React.createRef();
    
  2495. 
    
  2496.             const Component = ({show}) => {
    
  2497.               const ref = React.useRef(null);
    
  2498. 
    
  2499.               React.useEffect(() => {
    
  2500.                 const clear1 = setBeforeBlurHandle(ref.current, onBeforeBlur);
    
  2501.                 let clear2;
    
  2502.                 if (ref2.current) {
    
  2503.                   clear2 = setBeforeBlurHandle(ref2.current, onBeforeBlur);
    
  2504.                 }
    
  2505. 
    
  2506.                 return () => {
    
  2507.                   clear1();
    
  2508.                   if (clear2) {
    
  2509.                     clear2();
    
  2510.                   }
    
  2511.                 };
    
  2512.               });
    
  2513. 
    
  2514.               return (
    
  2515.                 <div ref={ref}>
    
  2516.                   {show && (
    
  2517.                     <div ref={ref2}>
    
  2518.                       <input ref={innerRef} />
    
  2519.                     </div>
    
  2520.                   )}
    
  2521.                   <div ref={innerRef2} />
    
  2522.                 </div>
    
  2523.               );
    
  2524.             };
    
  2525. 
    
  2526.             await act(() => {
    
  2527.               ReactDOM.render(<Component show={true} />, container);
    
  2528.             });
    
  2529. 
    
  2530.             const inner = innerRef.current;
    
  2531.             const target = createEventTarget(inner);
    
  2532.             target.focus();
    
  2533.             expect(onBeforeBlur).toHaveBeenCalledTimes(0);
    
  2534. 
    
  2535.             await act(() => {
    
  2536.               ReactDOM.render(<Component show={false} />, container);
    
  2537.             });
    
  2538. 
    
  2539.             expect(onBeforeBlur).toHaveBeenCalledTimes(1);
    
  2540.           });
    
  2541. 
    
  2542.           // @gate www
    
  2543.           it('beforeblur and afterblur are called after a focused element is suspended', async () => {
    
  2544.             const log = [];
    
  2545.             // We have to persist here because we want to read relatedTarget later.
    
  2546.             const onAfterBlur = jest.fn(e => {
    
  2547.               e.persist();
    
  2548.               log.push(e.type);
    
  2549.             });
    
  2550.             const onBeforeBlur = jest.fn(e => log.push(e.type));
    
  2551.             const innerRef = React.createRef();
    
  2552.             const Suspense = React.Suspense;
    
  2553.             let suspend = false;
    
  2554.             let resolve;
    
  2555.             const promise = new Promise(
    
  2556.               resolvePromise => (resolve = resolvePromise),
    
  2557.             );
    
  2558.             const setAfterBlurHandle =
    
  2559.               ReactDOM.unstable_createEventHandle('afterblur');
    
  2560.             const setBeforeBlurHandle =
    
  2561.               ReactDOM.unstable_createEventHandle('beforeblur');
    
  2562. 
    
  2563.             function Child() {
    
  2564.               if (suspend) {
    
  2565.                 throw promise;
    
  2566.               } else {
    
  2567.                 return <input ref={innerRef} />;
    
  2568.               }
    
  2569.             }
    
  2570. 
    
  2571.             const Component = () => {
    
  2572.               const ref = React.useRef(null);
    
  2573. 
    
  2574.               React.useEffect(() => {
    
  2575.                 const clear1 = setAfterBlurHandle(document, onAfterBlur);
    
  2576.                 const clear2 = setBeforeBlurHandle(ref.current, onBeforeBlur);
    
  2577. 
    
  2578.                 return () => {
    
  2579.                   clear1();
    
  2580.                   clear2();
    
  2581.                 };
    
  2582.               });
    
  2583. 
    
  2584.               return (
    
  2585.                 <div ref={ref}>
    
  2586.                   <Suspense fallback="Loading...">
    
  2587.                     <Child />
    
  2588.                   </Suspense>
    
  2589.                 </div>
    
  2590.               );
    
  2591.             };
    
  2592. 
    
  2593.             const container2 = document.createElement('div');
    
  2594.             document.body.appendChild(container2);
    
  2595. 
    
  2596.             const root = ReactDOMClient.createRoot(container2);
    
  2597. 
    
  2598.             await act(() => {
    
  2599.               root.render(<Component />);
    
  2600.             });
    
  2601.             jest.runAllTimers();
    
  2602. 
    
  2603.             const inner = innerRef.current;
    
  2604.             const target = createEventTarget(inner);
    
  2605.             target.focus();
    
  2606.             expect(onBeforeBlur).toHaveBeenCalledTimes(0);
    
  2607.             expect(onAfterBlur).toHaveBeenCalledTimes(0);
    
  2608. 
    
  2609.             suspend = true;
    
  2610.             await act(() => {
    
  2611.               root.render(<Component />);
    
  2612.             });
    
  2613.             jest.runAllTimers();
    
  2614. 
    
  2615.             expect(onBeforeBlur).toHaveBeenCalledTimes(1);
    
  2616.             expect(onAfterBlur).toHaveBeenCalledTimes(1);
    
  2617.             expect(onAfterBlur).toHaveBeenCalledWith(
    
  2618.               expect.objectContaining({relatedTarget: inner}),
    
  2619.             );
    
  2620.             resolve();
    
  2621.             expect(log).toEqual(['beforeblur', 'afterblur']);
    
  2622. 
    
  2623.             document.body.removeChild(container2);
    
  2624.           });
    
  2625. 
    
  2626.           // @gate www
    
  2627.           it('beforeblur should skip handlers from a deleted subtree after the focused element is suspended', async () => {
    
  2628.             const onBeforeBlur = jest.fn();
    
  2629.             const innerRef = React.createRef();
    
  2630.             const innerRef2 = React.createRef();
    
  2631.             const setBeforeBlurHandle =
    
  2632.               ReactDOM.unstable_createEventHandle('beforeblur');
    
  2633.             const ref2 = React.createRef();
    
  2634.             const Suspense = React.Suspense;
    
  2635.             let suspend = false;
    
  2636.             let resolve;
    
  2637.             const promise = new Promise(
    
  2638.               resolvePromise => (resolve = resolvePromise),
    
  2639.             );
    
  2640. 
    
  2641.             function Child() {
    
  2642.               if (suspend) {
    
  2643.                 throw promise;
    
  2644.               } else {
    
  2645.                 return <input ref={innerRef} />;
    
  2646.               }
    
  2647.             }
    
  2648. 
    
  2649.             const Component = () => {
    
  2650.               const ref = React.useRef(null);
    
  2651. 
    
  2652.               React.useEffect(() => {
    
  2653.                 const clear1 = setBeforeBlurHandle(ref.current, onBeforeBlur);
    
  2654.                 let clear2;
    
  2655.                 if (ref2.current) {
    
  2656.                   clear2 = setBeforeBlurHandle(ref2.current, onBeforeBlur);
    
  2657.                 }
    
  2658. 
    
  2659.                 return () => {
    
  2660.                   clear1();
    
  2661.                   if (clear2) {
    
  2662.                     clear2();
    
  2663.                   }
    
  2664.                 };
    
  2665.               });
    
  2666. 
    
  2667.               return (
    
  2668.                 <div ref={ref}>
    
  2669.                   <Suspense fallback="Loading...">
    
  2670.                     <div ref={ref2}>
    
  2671.                       <Child />
    
  2672.                     </div>
    
  2673.                   </Suspense>
    
  2674.                   <div ref={innerRef2} />
    
  2675.                 </div>
    
  2676.               );
    
  2677.             };
    
  2678. 
    
  2679.             const container2 = document.createElement('div');
    
  2680.             document.body.appendChild(container2);
    
  2681. 
    
  2682.             const root = ReactDOMClient.createRoot(container2);
    
  2683. 
    
  2684.             await act(() => {
    
  2685.               root.render(<Component />);
    
  2686.             });
    
  2687.             jest.runAllTimers();
    
  2688. 
    
  2689.             const inner = innerRef.current;
    
  2690.             const target = createEventTarget(inner);
    
  2691.             target.focus();
    
  2692.             expect(onBeforeBlur).toHaveBeenCalledTimes(0);
    
  2693. 
    
  2694.             suspend = true;
    
  2695.             await act(() => {
    
  2696.               root.render(<Component />);
    
  2697.             });
    
  2698.             jest.runAllTimers();
    
  2699. 
    
  2700.             expect(onBeforeBlur).toHaveBeenCalledTimes(1);
    
  2701.             resolve();
    
  2702. 
    
  2703.             document.body.removeChild(container2);
    
  2704.           });
    
  2705. 
    
  2706.           // @gate www
    
  2707.           it('regression: does not fire beforeblur/afterblur if target is already hidden', async () => {
    
  2708.             const Suspense = React.Suspense;
    
  2709.             let suspend = false;
    
  2710.             const fakePromise = {then() {}};
    
  2711.             const setBeforeBlurHandle =
    
  2712.               ReactDOM.unstable_createEventHandle('beforeblur');
    
  2713.             const innerRef = React.createRef();
    
  2714. 
    
  2715.             function Child() {
    
  2716.               if (suspend) {
    
  2717.                 throw fakePromise;
    
  2718.               }
    
  2719.               return <input ref={innerRef} />;
    
  2720.             }
    
  2721. 
    
  2722.             const Component = () => {
    
  2723.               const ref = React.useRef(null);
    
  2724.               const [, setState] = React.useState(0);
    
  2725. 
    
  2726.               React.useEffect(() => {
    
  2727.                 return setBeforeBlurHandle(ref.current, () => {
    
  2728.                   // In the regression case, this would trigger an update, then
    
  2729.                   // the resulting render would trigger another blur event,
    
  2730.                   // which would trigger an update again, and on and on in an
    
  2731.                   // infinite loop.
    
  2732.                   setState(n => n + 1);
    
  2733.                 });
    
  2734.               }, []);
    
  2735. 
    
  2736.               return (
    
  2737.                 <div ref={ref}>
    
  2738.                   <Suspense fallback="Loading...">
    
  2739.                     <Child />
    
  2740.                   </Suspense>
    
  2741.                 </div>
    
  2742.               );
    
  2743.             };
    
  2744. 
    
  2745.             const container2 = document.createElement('div');
    
  2746.             document.body.appendChild(container2);
    
  2747. 
    
  2748.             const root = ReactDOMClient.createRoot(container2);
    
  2749.             await act(() => {
    
  2750.               root.render(<Component />);
    
  2751.             });
    
  2752. 
    
  2753.             // Focus the input node
    
  2754.             const inner = innerRef.current;
    
  2755.             const target = createEventTarget(inner);
    
  2756.             target.focus();
    
  2757. 
    
  2758.             // Suspend. This hides the input node, causing it to lose focus.
    
  2759.             suspend = true;
    
  2760.             await act(() => {
    
  2761.               root.render(<Component />);
    
  2762.             });
    
  2763. 
    
  2764.             document.body.removeChild(container2);
    
  2765.           });
    
  2766. 
    
  2767.           // @gate www
    
  2768.           it('handle propagation of click events between disjointed comment roots', async () => {
    
  2769.             const buttonRef = React.createRef();
    
  2770.             const divRef = React.createRef();
    
  2771.             const log = [];
    
  2772.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  2773.             const setClickCapture = ReactDOM.unstable_createEventHandle(
    
  2774.               'click',
    
  2775.               {capture: true},
    
  2776.             );
    
  2777.             const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  2778.             const onClickCapture = jest.fn(e =>
    
  2779.               log.push(['capture', e.currentTarget]),
    
  2780.             );
    
  2781. 
    
  2782.             function Child() {
    
  2783.               React.useEffect(() => {
    
  2784.                 const click1 = setClick(divRef.current, onClick);
    
  2785.                 const click2 = setClickCapture(divRef.current, onClickCapture);
    
  2786.                 return () => {
    
  2787.                   click1();
    
  2788.                   click2();
    
  2789.                 };
    
  2790.               });
    
  2791. 
    
  2792.               return <div ref={divRef}>Click me!</div>;
    
  2793.             }
    
  2794. 
    
  2795.             function Parent() {
    
  2796.               React.useEffect(() => {
    
  2797.                 const click1 = setClick(buttonRef.current, onClick);
    
  2798.                 const click2 = setClickCapture(
    
  2799.                   buttonRef.current,
    
  2800.                   onClickCapture,
    
  2801.                 );
    
  2802.                 return () => {
    
  2803.                   click1();
    
  2804.                   click2();
    
  2805.                 };
    
  2806.               });
    
  2807. 
    
  2808.               return <button ref={buttonRef} />;
    
  2809.             }
    
  2810. 
    
  2811.             // We use a comment node here, then mount to it
    
  2812.             const disjointedNode = document.createComment(
    
  2813.               ' react-mount-point-unstable ',
    
  2814.             );
    
  2815.             await act(() => {
    
  2816.               ReactDOM.render(<Parent />, container);
    
  2817.             });
    
  2818.             buttonRef.current.appendChild(disjointedNode);
    
  2819.             await act(() => {
    
  2820.               ReactDOM.render(<Child />, disjointedNode);
    
  2821.             });
    
  2822. 
    
  2823.             const buttonElement = buttonRef.current;
    
  2824.             dispatchClickEvent(buttonElement);
    
  2825.             expect(onClick).toHaveBeenCalledTimes(1);
    
  2826.             expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  2827.             expect(log[0]).toEqual(['capture', buttonElement]);
    
  2828.             expect(log[1]).toEqual(['bubble', buttonElement]);
    
  2829. 
    
  2830.             const divElement = divRef.current;
    
  2831.             dispatchClickEvent(divElement);
    
  2832.             expect(onClick).toHaveBeenCalledTimes(3);
    
  2833.             expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  2834.             expect(log[2]).toEqual(['capture', buttonElement]);
    
  2835.             expect(log[3]).toEqual(['capture', divElement]);
    
  2836.             expect(log[4]).toEqual(['bubble', divElement]);
    
  2837.             expect(log[5]).toEqual(['bubble', buttonElement]);
    
  2838.           });
    
  2839. 
    
  2840.           // @gate www
    
  2841.           it('propagates known createEventHandle events through portals without inner listeners', async () => {
    
  2842.             const buttonRef = React.createRef();
    
  2843.             const divRef = React.createRef();
    
  2844.             const log = [];
    
  2845.             const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
    
  2846.             const onClickCapture = jest.fn(e =>
    
  2847.               log.push(['capture', e.currentTarget]),
    
  2848.             );
    
  2849.             const setClick = ReactDOM.unstable_createEventHandle('click');
    
  2850.             const setClickCapture = ReactDOM.unstable_createEventHandle(
    
  2851.               'click',
    
  2852.               {
    
  2853.                 capture: true,
    
  2854.               },
    
  2855.             );
    
  2856. 
    
  2857.             const portalElement = document.createElement('div');
    
  2858.             document.body.appendChild(portalElement);
    
  2859. 
    
  2860.             function Child() {
    
  2861.               return <div ref={divRef}>Click me!</div>;
    
  2862.             }
    
  2863. 
    
  2864.             function Parent() {
    
  2865.               React.useEffect(() => {
    
  2866.                 const clear1 = setClick(buttonRef.current, onClick);
    
  2867.                 const clear2 = setClickCapture(
    
  2868.                   buttonRef.current,
    
  2869.                   onClickCapture,
    
  2870.                 );
    
  2871.                 return () => {
    
  2872.                   clear1();
    
  2873.                   clear2();
    
  2874.                 };
    
  2875.               });
    
  2876. 
    
  2877.               return (
    
  2878.                 <button ref={buttonRef}>
    
  2879.                   {ReactDOM.createPortal(<Child />, portalElement)}
    
  2880.                 </button>
    
  2881.               );
    
  2882.             }
    
  2883. 
    
  2884.             await act(() => {
    
  2885.               ReactDOM.render(<Parent />, container);
    
  2886.             });
    
  2887. 
    
  2888.             const divElement = divRef.current;
    
  2889.             const buttonElement = buttonRef.current;
    
  2890.             dispatchClickEvent(divElement);
    
  2891.             expect(onClick).toHaveBeenCalledTimes(1);
    
  2892.             expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  2893.             expect(log[0]).toEqual(['capture', buttonElement]);
    
  2894.             expect(log[1]).toEqual(['bubble', buttonElement]);
    
  2895. 
    
  2896.             document.body.removeChild(portalElement);
    
  2897.           });
    
  2898. 
    
  2899.           describe('Compatibility with Scopes API', () => {
    
  2900.             beforeEach(() => {
    
  2901.               jest.resetModules();
    
  2902.               ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  2903.               ReactFeatureFlags.enableCreateEventHandleAPI = true;
    
  2904.               ReactFeatureFlags.enableScopeAPI = true;
    
  2905. 
    
  2906.               React = require('react');
    
  2907.               ReactDOM = require('react-dom');
    
  2908.               ReactDOMClient = require('react-dom/client');
    
  2909.               Scheduler = require('scheduler');
    
  2910.               ReactDOMServer = require('react-dom/server');
    
  2911.               act = require('internal-test-utils').act;
    
  2912.             });
    
  2913. 
    
  2914.             // @gate www
    
  2915.             it('handle propagation of click events on a scope', async () => {
    
  2916.               const buttonRef = React.createRef();
    
  2917.               const log = [];
    
  2918.               const onClick = jest.fn(e =>
    
  2919.                 log.push(['bubble', e.currentTarget]),
    
  2920.               );
    
  2921.               const onClickCapture = jest.fn(e =>
    
  2922.                 log.push(['capture', e.currentTarget]),
    
  2923.               );
    
  2924.               const TestScope = React.unstable_Scope;
    
  2925.               const setClick = ReactDOM.unstable_createEventHandle('click');
    
  2926.               const setClickCapture = ReactDOM.unstable_createEventHandle(
    
  2927.                 'click',
    
  2928.                 {
    
  2929.                   capture: true,
    
  2930.                 },
    
  2931.               );
    
  2932. 
    
  2933.               function Test() {
    
  2934.                 const scopeRef = React.useRef(null);
    
  2935. 
    
  2936.                 React.useEffect(() => {
    
  2937.                   const clear1 = setClick(scopeRef.current, onClick);
    
  2938.                   const clear2 = setClickCapture(
    
  2939.                     scopeRef.current,
    
  2940.                     onClickCapture,
    
  2941.                   );
    
  2942. 
    
  2943.                   return () => {
    
  2944.                     clear1();
    
  2945.                     clear2();
    
  2946.                   };
    
  2947.                 });
    
  2948. 
    
  2949.                 return (
    
  2950.                   <TestScope ref={scopeRef}>
    
  2951.                     <button ref={buttonRef} />
    
  2952.                   </TestScope>
    
  2953.                 );
    
  2954.               }
    
  2955. 
    
  2956.               await act(() => {
    
  2957.                 ReactDOM.render(<Test />, container);
    
  2958.               });
    
  2959. 
    
  2960.               const buttonElement = buttonRef.current;
    
  2961.               dispatchClickEvent(buttonElement);
    
  2962. 
    
  2963.               expect(onClick).toHaveBeenCalledTimes(1);
    
  2964.               expect(onClickCapture).toHaveBeenCalledTimes(1);
    
  2965.               expect(log).toEqual([
    
  2966.                 ['capture', buttonElement],
    
  2967.                 ['bubble', buttonElement],
    
  2968.               ]);
    
  2969.             });
    
  2970. 
    
  2971.             // @gate www
    
  2972.             it('handle mixed propagation of click events on a scope', async () => {
    
  2973.               const buttonRef = React.createRef();
    
  2974.               const divRef = React.createRef();
    
  2975.               const log = [];
    
  2976.               const onClick = jest.fn(e =>
    
  2977.                 log.push(['bubble', e.currentTarget]),
    
  2978.               );
    
  2979.               const onClickCapture = jest.fn(e =>
    
  2980.                 log.push(['capture', e.currentTarget]),
    
  2981.               );
    
  2982.               const TestScope = React.unstable_Scope;
    
  2983.               const setClick = ReactDOM.unstable_createEventHandle('click');
    
  2984.               const setClickCapture = ReactDOM.unstable_createEventHandle(
    
  2985.                 'click',
    
  2986.                 {
    
  2987.                   capture: true,
    
  2988.                 },
    
  2989.               );
    
  2990. 
    
  2991.               function Test() {
    
  2992.                 const scopeRef = React.useRef(null);
    
  2993. 
    
  2994.                 React.useEffect(() => {
    
  2995.                   const clear1 = setClick(scopeRef.current, onClick);
    
  2996.                   const clear2 = setClickCapture(
    
  2997.                     scopeRef.current,
    
  2998.                     onClickCapture,
    
  2999.                   );
    
  3000.                   const clear3 = setClick(buttonRef.current, onClick);
    
  3001.                   const clear4 = setClickCapture(
    
  3002.                     buttonRef.current,
    
  3003.                     onClickCapture,
    
  3004.                   );
    
  3005. 
    
  3006.                   return () => {
    
  3007.                     clear1();
    
  3008.                     clear2();
    
  3009.                     clear3();
    
  3010.                     clear4();
    
  3011.                   };
    
  3012.                 });
    
  3013. 
    
  3014.                 return (
    
  3015.                   <TestScope ref={scopeRef}>
    
  3016.                     <button ref={buttonRef}>
    
  3017.                       <div
    
  3018.                         ref={divRef}
    
  3019.                         onClick={onClick}
    
  3020.                         onClickCapture={onClickCapture}>
    
  3021.                         Click me!
    
  3022.                       </div>
    
  3023.                     </button>
    
  3024.                   </TestScope>
    
  3025.                 );
    
  3026.               }
    
  3027. 
    
  3028.               await act(() => {
    
  3029.                 ReactDOM.render(<Test />, container);
    
  3030.               });
    
  3031. 
    
  3032.               const buttonElement = buttonRef.current;
    
  3033.               dispatchClickEvent(buttonElement);
    
  3034. 
    
  3035.               expect(onClick).toHaveBeenCalledTimes(2);
    
  3036.               expect(onClickCapture).toHaveBeenCalledTimes(2);
    
  3037.               expect(log).toEqual([
    
  3038.                 ['capture', buttonElement],
    
  3039.                 ['capture', buttonElement],
    
  3040.                 ['bubble', buttonElement],
    
  3041.                 ['bubble', buttonElement],
    
  3042.               ]);
    
  3043. 
    
  3044.               log.length = 0;
    
  3045.               onClick.mockClear();
    
  3046.               onClickCapture.mockClear();
    
  3047. 
    
  3048.               const divElement = divRef.current;
    
  3049.               dispatchClickEvent(divElement);
    
  3050. 
    
  3051.               expect(onClick).toHaveBeenCalledTimes(3);
    
  3052.               expect(onClickCapture).toHaveBeenCalledTimes(3);
    
  3053.               expect(log).toEqual([
    
  3054.                 ['capture', buttonElement],
    
  3055.                 ['capture', buttonElement],
    
  3056.                 ['capture', divElement],
    
  3057.                 ['bubble', divElement],
    
  3058.                 ['bubble', buttonElement],
    
  3059.                 ['bubble', buttonElement],
    
  3060.               ]);
    
  3061.             });
    
  3062. 
    
  3063.             // @gate www
    
  3064.             it('should not handle the target being a dangling text node within a scope', async () => {
    
  3065.               const clickEvent = jest.fn();
    
  3066.               const buttonRef = React.createRef();
    
  3067.               const TestScope = React.unstable_Scope;
    
  3068.               const setClick = ReactDOM.unstable_createEventHandle('click');
    
  3069. 
    
  3070.               function Test() {
    
  3071.                 const scopeRef = React.useRef(null);
    
  3072. 
    
  3073.                 React.useEffect(() => {
    
  3074.                   return setClick(scopeRef.current, clickEvent);
    
  3075.                 });
    
  3076. 
    
  3077.                 return (
    
  3078.                   <button ref={buttonRef}>
    
  3079.                     <TestScope ref={scopeRef}>Click me!</TestScope>
    
  3080.                   </button>
    
  3081.                 );
    
  3082.               }
    
  3083. 
    
  3084.               await act(() => {
    
  3085.                 ReactDOM.render(<Test />, container);
    
  3086.               });
    
  3087. 
    
  3088.               const textNode = buttonRef.current.firstChild;
    
  3089.               dispatchClickEvent(textNode);
    
  3090.               // This should not work, as the target instance will be the
    
  3091.               // <button>, which is actually outside the scope.
    
  3092.               expect(clickEvent).toBeCalledTimes(0);
    
  3093.             });
    
  3094. 
    
  3095.             // @gate www
    
  3096.             it('handle stopPropagation (inner) correctly between scopes', async () => {
    
  3097.               const buttonRef = React.createRef();
    
  3098.               const outerOnClick = jest.fn();
    
  3099.               const innerOnClick = jest.fn(e => e.stopPropagation());
    
  3100.               const TestScope = React.unstable_Scope;
    
  3101.               const TestScope2 = React.unstable_Scope;
    
  3102.               const setClick = ReactDOM.unstable_createEventHandle('click');
    
  3103. 
    
  3104.               function Test() {
    
  3105.                 const scopeRef = React.useRef(null);
    
  3106.                 const scope2Ref = React.useRef(null);
    
  3107. 
    
  3108.                 React.useEffect(() => {
    
  3109.                   const clear1 = setClick(scopeRef.current, outerOnClick);
    
  3110.                   const clear2 = setClick(scope2Ref.current, innerOnClick);
    
  3111. 
    
  3112.                   return () => {
    
  3113.                     clear1();
    
  3114.                     clear2();
    
  3115.                   };
    
  3116.                 });
    
  3117. 
    
  3118.                 return (
    
  3119.                   <TestScope ref={scopeRef}>
    
  3120.                     <TestScope2 ref={scope2Ref}>
    
  3121.                       <button ref={buttonRef} />
    
  3122.                     </TestScope2>
    
  3123.                   </TestScope>
    
  3124.                 );
    
  3125.               }
    
  3126. 
    
  3127.               await act(() => {
    
  3128.                 ReactDOM.render(<Test />, container);
    
  3129.               });
    
  3130. 
    
  3131.               const buttonElement = buttonRef.current;
    
  3132.               dispatchClickEvent(buttonElement);
    
  3133. 
    
  3134.               expect(innerOnClick).toHaveBeenCalledTimes(1);
    
  3135.               expect(outerOnClick).toHaveBeenCalledTimes(0);
    
  3136.             });
    
  3137. 
    
  3138.             // @gate www
    
  3139.             it('handle stopPropagation (outer) correctly between scopes', async () => {
    
  3140.               const buttonRef = React.createRef();
    
  3141.               const outerOnClick = jest.fn(e => e.stopPropagation());
    
  3142.               const innerOnClick = jest.fn();
    
  3143.               const TestScope = React.unstable_Scope;
    
  3144.               const TestScope2 = React.unstable_Scope;
    
  3145.               const setClick = ReactDOM.unstable_createEventHandle('click');
    
  3146. 
    
  3147.               function Test() {
    
  3148.                 const scopeRef = React.useRef(null);
    
  3149.                 const scope2Ref = React.useRef(null);
    
  3150. 
    
  3151.                 React.useEffect(() => {
    
  3152.                   const clear1 = setClick(scopeRef.current, outerOnClick);
    
  3153.                   const clear2 = setClick(scope2Ref.current, innerOnClick);
    
  3154. 
    
  3155.                   return () => {
    
  3156.                     clear1();
    
  3157.                     clear2();
    
  3158.                   };
    
  3159.                 });
    
  3160. 
    
  3161.                 return (
    
  3162.                   <TestScope ref={scopeRef}>
    
  3163.                     <TestScope2 ref={scope2Ref}>
    
  3164.                       <button ref={buttonRef} />
    
  3165.                     </TestScope2>
    
  3166.                   </TestScope>
    
  3167.                 );
    
  3168.               }
    
  3169. 
    
  3170.               await act(() => {
    
  3171.                 ReactDOM.render(<Test />, container);
    
  3172.               });
    
  3173. 
    
  3174.               const buttonElement = buttonRef.current;
    
  3175.               dispatchClickEvent(buttonElement);
    
  3176. 
    
  3177.               expect(innerOnClick).toHaveBeenCalledTimes(1);
    
  3178.               expect(outerOnClick).toHaveBeenCalledTimes(1);
    
  3179.             });
    
  3180. 
    
  3181.             // @gate www
    
  3182.             it('handle stopPropagation (inner and outer) correctly between scopes', async () => {
    
  3183.               const buttonRef = React.createRef();
    
  3184.               const onClick = jest.fn(e => e.stopPropagation());
    
  3185.               const TestScope = React.unstable_Scope;
    
  3186.               const TestScope2 = React.unstable_Scope;
    
  3187.               const setClick = ReactDOM.unstable_createEventHandle('click');
    
  3188. 
    
  3189.               function Test() {
    
  3190.                 const scopeRef = React.useRef(null);
    
  3191.                 const scope2Ref = React.useRef(null);
    
  3192. 
    
  3193.                 React.useEffect(() => {
    
  3194.                   const clear1 = setClick(scopeRef.current, onClick);
    
  3195.                   const clear2 = setClick(scope2Ref.current, onClick);
    
  3196. 
    
  3197.                   return () => {
    
  3198.                     clear1();
    
  3199.                     clear2();
    
  3200.                   };
    
  3201.                 });
    
  3202. 
    
  3203.                 return (
    
  3204.                   <TestScope ref={scopeRef}>
    
  3205.                     <TestScope2 ref={scope2Ref}>
    
  3206.                       <button ref={buttonRef} />
    
  3207.                     </TestScope2>
    
  3208.                   </TestScope>
    
  3209.                 );
    
  3210.               }
    
  3211. 
    
  3212.               await act(() => {
    
  3213.                 ReactDOM.render(<Test />, container);
    
  3214.               });
    
  3215. 
    
  3216.               const buttonElement = buttonRef.current;
    
  3217.               dispatchClickEvent(buttonElement);
    
  3218. 
    
  3219.               expect(onClick).toHaveBeenCalledTimes(1);
    
  3220.             });
    
  3221. 
    
  3222.             // @gate www
    
  3223.             it('should be able to register handlers for events affected by the intervention', async () => {
    
  3224.               const rootContainer = document.createElement('div');
    
  3225.               container.appendChild(rootContainer);
    
  3226. 
    
  3227.               const allEvents = [];
    
  3228.               const defaultPreventedEvents = [];
    
  3229.               const handler = e => {
    
  3230.                 allEvents.push(e.type);
    
  3231.                 if (e.defaultPrevented) defaultPreventedEvents.push(e.type);
    
  3232.               };
    
  3233. 
    
  3234.               container.addEventListener('touchstart', handler);
    
  3235.               container.addEventListener('touchmove', handler);
    
  3236.               container.addEventListener('wheel', handler);
    
  3237. 
    
  3238.               const ref = React.createRef();
    
  3239.               const setTouchStart =
    
  3240.                 ReactDOM.unstable_createEventHandle('touchstart');
    
  3241.               const setTouchMove =
    
  3242.                 ReactDOM.unstable_createEventHandle('touchmove');
    
  3243.               const setWheel = ReactDOM.unstable_createEventHandle('wheel');
    
  3244. 
    
  3245.               function Component() {
    
  3246.                 React.useEffect(() => {
    
  3247.                   const clearTouchStart = setTouchStart(ref.current, e =>
    
  3248.                     e.preventDefault(),
    
  3249.                   );
    
  3250.                   const clearTouchMove = setTouchMove(ref.current, e =>
    
  3251.                     e.preventDefault(),
    
  3252.                   );
    
  3253.                   const clearWheel = setWheel(ref.current, e =>
    
  3254.                     e.preventDefault(),
    
  3255.                   );
    
  3256.                   return () => {
    
  3257.                     clearTouchStart();
    
  3258.                     clearTouchMove();
    
  3259.                     clearWheel();
    
  3260.                   };
    
  3261.                 });
    
  3262.                 return <div ref={ref}>test</div>;
    
  3263.               }
    
  3264. 
    
  3265.               await act(() => {
    
  3266.                 ReactDOM.render(<Component />, rootContainer);
    
  3267.               });
    
  3268. 
    
  3269.               dispatchEvent(ref.current, 'touchstart');
    
  3270.               dispatchEvent(ref.current, 'touchmove');
    
  3271.               dispatchEvent(ref.current, 'wheel');
    
  3272. 
    
  3273.               expect(allEvents).toEqual(['touchstart', 'touchmove', 'wheel']);
    
  3274.               // These events are passive by default, so we can't preventDefault.
    
  3275.               expect(defaultPreventedEvents).toEqual([]);
    
  3276.             });
    
  3277.           });
    
  3278.         });
    
  3279.       },
    
  3280.     );
    
  3281.   }
    
  3282. 
    
  3283.   withEnableLegacyFBSupport(false);
    
  3284.   withEnableLegacyFBSupport(true);
    
  3285. });