Skip to content

Commit 93eeb04

Browse files
author
Brian Vaughn
committed
Treat portals as fragments in test renderer
1 parent c0d103c commit 93eeb04

14 files changed

+45
-90
lines changed

packages/react-reconciler/src/ReactFiber.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ import type {ExpirationTime} from './ReactFiberExpirationTime';
1616
import type {UpdateQueue} from './ReactUpdateQueue';
1717

1818
import invariant from 'fbjs/lib/invariant';
19-
import {enableProfilerTimer} from 'shared/ReactFeatureFlags';
19+
import {
20+
convertPortalsToFragments,
21+
enableProfilerTimer,
22+
} from 'shared/ReactFeatureFlags';
2023
import {NoEffect} from 'shared/ReactTypeOfSideEffect';
2124
import {
2225
IndeterminateComponent,
@@ -513,7 +516,8 @@ export function createFiberFromPortal(
513516
expirationTime: ExpirationTime,
514517
): Fiber {
515518
const pendingProps = portal.children !== null ? portal.children : [];
516-
const fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
519+
const tag = convertPortalsToFragments ? Fragment : HostPortal;
520+
const fiber = createFiber(tag, pendingProps, portal.key, mode);
517521
fiber.expirationTime = expirationTime;
518522
fiber.stateNode = {
519523
containerInfo: portal.containerInfo,

packages/react-test-renderer/src/ReactTestHostConfig.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ export function appendChild(
5757
parentInstance: Instance | Container,
5858
child: Instance | TextInstance,
5959
): void {
60-
// Detect and ignore ReactDOM.createPortal() usage.
61-
// Test renderer's toJSON() method knows how to handle this case.
62-
if (parentInstance instanceof HTMLElement) {
63-
return;
64-
}
6560
const index = parentInstance.children.indexOf(child);
6661
if (index !== -1) {
6762
parentInstance.children.splice(index, 1);
@@ -86,11 +81,6 @@ export function removeChild(
8681
parentInstance: Instance | Container,
8782
child: Instance | TextInstance,
8883
): void {
89-
// Detect and ignore ReactDOM.createPortal() usage.
90-
// Test renderer's toJSON() method knows how to handle this case.
91-
if (parentInstance instanceof HTMLElement) {
92-
return;
93-
}
9484
const index = parentInstance.children.indexOf(child);
9585
parentInstance.children.splice(index, 1);
9686
}

packages/react-test-renderer/src/ReactTestRenderer.js

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
1111
import type {FiberRoot} from 'react-reconciler/src/ReactFiberRoot';
1212
import type {Instance, TextInstance} from './ReactTestHostConfig';
1313

14-
import React from 'react';
15-
import {typeOf} from 'react-is';
1614
import * as TestRenderer from 'react-reconciler/inline.test';
1715
import {batchedUpdates} from 'events/ReactGenericBatching';
1816
import {findCurrentFiberUsingSlowPath} from 'react-reconciler/reflection';
@@ -73,15 +71,7 @@ function toJSON(inst: Instance | TextInstance): ReactTestRendererNode {
7371
const {children, ...props} = inst.props;
7472
/* eslint-enable */
7573
let renderedChildren = null;
76-
if (inst.props != null && inst.props.children != null) {
77-
// At least some of this element's children are from another renderer
78-
// (e.g. ReactDOM.createPortal)
79-
// In this case, treat all children as potentially from the other renderer.
80-
// If we don't, the JSON representation may contain overlaps.
81-
renderedChildren = React.Children.toArray(inst.props.children).map(
82-
reactElementToJSON,
83-
);
84-
} else if (inst.children && inst.children.length) {
74+
if (inst.children && inst.children.length) {
8575
renderedChildren = inst.children.map(toJSON);
8676
}
8777

@@ -99,25 +89,6 @@ function toJSON(inst: Instance | TextInstance): ReactTestRendererNode {
9989
}
10090
}
10191

102-
function reactElementToJSON(inst: any): ReactTestRendererNode {
103-
if (typeof inst === 'object') {
104-
/* eslint-disable no-unused-vars */
105-
// We don't include the `children` prop in JSON.
106-
// Instead, we will include the actual rendered children.
107-
const {children, ...props} = inst.props != null ? inst.props : {};
108-
const type = typeOf(inst);
109-
return {
110-
type: type == null ? 'unknown' : type.toString(),
111-
props,
112-
children: React.Children.toArray(
113-
inst.children || inst.props.children,
114-
).map(reactElementToJSON),
115-
};
116-
} else {
117-
return inst;
118-
}
119-
}
120-
12192
function childrenToTree(node) {
12293
if (!node) {
12394
return null;

packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.js renamed to packages/react-test-renderer/src/__tests__/ReactTestRendererPortals-test.internal.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,25 @@ const ReactDOM = require('react-dom');
1414

1515
// Isolate test renderer.
1616
jest.resetModules();
17+
jest.mock('shared/ReactFeatureFlags', () =>
18+
require('shared/forks/ReactFeatureFlags.test-renderer'),
19+
);
1720
const ReactTestRenderer = require('react-test-renderer');
1821

1922
describe('ReactTestRenderer', () => {
20-
it('should support ReactDOM portal usage', () => {
23+
it('should support ReactDOM.createPortal', () => {
2124
const container = document.createElement('div');
2225
let rendered = ReactTestRenderer.create(
2326
<div>
24-
{ReactDOM.createPortal(<span>Rendered by ReactDOM</span>, container)}
27+
{ReactDOM.createPortal(<span>ReactDOM portal</span>, container)}
2528
</div>,
2629
);
2730
expect(rendered.toJSON()).toMatchSnapshot();
2831

2932
rendered.update(
3033
<div>
31-
<span>Rendered by ReactTestRenderer</span>
32-
{ReactDOM.createPortal(<span>Rendered by ReactDOM</span>, container)}
34+
<span>ReactTestRenderer span</span>
35+
{ReactDOM.createPortal(<span>ReactDOM portal</span>, container)}
3336
</div>,
3437
);
3538
expect(rendered.toJSON()).toMatchSnapshot();

packages/react-test-renderer/src/__tests__/__snapshots__/ReactTestRenderer-test.js.snap

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`ReactTestRenderer should support ReactDOM.createPortal 1`] = `
4+
<div>
5+
<span>
6+
ReactDOM portal
7+
</span>
8+
</div>
9+
`;
10+
11+
exports[`ReactTestRenderer should support ReactDOM.createPortal 2`] = `
12+
<div>
13+
<span>
14+
ReactTestRenderer span
15+
</span>
16+
<span>
17+
ReactDOM portal
18+
</span>
19+
</div>
20+
`;

packages/shared/ReactFeatureFlags.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ export const enableProfilerTimer = __DEV__;
4242
// Fires getDerivedStateFromProps for state *or* props changes
4343
export const fireGetDerivedStateFromPropsOnStateUpdates = true;
4444

45+
// Return a fragment from createPortal()
46+
// This enables TestRenderer to consume portals from e.g. ReactDOM
47+
export const convertPortalsToFragments = false;
48+
4549
// Only used in www builds.
4650
export function addUserTimingListener() {
4751
invariant(false, 'Not implemented.');

packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const warnAboutLegacyContextAPI = __DEV__;
2222
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
2323
export const enableProfilerTimer = __DEV__;
2424
export const fireGetDerivedStateFromPropsOnStateUpdates = true;
25+
export const convertPortalsToFragments = false;
2526

2627
// Only used in www builds.
2728
export function addUserTimingListener() {

packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const warnAboutLegacyContextAPI = false;
2222
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
2323
export const enableProfilerTimer = false;
2424
export const fireGetDerivedStateFromPropsOnStateUpdates = true;
25+
export const convertPortalsToFragments = false;
2526

2627
// Only used in www builds.
2728
export function addUserTimingListener() {

packages/shared/forks/ReactFeatureFlags.native-fb.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const {
2727
// The rest of the flags are static for better dead code elimination.
2828
export const enableUserTimingAPI = __DEV__;
2929
export const warnAboutLegacyContextAPI = __DEV__;
30+
export const convertPortalsToFragments = false;
3031

3132
// Only used in www builds.
3233
export function addUserTimingListener() {

0 commit comments

Comments
 (0)