@@ -13,9 +13,13 @@ import {
13
13
createTextVNode ,
14
14
createBlock ,
15
15
openBlock ,
16
- createCommentVNode
16
+ createCommentVNode ,
17
+ withCtx ,
18
+ createElementBlock ,
19
+ ref ,
20
+ renderSlot
17
21
} from '@vue/runtime-test'
18
- import { PatchFlags } from '@vue/shared'
22
+ import { PatchFlags , SlotFlags } from '@vue/shared'
19
23
import { renderList } from '../src/helpers/renderList'
20
24
21
25
describe ( 'renderer: fragment' , ( ) => {
@@ -351,4 +355,121 @@ describe('renderer: fragment', () => {
351
355
render ( renderFn ( [ 'two' , 'one' ] ) , root )
352
356
expect ( serializeInner ( root ) ) . toBe ( `text<div>two</div>text<div>one</div>` )
353
357
} )
358
+
359
+ // #9200
360
+ test ( 'stable fragment in unstable slot' , ( ) => {
361
+ const root = nodeOps . createElement ( 'div' )
362
+
363
+ const items = ref ( [ { field1 : 'one' , field2 : 'two' as string | undefined } ] )
364
+
365
+ const textBlock = 'text-block'
366
+ const vIfBlock = 'v-if-block'
367
+
368
+ const Comp = {
369
+ render ( ctx : any ) {
370
+ return (
371
+ openBlock ( true ) ,
372
+ createElementBlock (
373
+ Fragment ,
374
+ null ,
375
+ renderList ( items . value , ( item , i ) => {
376
+ return (
377
+ openBlock ( ) ,
378
+ createElementBlock ( 'div' , { key : i } , [
379
+ ( openBlock ( true ) ,
380
+ createElementBlock (
381
+ Fragment ,
382
+ null ,
383
+ renderList ( [ 'field1' , 'field2' ] as const , field => {
384
+ return (
385
+ openBlock ( ) ,
386
+ createElementBlock ( 'span' , { key : field } , [
387
+ renderSlot (
388
+ ctx . $slots ,
389
+ 'default' ,
390
+ {
391
+ value : item [ field ] ,
392
+ field : field
393
+ } ,
394
+ ( ) => [
395
+ item [ field ]
396
+ ? ( openBlock ( ) ,
397
+ createElementBlock (
398
+ Fragment ,
399
+ { key : 0 } ,
400
+ [ createTextVNode ( 'xxx' ) ] ,
401
+ PatchFlags . STABLE_FRAGMENT
402
+ ) )
403
+ : ( openBlock ( ) ,
404
+ createElementBlock (
405
+ Fragment ,
406
+ { key : 1 } ,
407
+ [ createTextVNode ( 'yyy' ) ] ,
408
+ PatchFlags . STABLE_FRAGMENT
409
+ ) )
410
+ ]
411
+ )
412
+ ] )
413
+ )
414
+ } ) ,
415
+ PatchFlags . KEYED_FRAGMENT
416
+ ) )
417
+ ] )
418
+ )
419
+ } ) ,
420
+ PatchFlags . KEYED_FRAGMENT
421
+ )
422
+ )
423
+ }
424
+ }
425
+
426
+ const hoisted1 = { key : 0 }
427
+ const hoisted2 = { key : 0 }
428
+ const hoisted3 = { key : 1 }
429
+
430
+ const renderFn = ( ) => {
431
+ return (
432
+ openBlock ( true ) ,
433
+ createVNode ( Comp , null , {
434
+ default : withCtx (
435
+ ( { field, value } : { field : string ; value : any } ) => [
436
+ field === 'field1'
437
+ ? ( openBlock ( ) , createElementBlock ( 'div' , hoisted1 , textBlock ) )
438
+ : field === 'field2'
439
+ ? ( openBlock ( ) ,
440
+ createElementBlock (
441
+ Fragment ,
442
+ { key : 1 } ,
443
+ [
444
+ value
445
+ ? ( openBlock ( ) ,
446
+ createElementBlock ( 'div' , hoisted2 , vIfBlock ) )
447
+ : createCommentVNode ( 'v-if' , true ) ,
448
+ value
449
+ ? ( openBlock ( ) ,
450
+ createElementBlock ( 'div' , hoisted3 , vIfBlock ) )
451
+ : createCommentVNode ( 'v-if' , true )
452
+ ] ,
453
+ PatchFlags . STABLE_FRAGMENT
454
+ ) )
455
+ : createCommentVNode ( 'v-if' , true )
456
+ ]
457
+ ) ,
458
+ _ : SlotFlags . STABLE
459
+ } )
460
+ )
461
+ }
462
+
463
+ render ( renderFn ( ) , root )
464
+ expect ( serializeInner ( root ) ) . toBe (
465
+ `<div><span><div>${ textBlock } </div></span><span><div>${ vIfBlock } </div><div>${ vIfBlock } </div></span></div>`
466
+ )
467
+
468
+ items . value = [ { field1 : 'one' , field2 : undefined } ]
469
+
470
+ render ( renderFn ( ) , root )
471
+ expect ( serializeInner ( root ) ) . toBe (
472
+ `<div><span><div>${ textBlock } </div></span><span>yyy</span></div>`
473
+ )
474
+ } )
354
475
} )
0 commit comments