188
}
189
190
if (ReactDOMFeatureFlags.useFiber) {
191
+ var svgEls, htmlEls, mathEls;
192
+ var expectSVG = {ref: el => svgEls.push(el)};
193
+ var expectHTML = {ref: el => htmlEls.push(el)};
194
+ var expectMath = {ref: el => mathEls.push(el)};
195
+
196
+ var portal = function(tree) {
197
+ return ReactDOM.unstable_createPortal(
198
+ tree,
199
+ document.createElement('div')
200
+ );
201
+ };
202
+
203
+ var assertNamespacesMatch = function(tree) {
204
+ container = document.createElement('div');
205
+ svgEls = [];
206
+ htmlEls = [];
207
+ mathEls = [];
208
+
209
+ ReactDOM.render(tree, container);
210
+ svgEls.forEach(el => {
211
+ expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg');
212
+ });
213
+ htmlEls.forEach(el => {
214
+ expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml');
215
+ });
216
+ mathEls.forEach(el => {
217
+ expect(el.namespaceURI).toBe('http://www.w3.org/1998/Math/MathML');
218
+ });
219
+
220
+ ReactDOM.unmountComponentAtNode(container);
221
+ expect(container.innerHTML).toBe('');
222
+ };
223
+
224
it('should render one portal', () => {
225
var portalContainer = document.createElement('div');
226
366
expect(container.innerHTML).toBe('');
367
});
368
369
+ it('should keep track of namespace across portals (simple)', () => {
370
+ assertNamespacesMatch(
371
+ <svg {...expectSVG}>
372
+ <image {...expectSVG} />
373
+ {portal(
374
+ <div {...expectHTML} />
375
+ )}
376
+ <image {...expectSVG} />
377
+ </svg>
378
+ );
379
+ assertNamespacesMatch(
380
+ <math {...expectMath}>
381
+ <mi {...expectMath} />
382
+ {portal(
383
+ <div {...expectHTML} />
384
+ )}
385
+ <mi {...expectMath} />
386
+ </math>
387
+ );
388
+ assertNamespacesMatch(
389
+ <div {...expectHTML}>
390
+ <p {...expectHTML} />
391
+ {portal(
392
+ <svg {...expectSVG}>
393
+ <image {...expectSVG} />
394
+ </svg>
395
+ )}
396
+ <p {...expectHTML} />
397
+ </div>
398
+ );
399
+ });
400
+
401
+ it('should keep track of namespace across portals (medium)', () => {
402
+ assertNamespacesMatch(
403
+ <div {...expectHTML}>
404
+ <math {...expectMath}>
405
+ <mi {...expectMath} />
406
+ {portal(
407
+ <svg {...expectSVG}>
408
+ <image {...expectSVG} />
409
+ </svg>
410
+ )}
411
+ </math>
412
+ <p {...expectHTML} />
413
+ </div>
414
+ );
415
+ assertNamespacesMatch(
416
+ <math {...expectMath}>
417
+ <mi {...expectMath} />
418
+ {portal(
419
+ <svg {...expectSVG}>
420
+ <image {...expectSVG} />
421
+ <foreignObject {...expectSVG}>
422
+ <p {...expectHTML} />
423
+ <math {...expectMath}>
424
+ <mi {...expectMath} />
425
+ </math>
426
+ <p {...expectHTML} />
427
+ </foreignObject>
428
+ <image {...expectSVG} />
429
+ </svg>
430
+ )}
431
+ <mi {...expectMath} />
432
+ </math>
433
+ );
434
+ assertNamespacesMatch(
435
+ <div {...expectHTML}>
436
+ {portal(
437
+ <svg {...expectSVG}>
438
+ {portal(
439
+ <div {...expectHTML} />
440
+ )}
441
+ <image {...expectSVG} />
442
+ </svg>
443
+ )}
444
+ <p {...expectHTML} />
445
+ </div>
446
+ );
447
+ assertNamespacesMatch(
448
+ <svg {...expectSVG}>
449
+ <svg {...expectSVG}>
450
+ {portal(
451
+ <div {...expectHTML} />
452
+ )}
453
+ <image {...expectSVG} />
454
+ </svg>
455
+ <image {...expectSVG} />
456
+ </svg>
457
+ );
458
+ });
459
+
460
+ it('should keep track of namespace across portals (complex)', () => {
461
+ assertNamespacesMatch(
462
+ <div {...expectHTML}>
463
+ {portal(
464
+ <svg {...expectSVG}>
465
+ <image {...expectSVG} />
466
+ </svg>
467
+ )}
468
+ <p {...expectHTML} />
469
+ <svg {...expectSVG}>
470
+ <image {...expectSVG} />
471
+ </svg>
472
+ <svg {...expectSVG}>
473
+ <svg {...expectSVG}>
474
+ <image {...expectSVG} />
475
+ </svg>
476
+ <image {...expectSVG} />
477
+ </svg>
478
+ <p {...expectHTML} />
479
+ </div>
480
+ );
481
+ assertNamespacesMatch(
482
+ <div {...expectHTML}>
483
+ <svg {...expectSVG}>
484
+ <svg {...expectSVG}>
485
+ <image {...expectSVG} />
486
+ {portal(
487
+ <svg {...expectSVG}>
488
+ <image {...expectSVG} />
489
+ <svg {...expectSVG}>
490
+ <image {...expectSVG} />
491
+ </svg>
492
+ <image {...expectSVG} />
493
+ </svg>
494
+ )}
495
+ <image {...expectSVG} />
496
+ <foreignObject {...expectSVG}>
497
+ <p {...expectHTML} />
498
+ {portal(<p {...expectHTML} />)}
499
+ <p {...expectHTML} />
500
+ </foreignObject>
501
+ </svg>
502
+ <image {...expectSVG} />
503
+ </svg>
504
+ <p {...expectHTML} />
505
+ </div>
506
+ );
507
+ assertNamespacesMatch(
508
+ <div {...expectHTML}>
509
+ <svg {...expectSVG}>
510
+ <foreignObject {...expectSVG}>
511
+ <p {...expectHTML} />
512
+ {portal(
513
+ <svg {...expectSVG}>
514
+ <image {...expectSVG} />
515
+ <svg {...expectSVG}>
516
+ <image {...expectSVG} />
517
+ <foreignObject {...expectSVG}>
518
+ <p {...expectHTML} />
519
+ </foreignObject>
520
+ {portal(<p {...expectHTML} />)}
521
+ </svg>
522
+ <image {...expectSVG} />
523
+ </svg>
524
+ )}
525
+ <p {...expectHTML} />
526
+ </foreignObject>
527
+ <image {...expectSVG} />
528
+ </svg>
529
+ <p {...expectHTML} />
530
+ </div>
531
+ );
532
+ });
533
+
534
+ it('should unwind namespaces on uncaught errors', () => {
535
+ function BrokenRender() {
536
+ throw new Error('Hello');
537
+ }
538
+
539
+ expect(() => {
540
+ assertNamespacesMatch(
541
+ <svg {...expectSVG}>
542
+ <BrokenRender />
543
+ </svg>
544
+ );
545
+ }).toThrow('Hello');
546
+ assertNamespacesMatch(
547
+ <div {...expectHTML} />
548
+ );
549
+ });
550
+
551
+ it('should unwind namespaces on caught errors', () => {
552
+ function BrokenRender() {
553
+ throw new Error('Hello');
554
+ }
555
+
556
+ class ErrorBoundary extends React.Component {
557
+ state = {error: null};
558
+ unstable_handleError(error) {
559
+ this.setState({error});
560
+ }
561
+ render() {
562
+ if (this.state.error) {
563
+ return <p {...expectHTML} />;
564
+ }
565
+ return this.props.children;
566
+ }
567
+ }
568
+
569
+ assertNamespacesMatch(
570
+ <svg {...expectSVG}>
571
+ <foreignObject {...expectSVG}>
572
+ <ErrorBoundary>
573
+ <math {...expectMath}>
574
+ <BrokenRender />
575
+ </math>
576
+ </ErrorBoundary>
577
+ </foreignObject>
578
+ <image {...expectSVG} />
579
+ </svg>
580
+ );
581
+ assertNamespacesMatch(
582
+ <div {...expectHTML} />
583
+ );
584
+ });
585
+
586
+ it('should unwind namespaces on caught errors in a portal', () => {
587
+ function BrokenRender() {
588
+ throw new Error('Hello');
589
+ }
590
+
591
+ class ErrorBoundary extends React.Component {
592
+ state = {error: null};
593
+ unstable_handleError(error) {
594
+ this.setState({error});
595
+ }
596
+ render() {
597
+ if (this.state.error) {
598
+ return <image {...expectSVG} />;
599
+ }
600
+ return this.props.children;
601
+ }
602
+ }
603
+
604
+ assertNamespacesMatch(
605
+ <svg {...expectSVG}>
606
+ <ErrorBoundary>
607
+ {portal(
608
+ <div {...expectHTML}>
609
+ <math {...expectMath}>
610
+ <BrokenRender />)
611
+ </math>
612
+ </div>
613
+ )}
614
+ </ErrorBoundary>
615
+ {
616
+ /*
617
+ * TODO: enable. Currently this leads to stack overflow
618
+ * but it might be a bug in error boundaries rather than SVG or portals.
619
+ portal(
620
+ <div {...expectHTML} />
621
+ )
622
+ */
623
+ }
624
+ </svg>
625
+ );
626
+ });
627
+
628
it('should pass portal context when rendering subtree elsewhere', () => {
629
var portalContainer = document.createElement('div');
630