@@ -97,10 +97,22 @@ describe('ReactDOMFizzServer', () => {
97
97
let node = element . firstChild ;
98
98
while ( node ) {
99
99
if ( node . nodeType === 1 ) {
100
- if ( node . tagName !== 'SCRIPT' && ! node . hasAttribute ( 'hidden' ) ) {
100
+ if (
101
+ node . tagName !== 'SCRIPT' &&
102
+ node . tagName !== 'TEMPLATE' &&
103
+ ! node . hasAttribute ( 'hidden' ) &&
104
+ ! node . hasAttribute ( 'aria-hidden' )
105
+ ) {
101
106
const props = { } ;
102
107
const attributes = node . attributes ;
103
108
for ( let i = 0 ; i < attributes . length ; i ++ ) {
109
+ if (
110
+ attributes [ i ] . name === 'id' &&
111
+ attributes [ i ] . value . includes ( ':' )
112
+ ) {
113
+ // We assume this is a React added ID that's a non-visual implementation detail.
114
+ continue ;
115
+ }
104
116
props [ attributes [ i ] . name ] = attributes [ i ] . value ;
105
117
}
106
118
props . children = getVisibleChildren ( node ) ;
@@ -112,7 +124,7 @@ describe('ReactDOMFizzServer', () => {
112
124
node = node . nextSibling ;
113
125
}
114
126
return children . length === 0
115
- ? null
127
+ ? undefined
116
128
: children . length === 1
117
129
? children [ 0 ]
118
130
: children ;
@@ -408,4 +420,237 @@ describe('ReactDOMFizzServer', () => {
408
420
</ div > ,
409
421
] ) ;
410
422
} ) ;
423
+
424
+ // @gate experimental
425
+ it ( 'can resolve async content in esoteric parents' , async ( ) => {
426
+ function AsyncOption ( { text} ) {
427
+ return < option > { readText ( text ) } </ option > ;
428
+ }
429
+
430
+ function AsyncCol ( { className} ) {
431
+ return < col className = { readText ( className ) } > { [ ] } </ col > ;
432
+ }
433
+
434
+ function AsyncPath ( { id} ) {
435
+ return < path id = { readText ( id ) } > { [ ] } </ path > ;
436
+ }
437
+
438
+ function AsyncMi ( { id} ) {
439
+ return < mi id = { readText ( id ) } > { [ ] } </ mi > ;
440
+ }
441
+
442
+ function App ( ) {
443
+ return (
444
+ < div >
445
+ < select >
446
+ < Suspense fallback = "Loading..." >
447
+ < AsyncOption text = "Hello" />
448
+ </ Suspense >
449
+ </ select >
450
+ < Suspense fallback = "Loading..." >
451
+ < table >
452
+ < colgroup >
453
+ < AsyncCol className = "World" />
454
+ </ colgroup >
455
+ </ table >
456
+ < svg >
457
+ < g >
458
+ < AsyncPath id = "my-path" />
459
+ </ g >
460
+ </ svg >
461
+ < math >
462
+ < AsyncMi id = "my-mi" />
463
+ </ math >
464
+ </ Suspense >
465
+ </ div >
466
+ ) ;
467
+ }
468
+
469
+ await act ( async ( ) => {
470
+ const { startWriting} = ReactDOMFizzServer . pipeToNodeWritable (
471
+ < App /> ,
472
+ writable ,
473
+ ) ;
474
+ startWriting ( ) ;
475
+ } ) ;
476
+
477
+ expect ( getVisibleChildren ( container ) ) . toEqual (
478
+ < div >
479
+ < select > Loading...</ select > Loading...
480
+ </ div > ,
481
+ ) ;
482
+
483
+ await act ( async ( ) => {
484
+ resolveText ( 'Hello' ) ;
485
+ } ) ;
486
+
487
+ await act ( async ( ) => {
488
+ resolveText ( 'World' ) ;
489
+ } ) ;
490
+
491
+ await act ( async ( ) => {
492
+ resolveText ( 'my-path' ) ;
493
+ resolveText ( 'my-mi' ) ;
494
+ } ) ;
495
+
496
+ expect ( getVisibleChildren ( container ) ) . toEqual (
497
+ < div >
498
+ < select >
499
+ < option > Hello</ option >
500
+ </ select >
501
+ < table >
502
+ < colgroup >
503
+ < col class = "World" />
504
+ </ colgroup >
505
+ </ table >
506
+ < svg >
507
+ < g >
508
+ < path id = "my-path" />
509
+ </ g >
510
+ </ svg >
511
+ < math >
512
+ < mi id = "my-mi" />
513
+ </ math >
514
+ </ div > ,
515
+ ) ;
516
+
517
+ expect ( container . querySelector ( '#my-path' ) . namespaceURI ) . toBe (
518
+ 'http://www.w3.org/2000/svg' ,
519
+ ) ;
520
+ expect ( container . querySelector ( '#my-mi' ) . namespaceURI ) . toBe (
521
+ 'http://www.w3.org/1998/Math/MathML' ,
522
+ ) ;
523
+ } ) ;
524
+
525
+ // @gate experimental
526
+ it ( 'can resolve async content in table parents' , async ( ) => {
527
+ function AsyncTableBody ( { className, children} ) {
528
+ return < tbody className = { readText ( className ) } > { children } </ tbody > ;
529
+ }
530
+
531
+ function AsyncTableRow ( { className, children} ) {
532
+ return < tr className = { readText ( className ) } > { children } </ tr > ;
533
+ }
534
+
535
+ function AsyncTableCell ( { text} ) {
536
+ return < td > { readText ( text ) } </ td > ;
537
+ }
538
+
539
+ function App ( ) {
540
+ return (
541
+ < table >
542
+ < Suspense
543
+ fallback = {
544
+ < tbody >
545
+ < tr >
546
+ < td > Loading...</ td >
547
+ </ tr >
548
+ </ tbody >
549
+ } >
550
+ < AsyncTableBody className = "A" >
551
+ < AsyncTableRow className = "B" >
552
+ < AsyncTableCell text = "C" />
553
+ </ AsyncTableRow >
554
+ </ AsyncTableBody >
555
+ </ Suspense >
556
+ </ table >
557
+ ) ;
558
+ }
559
+
560
+ await act ( async ( ) => {
561
+ const { startWriting} = ReactDOMFizzServer . pipeToNodeWritable (
562
+ < App /> ,
563
+ writable ,
564
+ ) ;
565
+ startWriting ( ) ;
566
+ } ) ;
567
+
568
+ expect ( getVisibleChildren ( container ) ) . toEqual (
569
+ < table >
570
+ < tbody >
571
+ < tr >
572
+ < td > Loading...</ td >
573
+ </ tr >
574
+ </ tbody >
575
+ </ table > ,
576
+ ) ;
577
+
578
+ await act ( async ( ) => {
579
+ resolveText ( 'A' ) ;
580
+ } ) ;
581
+
582
+ await act ( async ( ) => {
583
+ resolveText ( 'B' ) ;
584
+ } ) ;
585
+
586
+ await act ( async ( ) => {
587
+ resolveText ( 'C' ) ;
588
+ } ) ;
589
+
590
+ expect ( getVisibleChildren ( container ) ) . toEqual (
591
+ < table >
592
+ < tbody class = "A" >
593
+ < tr class = "B" >
594
+ < td > C</ td >
595
+ </ tr >
596
+ </ tbody >
597
+ </ table > ,
598
+ ) ;
599
+ } ) ;
600
+
601
+ // @gate experimental
602
+ it ( 'can stream into an SVG container' , async ( ) => {
603
+ function AsyncPath ( { id} ) {
604
+ return < path id = { readText ( id ) } > { [ ] } </ path > ;
605
+ }
606
+
607
+ function App ( ) {
608
+ return (
609
+ < g >
610
+ < Suspense fallback = { < text > Loading...</ text > } >
611
+ < AsyncPath id = "my-path" />
612
+ </ Suspense >
613
+ </ g >
614
+ ) ;
615
+ }
616
+
617
+ await act ( async ( ) => {
618
+ const { startWriting} = ReactDOMFizzServer . pipeToNodeWritable (
619
+ < App /> ,
620
+ writable ,
621
+ {
622
+ namespaceURI : 'http://www.w3.org/2000/svg' ,
623
+ onReadyToStream ( ) {
624
+ writable . write ( '<svg>' ) ;
625
+ startWriting ( ) ;
626
+ writable . write ( '</svg>' ) ;
627
+ } ,
628
+ } ,
629
+ ) ;
630
+ } ) ;
631
+
632
+ expect ( getVisibleChildren ( container ) ) . toEqual (
633
+ < svg >
634
+ < g >
635
+ < text > Loading...</ text >
636
+ </ g >
637
+ </ svg > ,
638
+ ) ;
639
+
640
+ await act ( async ( ) => {
641
+ resolveText ( 'my-path' ) ;
642
+ } ) ;
643
+
644
+ expect ( getVisibleChildren ( container ) ) . toEqual (
645
+ < svg >
646
+ < g >
647
+ < path id = "my-path" />
648
+ </ g >
649
+ </ svg > ,
650
+ ) ;
651
+
652
+ expect ( container . querySelector ( '#my-path' ) . namespaceURI ) . toBe (
653
+ 'http://www.w3.org/2000/svg' ,
654
+ ) ;
655
+ } ) ;
411
656
} ) ;
0 commit comments