@@ -97,10 +97,22 @@ describe('ReactDOMFizzServer', () => {
9797 let node = element . firstChild ;
9898 while ( node ) {
9999 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+ ) {
101106 const props = { } ;
102107 const attributes = node . attributes ;
103108 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+ }
104116 props [ attributes [ i ] . name ] = attributes [ i ] . value ;
105117 }
106118 props . children = getVisibleChildren ( node ) ;
@@ -112,7 +124,7 @@ describe('ReactDOMFizzServer', () => {
112124 node = node . nextSibling ;
113125 }
114126 return children . length === 0
115- ? null
127+ ? undefined
116128 : children . length === 1
117129 ? children [ 0 ]
118130 : children ;
@@ -408,4 +420,237 @@ describe('ReactDOMFizzServer', () => {
408420 </ div > ,
409421 ] ) ;
410422 } ) ;
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+ } ) ;
411656} ) ;
0 commit comments