@samitouri / QOS-React / commits / c6f3524df5

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

Dominic Gannaway committed Mar 28, 2019 at 15:36 UTC c6f3524df585c193214e7e685a11b5f2d1953b49
2 files changed +250 -1
packages/react-dom/src/server/ReactPartialRenderer.js
+29
@@ -22,6 +22,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
22 import {
23 warnAboutDeprecatedLifecycles,
24 enableSuspenseServerRenderer,
25 + enableEventAPI,
26 } from 'shared/ReactFeatureFlags';
27
28 import {
@@ -36,6 +37,8 @@ import {
37 REACT_CONTEXT_TYPE,
38 REACT_LAZY_TYPE,
39 REACT_MEMO_TYPE,
40 + REACT_EVENT_COMPONENT_TYPE,
41 + REACT_EVENT_TARGET_TYPE,
42 } from 'shared/ReactSymbols';
43
44 import {
@@ -1162,6 +1165,32 @@ class ReactDOMServerRenderer {
1165 this.stack.push(frame);
1166 return '';
1167 }
1168 + case REACT_EVENT_COMPONENT_TYPE:
1169 + case REACT_EVENT_TARGET_TYPE: {
1170 + if (enableEventAPI) {
1171 + const nextChildren = toArray(
1172 + ((nextChild: any): ReactElement).props.children,
1173 + );
1174 + const frame: Frame = {
1175 + type: null,
1176 + domNamespace: parentNamespace,
1177 + children: nextChildren,
1178 + childIndex: 0,
1179 + context: context,
1180 + footer: '',
1181 + };
1182 + if (__DEV__) {
1183 + ((frame: any): FrameDev).debugElementStack = [];
1184 + }
1185 + this.stack.push(frame);
1186 + return '';
1187 + }
1188 + invariant(
1189 + false,
1190 + 'ReactDOMServer does not yet support the event API.',
1191 + );
1192 + }
1193 + // eslint-disable-next-line-no-fallthrough
1194 case REACT_LAZY_TYPE:
1195 invariant(
1196 false,
packages/react-reconciler/src/__tests__/ReactFiberEvents-test-internal.js
+221 -1
@@ -5,7 +5,6 @@
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @emails react-core
8 - * @jest-environment node
8 */
9
10 'use strict';
@@ -16,6 +15,8 @@ let Scheduler;
15 let ReactFeatureFlags;
16 let EventComponent;
17 let ReactTestRenderer;
18 +let ReactDOM;
19 +let ReactDOMServer;
20 let EventTarget;
21 let ReactEvents;
22
@@ -51,6 +52,16 @@ function initTestRenderer() {
52 ReactTestRenderer = require('react-test-renderer');
53 }
54
55 +function initReactDOM() {
56 + init();
57 + ReactDOM = require('react-dom');
58 +}
59 +
60 +function initReactDOMServer() {
61 + init();
62 + ReactDOMServer = require('react-dom/server');
63 +}
64 +
65 // This is a new feature in Fiber so I put it in its own test file. It could
66 // probably move to one of the other test files once it is official.
67 describe('ReactFiberEvents', () => {
@@ -358,4 +369,213 @@ describe('ReactFiberEvents', () => {
369 );
370 });
371 });
372 +
373 + describe('ReactDOM', () => {
374 + beforeEach(() => {
375 + initReactDOM();
376 + EventComponent = createReactEventComponent();
377 + EventTarget = ReactEvents.TouchHitTarget;
378 + });
379 +
380 + it('should render a simple event component with a single child', () => {
381 + const Test = () => (
382 + <EventComponent>
383 + <div>Hello world</div>
384 + </EventComponent>
385 + );
386 + const container = document.createElement('div');
387 + ReactDOM.render(<Test />, container);
388 + expect(Scheduler).toFlushWithoutYielding();
389 + expect(container.innerHTML).toBe('<div>Hello world</div>');
390 + });
391 +
392 + it('should warn when an event component has a direct text child', () => {
393 + const Test = () => <EventComponent>Hello world</EventComponent>;
394 +
395 + expect(() => {
396 + const container = document.createElement('div');
397 + ReactDOM.render(<Test />, container);
398 + expect(Scheduler).toFlushWithoutYielding();
399 + }).toWarnDev(
400 + 'Warning: validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
401 + 'Wrap the child text "Hello world" in an element.',
402 + );
403 + });
404 +
405 + it('should warn when an event component has a direct text child #2', () => {
406 + const ChildWrapper = () => 'Hello world';
407 + const Test = () => (
408 + <EventComponent>
409 + <ChildWrapper />
410 + </EventComponent>
411 + );
412 +
413 + expect(() => {
414 + const container = document.createElement('div');
415 + ReactDOM.render(<Test />, container);
416 + expect(Scheduler).toFlushWithoutYielding();
417 + }).toWarnDev(
418 + 'Warning: validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
419 + 'Wrap the child text "Hello world" in an element.',
420 + );
421 + });
422 +
423 + it('should render a simple event component with a single event target', () => {
424 + const Test = () => (
425 + <EventComponent>
426 + <EventTarget>
427 + <div>Hello world</div>
428 + </EventTarget>
429 + </EventComponent>
430 + );
431 +
432 + const container = document.createElement('div');
433 + ReactDOM.render(<Test />, container);
434 + expect(Scheduler).toFlushWithoutYielding();
435 + expect(container.innerHTML).toBe('<div>Hello world</div>');
436 +
437 + const Test2 = () => (
438 + <EventComponent>
439 + <EventTarget>
440 + <span>I am now a span</span>
441 + </EventTarget>
442 + </EventComponent>
443 + );
444 +
445 + ReactDOM.render(<Test2 />, container);
446 + expect(Scheduler).toFlushWithoutYielding();
447 + expect(container.innerHTML).toBe('<span>I am now a span</span>');
448 + });
449 +
450 + it('should warn when an event target has a direct text child', () => {
451 + const Test = () => (
452 + <EventComponent>
453 + <EventTarget>Hello world</EventTarget>
454 + </EventComponent>
455 + );
456 +
457 + expect(() => {
458 + const container = document.createElement('div');
459 + ReactDOM.render(<Test />, container);
460 + expect(Scheduler).toFlushWithoutYielding();
461 + }).toWarnDev([
462 + 'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
463 + 'Wrap the child text "Hello world" in an element.',
464 + 'Warning: <TouchHitTarget> must have a single DOM element as a child. Found no children.',
465 + ]);
466 + });
467 +
468 + it('should warn when an event target has a direct text child #2', () => {
469 + const ChildWrapper = () => 'Hello world';
470 + const Test = () => (
471 + <EventComponent>
472 + <EventTarget>
473 + <ChildWrapper />
474 + </EventTarget>
475 + </EventComponent>
476 + );
477 +
478 + expect(() => {
479 + const container = document.createElement('div');
480 + ReactDOM.render(<Test />, container);
481 + expect(Scheduler).toFlushWithoutYielding();
482 + }).toWarnDev([
483 + 'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
484 + 'Wrap the child text "Hello world" in an element.',
485 + 'Warning: <TouchHitTarget> must have a single DOM element as a child. Found no children.',
486 + ]);
487 + });
488 +
489 + it('should warn when an event target has more than one child', () => {
490 + const Test = () => (
491 + <EventComponent>
492 + <EventTarget>
493 + <span>Child 1</span>
494 + <span>Child 2</span>
495 + </EventTarget>
496 + </EventComponent>
497 + );
498 +
499 + const container = document.createElement('div');
500 + expect(() => {
501 + ReactDOM.render(<Test />, container);
502 + expect(Scheduler).toFlushWithoutYielding();
503 + }).toWarnDev(
504 + 'Warning: <TouchHitTarget> must only have a single DOM element as a child. Found many children.',
505 + );
506 + // This should not fire a warning, as this is now valid.
507 + const Test2 = () => (
508 + <EventComponent>
509 + <EventTarget>
510 + <span>Child 1</span>
511 + </EventTarget>
512 + </EventComponent>
513 + );
514 + ReactDOM.render(<Test2 />, container);
515 + expect(Scheduler).toFlushWithoutYielding();
516 + expect(container.innerHTML).toBe('<span>Child 1</span>');
517 + });
518 +
519 + it('should warn if an event target is not a direct child of an event component', () => {
520 + const Test = () => (
521 + <EventComponent>
522 + <div>
523 + <EventTarget>
524 + <span>Child 1</span>
525 + </EventTarget>
526 + </div>
527 + </EventComponent>
528 + );
529 +
530 + expect(() => {
531 + const container = document.createElement('div');
532 + ReactDOM.render(<Test />, container);
533 + expect(Scheduler).toFlushWithoutYielding();
534 + }).toWarnDev(
535 + 'Warning: validateDOMNesting: React event targets must be direct children of event components.',
536 + );
537 + });
538 + });
539 +
540 + describe('ReactDOMServer', () => {
541 + beforeEach(() => {
542 + initReactDOMServer();
543 + EventComponent = createReactEventComponent();
544 + EventTarget = ReactEvents.TouchHitTarget;
545 + });
546 +
547 + it('should render a simple event component with a single child', () => {
548 + const Test = () => (
549 + <EventComponent>
550 + <div>Hello world</div>
551 + </EventComponent>
552 + );
553 + const output = ReactDOMServer.renderToString(<Test />);
554 + expect(output).toBe('<div>Hello world</div>');
555 + });
556 +
557 + it('should render a simple event component with a single event target', () => {
558 + const Test = () => (
559 + <EventComponent>
560 + <EventTarget>
561 + <div>Hello world</div>
562 + </EventTarget>
563 + </EventComponent>
564 + );
565 +
566 + let output = ReactDOMServer.renderToString(<Test />);
567 + expect(output).toBe('<div>Hello world</div>');
568 +
569 + const Test2 = () => (
570 + <EventComponent>
571 + <EventTarget>
572 + <span>I am now a span</span>
573 + </EventTarget>
574 + </EventComponent>
575 + );
576 +
577 + output = ReactDOMServer.renderToString(<Test2 />);
578 + expect(output).toBe('<span>I am now a span</span>');
579 + });
580 + });
581 });