Adds React event component and React event target support to SSR renderer (#15242)

* Adds React event component and React event target support to SSR renderer
This commit is contained in:
Dominic Gannaway
2019-03-28 15:36:21 -07:00
committed by GitHub
parent c7a2dce50a
commit c6f3524df5
2 changed files with 250 additions and 1 deletions
+29
View File
@@ -22,6 +22,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
import {
warnAboutDeprecatedLifecycles,
enableSuspenseServerRenderer,
enableEventAPI,
} from 'shared/ReactFeatureFlags';
import {
@@ -36,6 +37,8 @@ import {
REACT_CONTEXT_TYPE,
REACT_LAZY_TYPE,
REACT_MEMO_TYPE,
REACT_EVENT_COMPONENT_TYPE,
REACT_EVENT_TARGET_TYPE,
} from 'shared/ReactSymbols';
import {
@@ -1162,6 +1165,32 @@ class ReactDOMServerRenderer {
this.stack.push(frame);
return '';
}
case REACT_EVENT_COMPONENT_TYPE:
case REACT_EVENT_TARGET_TYPE: {
if (enableEventAPI) {
const nextChildren = toArray(
((nextChild: any): ReactElement).props.children,
);
const frame: Frame = {
type: null,
domNamespace: parentNamespace,
children: nextChildren,
childIndex: 0,
context: context,
footer: '',
};
if (__DEV__) {
((frame: any): FrameDev).debugElementStack = [];
}
this.stack.push(frame);
return '';
}
invariant(
false,
'ReactDOMServer does not yet support the event API.',
);
}
// eslint-disable-next-line-no-fallthrough
case REACT_LAZY_TYPE:
invariant(
false,
@@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
* @jest-environment node
*/
'use strict';
@@ -16,6 +15,8 @@ let Scheduler;
let ReactFeatureFlags;
let EventComponent;
let ReactTestRenderer;
let ReactDOM;
let ReactDOMServer;
let EventTarget;
let ReactEvents;
@@ -51,6 +52,16 @@ function initTestRenderer() {
ReactTestRenderer = require('react-test-renderer');
}
function initReactDOM() {
init();
ReactDOM = require('react-dom');
}
function initReactDOMServer() {
init();
ReactDOMServer = require('react-dom/server');
}
// This is a new feature in Fiber so I put it in its own test file. It could
// probably move to one of the other test files once it is official.
describe('ReactFiberEvents', () => {
@@ -358,4 +369,213 @@ describe('ReactFiberEvents', () => {
);
});
});
describe('ReactDOM', () => {
beforeEach(() => {
initReactDOM();
EventComponent = createReactEventComponent();
EventTarget = ReactEvents.TouchHitTarget;
});
it('should render a simple event component with a single child', () => {
const Test = () => (
<EventComponent>
<div>Hello world</div>
</EventComponent>
);
const container = document.createElement('div');
ReactDOM.render(<Test />, container);
expect(Scheduler).toFlushWithoutYielding();
expect(container.innerHTML).toBe('<div>Hello world</div>');
});
it('should warn when an event component has a direct text child', () => {
const Test = () => <EventComponent>Hello world</EventComponent>;
expect(() => {
const container = document.createElement('div');
ReactDOM.render(<Test />, container);
expect(Scheduler).toFlushWithoutYielding();
}).toWarnDev(
'Warning: validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
'Wrap the child text "Hello world" in an element.',
);
});
it('should warn when an event component has a direct text child #2', () => {
const ChildWrapper = () => 'Hello world';
const Test = () => (
<EventComponent>
<ChildWrapper />
</EventComponent>
);
expect(() => {
const container = document.createElement('div');
ReactDOM.render(<Test />, container);
expect(Scheduler).toFlushWithoutYielding();
}).toWarnDev(
'Warning: validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
'Wrap the child text "Hello world" in an element.',
);
});
it('should render a simple event component with a single event target', () => {
const Test = () => (
<EventComponent>
<EventTarget>
<div>Hello world</div>
</EventTarget>
</EventComponent>
);
const container = document.createElement('div');
ReactDOM.render(<Test />, container);
expect(Scheduler).toFlushWithoutYielding();
expect(container.innerHTML).toBe('<div>Hello world</div>');
const Test2 = () => (
<EventComponent>
<EventTarget>
<span>I am now a span</span>
</EventTarget>
</EventComponent>
);
ReactDOM.render(<Test2 />, container);
expect(Scheduler).toFlushWithoutYielding();
expect(container.innerHTML).toBe('<span>I am now a span</span>');
});
it('should warn when an event target has a direct text child', () => {
const Test = () => (
<EventComponent>
<EventTarget>Hello world</EventTarget>
</EventComponent>
);
expect(() => {
const container = document.createElement('div');
ReactDOM.render(<Test />, container);
expect(Scheduler).toFlushWithoutYielding();
}).toWarnDev([
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
'Wrap the child text "Hello world" in an element.',
'Warning: <TouchHitTarget> must have a single DOM element as a child. Found no children.',
]);
});
it('should warn when an event target has a direct text child #2', () => {
const ChildWrapper = () => 'Hello world';
const Test = () => (
<EventComponent>
<EventTarget>
<ChildWrapper />
</EventTarget>
</EventComponent>
);
expect(() => {
const container = document.createElement('div');
ReactDOM.render(<Test />, container);
expect(Scheduler).toFlushWithoutYielding();
}).toWarnDev([
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
'Wrap the child text "Hello world" in an element.',
'Warning: <TouchHitTarget> must have a single DOM element as a child. Found no children.',
]);
});
it('should warn when an event target has more than one child', () => {
const Test = () => (
<EventComponent>
<EventTarget>
<span>Child 1</span>
<span>Child 2</span>
</EventTarget>
</EventComponent>
);
const container = document.createElement('div');
expect(() => {
ReactDOM.render(<Test />, container);
expect(Scheduler).toFlushWithoutYielding();
}).toWarnDev(
'Warning: <TouchHitTarget> must only have a single DOM element as a child. Found many children.',
);
// This should not fire a warning, as this is now valid.
const Test2 = () => (
<EventComponent>
<EventTarget>
<span>Child 1</span>
</EventTarget>
</EventComponent>
);
ReactDOM.render(<Test2 />, container);
expect(Scheduler).toFlushWithoutYielding();
expect(container.innerHTML).toBe('<span>Child 1</span>');
});
it('should warn if an event target is not a direct child of an event component', () => {
const Test = () => (
<EventComponent>
<div>
<EventTarget>
<span>Child 1</span>
</EventTarget>
</div>
</EventComponent>
);
expect(() => {
const container = document.createElement('div');
ReactDOM.render(<Test />, container);
expect(Scheduler).toFlushWithoutYielding();
}).toWarnDev(
'Warning: validateDOMNesting: React event targets must be direct children of event components.',
);
});
});
describe('ReactDOMServer', () => {
beforeEach(() => {
initReactDOMServer();
EventComponent = createReactEventComponent();
EventTarget = ReactEvents.TouchHitTarget;
});
it('should render a simple event component with a single child', () => {
const Test = () => (
<EventComponent>
<div>Hello world</div>
</EventComponent>
);
const output = ReactDOMServer.renderToString(<Test />);
expect(output).toBe('<div>Hello world</div>');
});
it('should render a simple event component with a single event target', () => {
const Test = () => (
<EventComponent>
<EventTarget>
<div>Hello world</div>
</EventTarget>
</EventComponent>
);
let output = ReactDOMServer.renderToString(<Test />);
expect(output).toBe('<div>Hello world</div>');
const Test2 = () => (
<EventComponent>
<EventTarget>
<span>I am now a span</span>
</EventTarget>
</EventComponent>
);
output = ReactDOMServer.renderToString(<Test2 />);
expect(output).toBe('<span>I am now a span</span>');
});
});
});