From 5f2afe009e643b8c25b40b6740218d37f0211e27 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Mon, 4 Mar 2024 08:19:17 -0800 Subject: [PATCH] [DOM] disable legacy mode behind flag (#28468) Adds a flag to disable legacy mode. Currently this flag is used to cause legacy mode apis like render and hydrate to throw. This change also removes render, hydrate, unmountComponentAtNode, and unstable_renderSubtreeIntoContainer from the experiemntal entrypoint. Right now for Meta builds this flag is off (legacy mode is still supported). In OSS builds this flag matches __NEXT_MAJOR__ which means it currently is on in experiemental. This means that after merging legacy mode is effectively removed from experimental builds. While this is a breaking change, experimental builds are not stable and users can pin to older versions or update their use of react-dom to no longer use legacy mode APIs. --- .../src/__tests__/store-test.js | 5 + .../src/__tests__/storeStressSync-test.js | 1 + packages/react-dom/index.experimental.js | 4 - .../src/__tests__/ReactComponent-test.js | 2 + .../ReactCompositeComponentState-test.js | 1 + .../react-dom/src/__tests__/ReactDOM-test.js | 2 + .../src/__tests__/ReactDOMComponent-test.js | 22 +-- ...eactDOMConsoleErrorReportingLegacy-test.js | 7 + .../src/__tests__/ReactDOMFiberAsync-test.js | 1 + .../src/__tests__/ReactDOMHooks-test.js | 45 +++++++ .../src/__tests__/ReactDOMInput-test.js | 24 ++-- ...actDOMLegacyComponentTree-test.internal.js | 2 + .../src/__tests__/ReactDOMLegacyFiber-test.js | 42 ++++++ .../src/__tests__/ReactDOMOption-test.js | 125 +++++++++--------- .../src/__tests__/ReactDOMSelect-test.js | 1 + ...ctDOMServerIntegrationReconnecting-test.js | 4 + ...DOMServerPartialHydration-test.internal.js | 1 + .../ReactDOMSingletonComponents-test.js | 3 + .../ReactDOMSuspensePlaceholder-test.js | 3 + .../ReactLegacyCompositeComponent-test.js | 9 ++ ...eactLegacyErrorBoundaries-test.internal.js | 42 ++++++ .../src/__tests__/ReactLegacyMount-test.js | 23 ++++ .../__tests__/ReactLegacyRootWarnings-test.js | 2 + .../src/__tests__/ReactLegacyUpdates-test.js | 39 ++++++ .../__tests__/ReactMountDestruction-test.js | 2 + .../src/__tests__/ReactRenderDocument-test.js | 1 + .../ReactServerRenderingHydration-test.js | 3 + .../src/__tests__/findDOMNode-test.js | 6 + .../src/__tests__/refsLegacy-test.js | 1 + .../renderSubtreeIntoContainer-test.js | 46 +------ .../react-dom/src/client/ReactDOMLegacy.js | 25 ++++ .../__tests__/ChangeEventPlugin-test.js | 114 +++++++++------- .../__tests__/EnterLeaveEventPlugin-test.js | 20 +-- .../unstable_testing.experimental.js | 4 - .../__tests__/ReactSuspense-test.internal.js | 17 +++ .../ReactSuspenseEffectsSemanticsDOM-test.js | 1 + .../__tests__/ReactUpdaters-test.internal.js | 3 + .../src/__tests__/ReactStrictMode-test.js | 2 + packages/shared/ReactFeatureFlags.js | 5 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.native.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.www.js | 2 + scripts/error-codes/codes.json | 3 +- 46 files changed, 483 insertions(+), 187 deletions(-) diff --git a/packages/react-devtools-shared/src/__tests__/store-test.js b/packages/react-devtools-shared/src/__tests__/store-test.js index a28641ae3594d..417158ce3a4d2 100644 --- a/packages/react-devtools-shared/src/__tests__/store-test.js +++ b/packages/react-devtools-shared/src/__tests__/store-test.js @@ -227,6 +227,7 @@ describe('Store', () => { // @reactVersion >= 18.0 // @reactVersion < 19 + // @gate !disableLegacyMode it('should support mount and update operations for multiple roots (legacy render)', () => { const Parent = ({count}) => new Array(count).fill(true).map((_, index) => ); @@ -941,6 +942,7 @@ describe('Store', () => { // @reactVersion >= 18.0 // @reactVersion < 19 + // @gate !disableLegacyMode it('should support mount and update operations for multiple roots (legacy render)', () => { const Parent = ({count}) => new Array(count).fill(true).map((_, index) => ); @@ -1469,6 +1471,7 @@ describe('Store', () => { // @reactVersion >= 18.0 // @reactVersion < 19 + // @gate !disableLegacyMode it('detects and updates profiling support based on the attached roots (legacy render)', () => { const Component = () => null; @@ -1632,6 +1635,7 @@ describe('Store', () => { // @reactVersion >= 18.0 // @reactVersion < 19 + // @gate !disableLegacyMode it('should support Lazy components (legacy render)', async () => { const container = document.createElement('div'); @@ -1702,6 +1706,7 @@ describe('Store', () => { // @reactVersion >= 18.0 // @reactVersion < 19 + // @gate !disableLegacyMode it('should support Lazy components that are unmounted before they finish loading (legacy render)', async () => { const container = document.createElement('div'); diff --git a/packages/react-devtools-shared/src/__tests__/storeStressSync-test.js b/packages/react-devtools-shared/src/__tests__/storeStressSync-test.js index bf444f169249a..585499fd81a65 100644 --- a/packages/react-devtools-shared/src/__tests__/storeStressSync-test.js +++ b/packages/react-devtools-shared/src/__tests__/storeStressSync-test.js @@ -36,6 +36,7 @@ describe('StoreStress (Legacy Mode)', () => { // It renders different trees that should produce the same output. // @reactVersion >= 16.9 // @reactVersion < 19 + // @gate !disableLegacyMode it('should handle a stress test with different tree operations (Legacy Mode)', () => { let setShowX; const A = () => 'a'; diff --git a/packages/react-dom/index.experimental.js b/packages/react-dom/index.experimental.js index 012eb6866e8a4..7a6e52121f75b 100644 --- a/packages/react-dom/index.experimental.js +++ b/packages/react-dom/index.experimental.js @@ -14,11 +14,7 @@ export { hydrateRoot, findDOMNode, flushSync, - hydrate, - render, - unmountComponentAtNode, unstable_batchedUpdates, - unstable_renderSubtreeIntoContainer, unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority. useFormStatus, useFormState, diff --git a/packages/react-dom/src/__tests__/ReactComponent-test.js b/packages/react-dom/src/__tests__/ReactComponent-test.js index 44c86a7e4d501..1a77cbad680af 100644 --- a/packages/react-dom/src/__tests__/ReactComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactComponent-test.js @@ -26,6 +26,7 @@ describe('ReactComponent', () => { act = require('internal-test-utils').act; }); + // @gate !disableLegacyMode it('should throw on invalid render targets in legacy roots', () => { const container = document.createElement('div'); // jQuery objects are basically arrays; people often pass them in by mistake @@ -455,6 +456,7 @@ describe('ReactComponent', () => { /* eslint-enable indent */ }); + // @gate !disableLegacyMode it('fires the callback after a component is rendered in legacy roots', () => { const callback = jest.fn(); const container = document.createElement('div'); diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js index a2894b2ca7b27..a1d3d28533fe9 100644 --- a/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js +++ b/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js @@ -630,6 +630,7 @@ describe('ReactCompositeComponent-state', () => { ); }); + // @gate !disableLegacyMode it('Legacy mode should support setState in componentWillUnmount (#18851)', () => { let subscription; class A extends React.Component { diff --git a/packages/react-dom/src/__tests__/ReactDOM-test.js b/packages/react-dom/src/__tests__/ReactDOM-test.js index a6bd2b80f3f7b..83e3e0229d7b1 100644 --- a/packages/react-dom/src/__tests__/ReactDOM-test.js +++ b/packages/react-dom/src/__tests__/ReactDOM-test.js @@ -164,6 +164,7 @@ describe('ReactDOM', () => { expect(dog.className).toBe('bigdog'); }); + // @gate !disableLegacyMode it('throws in render() if the mount callback in legacy roots is not a function', async () => { function Foo() { this.a = 1; @@ -216,6 +217,7 @@ describe('ReactDOM', () => { ); }); + // @gate !disableLegacyMode it('throws in render() if the update callback in legacy roots is not a function', async () => { function Foo() { this.a = 1; diff --git a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js index 5d7854e83811e..5c62605af2bc4 100644 --- a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js @@ -332,7 +332,7 @@ describe('ReactDOMComponent', () => { }); }); - it('throws with Temporal-like objects as style values', () => { + it('throws with Temporal-like objects as style values', async () => { class TemporalLike { valueOf() { // Throwing here is the behavior of ECMAScript "Temporal" date/time API. @@ -344,14 +344,17 @@ describe('ReactDOMComponent', () => { } } const style = {fontSize: new TemporalLike()}; - const div = document.createElement('div'); - const test = () => ReactDOM.render(, div); - expect(() => - expect(test).toThrowError(new TypeError('prod message')), - ).toErrorDev( - 'Warning: The provided `fontSize` CSS property is an unsupported type TemporalLike.' + - ' This value must be coerced to a string before using it here.', - ); + const root = ReactDOMClient.createRoot(document.createElement('div')); + await expect(async () => { + await expect(async () => { + await act(() => { + root.render(); + }); + }).toErrorDev( + 'Warning: The provided `fontSize` CSS property is an unsupported type TemporalLike.' + + ' This value must be coerced to a string before using it here.', + ); + }).rejects.toThrowError(new TypeError('prod message')); }); it('should update styles if initially null', async () => { @@ -3688,6 +3691,7 @@ describe('ReactDOMComponent', () => { expect(typeof portalContainer.onclick).toBe('function'); }); + // @gate !disableLegacyMode it('does not add onclick handler to the React root in legacy mode', () => { const container = document.createElement('div'); diff --git a/packages/react-dom/src/__tests__/ReactDOMConsoleErrorReportingLegacy-test.js b/packages/react-dom/src/__tests__/ReactDOMConsoleErrorReportingLegacy-test.js index bb1e9c83ecf65..7514a7dab9247 100644 --- a/packages/react-dom/src/__tests__/ReactDOMConsoleErrorReportingLegacy-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMConsoleErrorReportingLegacy-test.js @@ -55,6 +55,7 @@ describe('ReactDOMConsoleErrorReporting', () => { }); describe('ReactDOM.render', () => { + // @gate !disableLegacyMode it('logs errors during event handlers', async () => { spyOnDevAndProd(console, 'error'); @@ -156,6 +157,7 @@ describe('ReactDOMConsoleErrorReporting', () => { } }); + // @gate !disableLegacyMode it('logs render errors without an error boundary', async () => { spyOnDevAndProd(console, 'error'); @@ -223,6 +225,7 @@ describe('ReactDOMConsoleErrorReporting', () => { } }); + // @gate !disableLegacyMode it('logs render errors with an error boundary', async () => { spyOnDevAndProd(console, 'error'); @@ -295,6 +298,7 @@ describe('ReactDOMConsoleErrorReporting', () => { } }); + // @gate !disableLegacyMode it('logs layout effect errors without an error boundary', async () => { spyOnDevAndProd(console, 'error'); @@ -365,6 +369,7 @@ describe('ReactDOMConsoleErrorReporting', () => { } }); + // @gate !disableLegacyMode it('logs layout effect errors with an error boundary', async () => { spyOnDevAndProd(console, 'error'); @@ -440,6 +445,7 @@ describe('ReactDOMConsoleErrorReporting', () => { } }); + // @gate !disableLegacyMode it('logs passive effect errors without an error boundary', async () => { spyOnDevAndProd(console, 'error'); @@ -511,6 +517,7 @@ describe('ReactDOMConsoleErrorReporting', () => { } }); + // @gate !disableLegacyMode it('logs passive effect errors with an error boundary', async () => { spyOnDevAndProd(console, 'error'); diff --git a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js index 43bc0d8b34382..cf47e867da7f7 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js @@ -50,6 +50,7 @@ describe('ReactDOMFiberAsync', () => { document.body.removeChild(container); }); + // @gate !disableLegacyMode it('renders synchronously by default in legacy mode', () => { const ops = []; ReactDOM.render(
Hi
, container, () => { diff --git a/packages/react-dom/src/__tests__/ReactDOMHooks-test.js b/packages/react-dom/src/__tests__/ReactDOMHooks-test.js index ad8fadad1ff97..5e88c60312a08 100644 --- a/packages/react-dom/src/__tests__/ReactDOMHooks-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMHooks-test.js @@ -35,6 +35,7 @@ describe('ReactDOMHooks', () => { document.body.removeChild(container); }); + // @gate !disableLegacyMode it('can ReactDOM.render() from useEffect', async () => { const container2 = document.createElement('div'); const container3 = document.createElement('div'); @@ -76,6 +77,50 @@ describe('ReactDOMHooks', () => { expect(container3.textContent).toBe('6'); }); + it('can render() from useEffect', async () => { + const container2 = document.createElement('div'); + const container3 = document.createElement('div'); + + const root1 = ReactDOMClient.createRoot(container); + const root2 = ReactDOMClient.createRoot(container2); + const root3 = ReactDOMClient.createRoot(container3); + + function Example1({n}) { + React.useEffect(() => { + root2.render(); + }); + return 1 * n; + } + + function Example2({n}) { + React.useEffect(() => { + root3.render(); + }); + return 2 * n; + } + + function Example3({n}) { + return 3 * n; + } + + await act(() => { + root1.render(); + }); + await waitForAll([]); + expect(container.textContent).toBe('1'); + expect(container2.textContent).toBe('2'); + expect(container3.textContent).toBe('3'); + + await act(() => { + root1.render(); + }); + await waitForAll([]); + expect(container.textContent).toBe('2'); + expect(container2.textContent).toBe('4'); + expect(container3.textContent).toBe('6'); + }); + + // @gate !disableLegacyMode it('should not bail out when an update is scheduled from within an event handler', () => { const {createRef, useCallback, useState} = React; diff --git a/packages/react-dom/src/__tests__/ReactDOMInput-test.js b/packages/react-dom/src/__tests__/ReactDOMInput-test.js index 367e4a5763c3e..f817ede5214ca 100644 --- a/packages/react-dom/src/__tests__/ReactDOMInput-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMInput-test.js @@ -733,7 +733,7 @@ describe('ReactDOMInput', () => { expect(node.value).toBe('foobar'); }); - it('should throw for date inputs if `defaultValue` is an object where valueOf() throws', () => { + it('should throw for date inputs if `defaultValue` is an object where valueOf() throws', async () => { class TemporalLike { valueOf() { // Throwing here is the behavior of ECMAScript "Temporal" date/time API. @@ -744,19 +744,16 @@ describe('ReactDOMInput', () => { return '2020-01-01'; } } - const legacyContainer = document.createElement('div'); - document.body.appendChild(legacyContainer); - const test = () => - ReactDOM.render( - , - legacyContainer, + await expect(async () => { + await expect(async () => { + await act(() => { + root.render(); + }); + }).toErrorDev( + 'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' + + 'strings, not TemporalLike. This value must be coerced to a string before using it here.', ); - expect(() => - expect(test).toThrowError(new TypeError('prod message')), - ).toErrorDev( - 'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' + - 'strings, not TemporalLike. This value must be coerced to a string before using it here.', - ); + }).rejects.toThrowError(new TypeError('prod message')); }); it('should throw for text inputs if `defaultValue` is an object where valueOf() throws', async () => { @@ -1736,6 +1733,7 @@ describe('ReactDOMInput', () => { assertInputTrackingIsCurrent(container); }); + // @gate !disableLegacyMode it('should control radio buttons if the tree updates during render in legacy mode', async () => { container.remove(); container = document.createElement('div'); diff --git a/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js index 6b43aab0ee451..8465e68703961 100644 --- a/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js @@ -27,6 +27,7 @@ describe('ReactDOMComponentTree', () => { container = null; }); + // @gate !disableLegacyMode it('finds instance of node that is attempted to be unmounted', () => { const component =
; const node = ReactDOM.render(
{component}
, container); @@ -39,6 +40,7 @@ describe('ReactDOMComponentTree', () => { ); }); + // @gate !disableLegacyMode it('finds instance from node to stop rendering over other react rendered components', () => { const component = (
diff --git a/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js b/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js index 3db740e2fbbd1..c2dbc38580e3f 100644 --- a/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js @@ -27,6 +27,7 @@ describe('ReactDOMLegacyFiber', () => { jest.restoreAllMocks(); }); + // @gate !disableLegacyMode it('should render strings as children', () => { const Box = ({value}) =>
{value}
; @@ -34,6 +35,7 @@ describe('ReactDOMLegacyFiber', () => { expect(container.textContent).toEqual('foo'); }); + // @gate !disableLegacyMode it('should render numbers as children', () => { const Box = ({value}) =>
{value}
; @@ -42,6 +44,7 @@ describe('ReactDOMLegacyFiber', () => { expect(container.textContent).toEqual('10'); }); + // @gate !disableLegacyMode it('should be called a callback argument', () => { // mounting phase let called = false; @@ -54,6 +57,7 @@ describe('ReactDOMLegacyFiber', () => { expect(called).toEqual(true); }); + // @gate !disableLegacyMode it('should call a callback argument when the same element is re-rendered', () => { class Foo extends React.Component { render() { @@ -75,6 +79,7 @@ describe('ReactDOMLegacyFiber', () => { expect(called).toEqual(true); }); + // @gate !disableLegacyMode it('should render a component returning strings directly from render', () => { const Text = ({value}) => value; @@ -82,6 +87,7 @@ describe('ReactDOMLegacyFiber', () => { expect(container.textContent).toEqual('foo'); }); + // @gate !disableLegacyMode it('should render a component returning numbers directly from render', () => { const Text = ({value}) => value; @@ -90,6 +96,7 @@ describe('ReactDOMLegacyFiber', () => { expect(container.textContent).toEqual('10'); }); + // @gate !disableLegacyMode it('finds the DOM Text node of a string child', () => { class Text extends React.Component { render() { @@ -109,6 +116,7 @@ describe('ReactDOMLegacyFiber', () => { expect(textNode.nodeValue).toBe('foo'); }); + // @gate !disableLegacyMode it('finds the first child when a component returns a fragment', () => { class Fragment extends React.Component { render() { @@ -126,6 +134,7 @@ describe('ReactDOMLegacyFiber', () => { expect(firstNode.tagName).toBe('DIV'); }); + // @gate !disableLegacyMode it('finds the first child even when fragment is nested', () => { class Wrapper extends React.Component { render() { @@ -154,6 +163,7 @@ describe('ReactDOMLegacyFiber', () => { expect(firstNode.tagName).toBe('DIV'); }); + // @gate !disableLegacyMode it('finds the first child even when first child renders null', () => { class NullComponent extends React.Component { render() { @@ -177,6 +187,7 @@ describe('ReactDOMLegacyFiber', () => { expect(firstNode.tagName).toBe('DIV'); }); + // @gate !disableLegacyMode it('renders an empty fragment', () => { const Div = () =>
; const EmptyFragment = () => <>; @@ -232,6 +243,7 @@ describe('ReactDOMLegacyFiber', () => { expect(testContainer.innerHTML).toBe(''); }; + // @gate !disableLegacyMode it('should render one portal', () => { const portalContainer = document.createElement('div'); @@ -247,6 +259,7 @@ describe('ReactDOMLegacyFiber', () => { expect(container.innerHTML).toBe(''); }); + // @gate !disableLegacyMode it('should render many portals', () => { const portalContainer1 = document.createElement('div'); const portalContainer2 = document.createElement('div'); @@ -347,6 +360,7 @@ describe('ReactDOMLegacyFiber', () => { ]); }); + // @gate !disableLegacyMode it('should render nested portals', () => { const portalContainer1 = document.createElement('div'); const portalContainer2 = document.createElement('div'); @@ -390,6 +404,7 @@ describe('ReactDOMLegacyFiber', () => { expect(container.innerHTML).toBe(''); }); + // @gate !disableLegacyMode it('should reconcile portal children', () => { const portalContainer = document.createElement('div'); @@ -436,6 +451,7 @@ describe('ReactDOMLegacyFiber', () => { expect(container.innerHTML).toBe('
'); }); + // @gate !disableLegacyMode it('should unmount empty portal component wherever it appears', () => { const portalContainer = document.createElement('div'); @@ -470,6 +486,7 @@ describe('ReactDOMLegacyFiber', () => { expect(container.innerHTML).toBe('
parent
'); }); + // @gate !disableLegacyMode it('should keep track of namespace across portals (simple)', () => { assertNamespacesMatch( @@ -498,6 +515,7 @@ describe('ReactDOMLegacyFiber', () => { ); }); + // @gate !disableLegacyMode it('should keep track of namespace across portals (medium)', () => { assertNamespacesMatch( @@ -562,6 +580,7 @@ describe('ReactDOMLegacyFiber', () => { ); }); + // @gate !disableLegacyMode it('should keep track of namespace across portals (complex)', () => { assertNamespacesMatch(
@@ -636,6 +655,7 @@ describe('ReactDOMLegacyFiber', () => { ); }); + // @gate !disableLegacyMode it('should unwind namespaces on uncaught errors', () => { function BrokenRender() { throw new Error('Hello'); @@ -651,6 +671,7 @@ describe('ReactDOMLegacyFiber', () => { assertNamespacesMatch(
); }); + // @gate !disableLegacyMode it('should unwind namespaces on caught errors', () => { function BrokenRender() { throw new Error('Hello'); @@ -684,6 +705,7 @@ describe('ReactDOMLegacyFiber', () => { assertNamespacesMatch(
); }); + // @gate !disableLegacyMode it('should unwind namespaces on caught errors in a portal', () => { function BrokenRender() { throw new Error('Hello'); @@ -719,6 +741,7 @@ describe('ReactDOMLegacyFiber', () => { }); // @gate !disableLegacyContext + // @gate !disableLegacyMode it('should pass portal context when rendering subtree elsewhere', () => { const portalContainer = document.createElement('div'); @@ -754,6 +777,7 @@ describe('ReactDOMLegacyFiber', () => { }); // @gate !disableLegacyContext + // @gate !disableLegacyMode it('should update portal context if it changes due to setState', () => { const portalContainer = document.createElement('div'); @@ -799,6 +823,7 @@ describe('ReactDOMLegacyFiber', () => { }); // @gate !disableLegacyContext + // @gate !disableLegacyMode it('should update portal context if it changes due to re-render', () => { const portalContainer = document.createElement('div'); @@ -839,6 +864,7 @@ describe('ReactDOMLegacyFiber', () => { expect(container.innerHTML).toBe(''); }); + // @gate !disableLegacyMode it('findDOMNode should find dom element after expanding a fragment', () => { class MyNode extends React.Component { render() { @@ -859,6 +885,7 @@ describe('ReactDOMLegacyFiber', () => { expect(b.tagName).toBe('SPAN'); }); + // @gate !disableLegacyMode it('should bubble events from the portal to the parent', () => { const portalContainer = document.createElement('div'); document.body.appendChild(portalContainer); @@ -890,6 +917,7 @@ describe('ReactDOMLegacyFiber', () => { } }); + // @gate !disableLegacyMode it('should not onMouseLeave when staying in the portal', () => { const portalContainer = document.createElement('div'); document.body.appendChild(portalContainer); @@ -966,6 +994,7 @@ describe('ReactDOMLegacyFiber', () => { }); // Regression test for https://github.com/facebook/react/issues/19562 + // @gate !disableLegacyMode it('does not fire mouseEnter twice when relatedTarget is the root node', () => { let ops = []; let target = null; @@ -1016,6 +1045,7 @@ describe('ReactDOMLegacyFiber', () => { expect(ops).toEqual([]); }); + // @gate !disableLegacyMode it('listens to events that do not exist in the Portal subtree', () => { const onClick = jest.fn(); @@ -1043,6 +1073,7 @@ describe('ReactDOMLegacyFiber', () => { }).toThrow('Target container is not a DOM element.'); }); + // @gate !disableLegacyMode it('should warn for non-functional event listeners', () => { class Example extends React.Component { render() { @@ -1056,6 +1087,7 @@ describe('ReactDOMLegacyFiber', () => { ); }); + // @gate !disableLegacyMode it('should warn with a special message for `false` event listeners', () => { class Example extends React.Component { render() { @@ -1071,6 +1103,7 @@ describe('ReactDOMLegacyFiber', () => { ); }); + // @gate !disableLegacyMode it('should not update event handlers until commit', () => { spyOnDev(console, 'error'); @@ -1168,6 +1201,7 @@ describe('ReactDOMLegacyFiber', () => { } }); + // @gate !disableLegacyMode it('should not crash encountering low-priority tree', () => { ReactDOM.render(