componentSlots.spec.ts 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223
  1. // NOTE: This test is implemented based on the case of `runtime-core/__test__/componentSlots.spec.ts`.
  2. import {
  3. child,
  4. createComponent,
  5. createFor,
  6. createForSlots,
  7. createIf,
  8. createSlot,
  9. createVaporApp,
  10. defineVaporComponent,
  11. insert,
  12. prepend,
  13. renderEffect,
  14. setInsertionState,
  15. template,
  16. txt,
  17. vaporInteropPlugin,
  18. withVaporCtx,
  19. } from '../src'
  20. import {
  21. type Ref,
  22. createApp,
  23. createSlots,
  24. currentInstance,
  25. h,
  26. nextTick,
  27. ref,
  28. renderSlot,
  29. toDisplayString,
  30. } from '@vue/runtime-dom'
  31. import { makeRender } from './_utils'
  32. import type { DynamicSlot } from '../src/componentSlots'
  33. import { setElementText, setText } from '../src/dom/prop'
  34. const define = makeRender<any>()
  35. function renderWithSlots(slots: any): any {
  36. let instance: any
  37. const Comp = defineVaporComponent({
  38. setup() {
  39. const t0 = template('<div></div>')
  40. const n0 = t0()
  41. instance = currentInstance
  42. return n0
  43. },
  44. })
  45. const { render } = define({
  46. render() {
  47. return createComponent(Comp, {}, slots)
  48. },
  49. })
  50. render()
  51. return instance
  52. }
  53. describe('component: slots', () => {
  54. test('initSlots: instance.slots should be set correctly', () => {
  55. const { slots } = renderWithSlots({
  56. default: () => template('<span></span>')(),
  57. })
  58. expect(slots.default()).toMatchObject(document.createElement('span'))
  59. })
  60. test('updateSlots: instance.slots should be updated correctly', async () => {
  61. const flag1 = ref(true)
  62. let instance: any
  63. const Child = () => {
  64. instance = currentInstance
  65. return template('child')()
  66. }
  67. const { render } = define({
  68. render() {
  69. return createComponent(
  70. Child,
  71. {},
  72. {
  73. $: [
  74. () =>
  75. flag1.value
  76. ? { name: 'one', fn: () => template('<span></span>')() }
  77. : { name: 'two', fn: () => template('<div></div>')() },
  78. ],
  79. },
  80. )
  81. },
  82. })
  83. render()
  84. expect(instance.slots).toHaveProperty('one')
  85. expect(instance.slots).not.toHaveProperty('two')
  86. flag1.value = false
  87. await nextTick()
  88. expect(instance.slots).not.toHaveProperty('one')
  89. expect(instance.slots).toHaveProperty('two')
  90. })
  91. // passes but no warning for slot invocation in vapor currently
  92. test.todo('should not warn when mounting another app in setup', () => {
  93. const Comp = defineVaporComponent({
  94. setup(_, { slots }) {
  95. return slots.default!()
  96. },
  97. })
  98. const mountComp = () => {
  99. createVaporApp({
  100. render() {
  101. return createComponent(
  102. Comp,
  103. {},
  104. { default: () => template('msg')() },
  105. )!
  106. },
  107. })
  108. }
  109. const App = {
  110. setup() {
  111. mountComp()
  112. return []
  113. },
  114. }
  115. createVaporApp(App).mount(document.createElement('div'))
  116. expect(
  117. 'Slot "default" invoked outside of the render function',
  118. ).not.toHaveBeenWarned()
  119. })
  120. describe('createSlot', () => {
  121. test('slot should be rendered correctly', () => {
  122. const Comp = defineVaporComponent(() => {
  123. const n0 = template('<div>')()
  124. insert(createSlot('header'), n0 as any as ParentNode)
  125. return n0
  126. })
  127. const { host } = define(() => {
  128. return createComponent(Comp, null, {
  129. header: () => template('header')(),
  130. })
  131. }).render()
  132. expect(host.innerHTML).toBe('<div>header<!--slot--></div>')
  133. })
  134. test('slot should be rendered correctly with slot props', async () => {
  135. const src = ref('header')
  136. const Comp = defineVaporComponent(() => {
  137. const n0 = template('<div></div>')()
  138. insert(
  139. createSlot('header', { title: () => src.value }),
  140. n0 as any as ParentNode,
  141. )
  142. return n0
  143. })
  144. const { host } = define(() => {
  145. return createComponent(Comp, null, {
  146. header: props => {
  147. const el = template('<h1></h1>')()
  148. renderEffect(() => {
  149. setElementText(el, props.title)
  150. })
  151. return el
  152. },
  153. })
  154. }).render()
  155. expect(host.innerHTML).toBe('<div><h1>header</h1><!--slot--></div>')
  156. src.value = 'footer'
  157. await nextTick()
  158. expect(host.innerHTML).toBe('<div><h1>footer</h1><!--slot--></div>')
  159. })
  160. test('dynamic slot props', async () => {
  161. let props: any
  162. const bindObj = ref<Record<string, any>>({ foo: 1, baz: 'qux' })
  163. const Comp = defineVaporComponent(() =>
  164. createSlot('default', { $: [() => bindObj.value] }),
  165. )
  166. define(() =>
  167. createComponent(Comp, null, {
  168. default: (_props: any) => ((props = _props), []),
  169. }),
  170. ).render()
  171. expect(props).toEqual({ foo: 1, baz: 'qux' })
  172. bindObj.value.foo = 2
  173. await nextTick()
  174. expect(props).toEqual({ foo: 2, baz: 'qux' })
  175. delete bindObj.value.baz
  176. await nextTick()
  177. expect(props).toEqual({ foo: 2 })
  178. })
  179. test('dynamic slot props with static slot props', async () => {
  180. let props: any
  181. const foo = ref(0)
  182. const bindObj = ref<Record<string, any>>({ foo: 100, baz: 'qux' })
  183. const Comp = defineVaporComponent(() =>
  184. createSlot('default', {
  185. foo: () => foo.value,
  186. $: [() => bindObj.value],
  187. }),
  188. )
  189. define(() =>
  190. createComponent(Comp, null, {
  191. default: (_props: any) => ((props = _props), []),
  192. }),
  193. ).render()
  194. expect(props).toEqual({ foo: 100, baz: 'qux' })
  195. foo.value = 2
  196. await nextTick()
  197. expect(props).toEqual({ foo: 100, baz: 'qux' })
  198. delete bindObj.value.foo
  199. await nextTick()
  200. expect(props).toEqual({ foo: 2, baz: 'qux' })
  201. })
  202. test('dynamic slot should be rendered correctly with slot props', async () => {
  203. const val = ref('header')
  204. const Comp = defineVaporComponent(() => {
  205. const n0 = template('<div></div>')()
  206. prepend(
  207. n0 as any as ParentNode,
  208. createSlot('header', { title: () => val.value }),
  209. )
  210. return n0
  211. })
  212. const { host } = define(() => {
  213. // dynamic slot
  214. return createComponent(Comp, null, {
  215. $: [
  216. () => ({
  217. name: 'header',
  218. fn: (props: any) => {
  219. const el = template('<h1></h1>')()
  220. renderEffect(() => {
  221. setElementText(el, props.title)
  222. })
  223. return el
  224. },
  225. }),
  226. ],
  227. })
  228. }).render()
  229. expect(host.innerHTML).toBe('<div><h1>header</h1><!--slot--></div>')
  230. val.value = 'footer'
  231. await nextTick()
  232. expect(host.innerHTML).toBe('<div><h1>footer</h1><!--slot--></div>')
  233. })
  234. test('dynamic slot outlet should be render correctly with slot props', async () => {
  235. const val = ref('header')
  236. const Comp = defineVaporComponent(() => {
  237. const n0 = template('<div></div>')()
  238. prepend(
  239. n0 as any as ParentNode,
  240. createSlot(
  241. () => val.value, // dynamic slot outlet name
  242. ),
  243. )
  244. return n0
  245. })
  246. const { host } = define(() => {
  247. return createComponent(Comp, null, {
  248. header: () => template('header')(),
  249. footer: () => template('footer')(),
  250. })
  251. }).render()
  252. expect(host.innerHTML).toBe('<div>header<!--slot--></div>')
  253. val.value = 'footer'
  254. await nextTick()
  255. expect(host.innerHTML).toBe('<div>footer<!--slot--></div>')
  256. })
  257. test('fallback should be render correctly', () => {
  258. const Comp = defineVaporComponent(() => {
  259. const n0 = template('<div></div>')()
  260. insert(
  261. createSlot('header', undefined, () => template('fallback')()),
  262. n0 as any as ParentNode,
  263. )
  264. return n0
  265. })
  266. const { host } = define(() => {
  267. return createComponent(Comp, {}, {})
  268. }).render()
  269. expect(host.innerHTML).toBe('<div>fallback<!--slot--></div>')
  270. })
  271. test('dynamic slot should be updated correctly', async () => {
  272. const flag1 = ref(true)
  273. const Child = defineVaporComponent(() => {
  274. const temp0 = template('<p></p>')
  275. const el0 = temp0()
  276. const el1 = temp0()
  277. const slot1 = createSlot('one', null, () => template('one fallback')())
  278. const slot2 = createSlot('two', null, () => template('two fallback')())
  279. insert(slot1, el0 as any as ParentNode)
  280. insert(slot2, el1 as any as ParentNode)
  281. return [el0, el1]
  282. })
  283. const { host } = define(() => {
  284. return createComponent(Child, null, {
  285. $: [
  286. () =>
  287. flag1.value
  288. ? {
  289. name: 'one',
  290. fn: () => template('one content')(),
  291. }
  292. : {
  293. name: 'two',
  294. fn: () => template('two content')(),
  295. },
  296. ],
  297. })
  298. }).render()
  299. expect(host.innerHTML).toBe(
  300. '<p>one content<!--slot--></p><p>two fallback<!--slot--></p>',
  301. )
  302. flag1.value = false
  303. await nextTick()
  304. expect(host.innerHTML).toBe(
  305. '<p>one fallback<!--slot--></p><p>two content<!--slot--></p>',
  306. )
  307. flag1.value = true
  308. await nextTick()
  309. expect(host.innerHTML).toBe(
  310. '<p>one content<!--slot--></p><p>two fallback<!--slot--></p>',
  311. )
  312. })
  313. test('dynamic slot outlet should be updated correctly', async () => {
  314. const slotOutletName = ref('one')
  315. const Child = defineVaporComponent(() => {
  316. const temp0 = template('<p>')
  317. const el0 = temp0()
  318. const slot1 = createSlot(
  319. () => slotOutletName.value,
  320. undefined,
  321. () => template('fallback')(),
  322. )
  323. insert(slot1, el0 as any as ParentNode)
  324. return el0
  325. })
  326. const { host } = define(() => {
  327. return createComponent(
  328. Child,
  329. {},
  330. {
  331. one: () => template('one content')(),
  332. two: () => template('two content')(),
  333. },
  334. )
  335. }).render()
  336. expect(host.innerHTML).toBe('<p>one content<!--slot--></p>')
  337. slotOutletName.value = 'two'
  338. await nextTick()
  339. expect(host.innerHTML).toBe('<p>two content<!--slot--></p>')
  340. slotOutletName.value = 'none'
  341. await nextTick()
  342. expect(host.innerHTML).toBe('<p>fallback<!--slot--></p>')
  343. })
  344. test('non-exist slot', async () => {
  345. const Child = defineVaporComponent(() => {
  346. const el0 = template('<p>')()
  347. const slot = createSlot('not-exist', undefined)
  348. insert(slot, el0 as any as ParentNode)
  349. return el0
  350. })
  351. const { host } = define(() => {
  352. return createComponent(Child)
  353. }).render()
  354. expect(host.innerHTML).toBe('<p><!--slot--></p>')
  355. })
  356. test('use fallback when inner content changes', async () => {
  357. const Child = {
  358. setup() {
  359. return createSlot('default', null, () =>
  360. document.createTextNode('fallback'),
  361. )
  362. },
  363. }
  364. const toggle = ref(true)
  365. const { html } = define({
  366. setup() {
  367. return createComponent(Child, null, {
  368. default: () => {
  369. return createIf(
  370. () => toggle.value,
  371. () => {
  372. return document.createTextNode('content')
  373. },
  374. )
  375. },
  376. })
  377. },
  378. }).render()
  379. expect(html()).toBe('content<!--if--><!--slot-->')
  380. toggle.value = false
  381. await nextTick()
  382. expect(html()).toBe('fallback<!--if--><!--slot-->')
  383. toggle.value = true
  384. await nextTick()
  385. expect(html()).toBe('content<!--if--><!--slot-->')
  386. })
  387. test('use fallback on initial render', async () => {
  388. const Child = {
  389. setup() {
  390. return createSlot('default', null, () =>
  391. document.createTextNode('fallback'),
  392. )
  393. },
  394. }
  395. const toggle = ref(false)
  396. const { html } = define({
  397. setup() {
  398. return createComponent(Child, null, {
  399. default: () => {
  400. return createIf(
  401. () => toggle.value,
  402. () => {
  403. return document.createTextNode('content')
  404. },
  405. )
  406. },
  407. })
  408. },
  409. }).render()
  410. expect(html()).toBe('fallback<!--if--><!--slot-->')
  411. toggle.value = true
  412. await nextTick()
  413. expect(html()).toBe('content<!--if--><!--slot-->')
  414. toggle.value = false
  415. await nextTick()
  416. expect(html()).toBe('fallback<!--if--><!--slot-->')
  417. })
  418. test('dynamic slot work with v-if', async () => {
  419. const val = ref('header')
  420. const toggle = ref(false)
  421. const Comp = defineVaporComponent(() => {
  422. const n0 = template('<div></div>')()
  423. prepend(n0 as any as ParentNode, createSlot('header', null))
  424. return n0
  425. })
  426. const { host } = define(() => {
  427. // dynamic slot
  428. return createComponent(Comp, null, {
  429. $: [
  430. () =>
  431. (toggle.value
  432. ? {
  433. name: val.value,
  434. fn: () => {
  435. return template('<h1></h1>')()
  436. },
  437. }
  438. : void 0) as DynamicSlot,
  439. ],
  440. })
  441. }).render()
  442. expect(host.innerHTML).toBe('<div><!--slot--></div>')
  443. toggle.value = true
  444. await nextTick()
  445. expect(host.innerHTML).toBe('<div><h1></h1><!--slot--></div>')
  446. })
  447. test('slots proxy ownKeys trap correctly reflects dynamic slot presence', async () => {
  448. const val = ref('header')
  449. const toggle = ref(false)
  450. let instance: any
  451. const Comp = defineVaporComponent(() => {
  452. instance = currentInstance
  453. const n0 = template('<div></div>')()
  454. prepend(n0 as any as ParentNode, createSlot('header', null))
  455. return n0
  456. })
  457. define(() => {
  458. // dynamic slot
  459. return createComponent(Comp, null, {
  460. $: [
  461. () =>
  462. (toggle.value
  463. ? {
  464. name: val.value,
  465. fn: () => {
  466. return template('<h1></h1>')()
  467. },
  468. }
  469. : void 0) as DynamicSlot,
  470. ],
  471. })
  472. }).render()
  473. expect(Reflect.ownKeys(instance.slots)).not.toContain('header')
  474. toggle.value = true
  475. await nextTick()
  476. expect(Reflect.ownKeys(instance.slots)).toContain('header')
  477. toggle.value = false
  478. await nextTick()
  479. expect(Reflect.ownKeys(instance.slots)).not.toContain('header')
  480. })
  481. test('render fallback when slot content is not valid', async () => {
  482. const Child = {
  483. setup() {
  484. return createSlot('default', null, () =>
  485. document.createTextNode('fallback'),
  486. )
  487. },
  488. }
  489. const { html } = define({
  490. setup() {
  491. return createComponent(Child, null, {
  492. default: () => {
  493. return template('<!--comment-->')()
  494. },
  495. })
  496. },
  497. }).render()
  498. expect(html()).toBe('fallback<!--slot-->')
  499. })
  500. test('render fallback when v-if condition is false', async () => {
  501. const Child = {
  502. setup() {
  503. return createSlot('default', null, () =>
  504. document.createTextNode('fallback'),
  505. )
  506. },
  507. }
  508. const toggle = ref(false)
  509. const { html } = define({
  510. setup() {
  511. return createComponent(Child, null, {
  512. default: () => {
  513. return createIf(
  514. () => toggle.value,
  515. () => {
  516. return document.createTextNode('content')
  517. },
  518. )
  519. },
  520. })
  521. },
  522. }).render()
  523. expect(html()).toBe('fallback<!--if--><!--slot-->')
  524. toggle.value = true
  525. await nextTick()
  526. expect(html()).toBe('content<!--if--><!--slot-->')
  527. toggle.value = false
  528. await nextTick()
  529. expect(html()).toBe('fallback<!--if--><!--slot-->')
  530. })
  531. test('render fallback with nested v-if', async () => {
  532. const Child = {
  533. setup() {
  534. return createSlot('default', null, () =>
  535. document.createTextNode('fallback'),
  536. )
  537. },
  538. }
  539. const outerShow = ref(false)
  540. const innerShow = ref(false)
  541. const { html } = define({
  542. setup() {
  543. return createComponent(Child, null, {
  544. default: () => {
  545. return createIf(
  546. () => outerShow.value,
  547. () => {
  548. return createIf(
  549. () => innerShow.value,
  550. () => {
  551. return document.createTextNode('content')
  552. },
  553. )
  554. },
  555. )
  556. },
  557. })
  558. },
  559. }).render()
  560. expect(html()).toBe('fallback<!--if--><!--slot-->')
  561. outerShow.value = true
  562. await nextTick()
  563. expect(html()).toBe('fallback<!--if--><!--if--><!--slot-->')
  564. innerShow.value = true
  565. await nextTick()
  566. expect(html()).toBe('content<!--if--><!--if--><!--slot-->')
  567. innerShow.value = false
  568. await nextTick()
  569. expect(html()).toBe('fallback<!--if--><!--if--><!--slot-->')
  570. outerShow.value = false
  571. await nextTick()
  572. expect(html()).toBe('fallback<!--if--><!--slot-->')
  573. outerShow.value = true
  574. await nextTick()
  575. expect(html()).toBe('fallback<!--if--><!--if--><!--slot-->')
  576. innerShow.value = true
  577. await nextTick()
  578. expect(html()).toBe('content<!--if--><!--if--><!--slot-->')
  579. })
  580. test('render fallback with v-for', async () => {
  581. const Child = {
  582. setup() {
  583. return createSlot('default', null, () =>
  584. document.createTextNode('fallback'),
  585. )
  586. },
  587. }
  588. const items = ref<number[]>([1])
  589. const { html } = define({
  590. setup() {
  591. return createComponent(Child, null, {
  592. default: () => {
  593. const n2 = createFor(
  594. () => items.value,
  595. for_item0 => {
  596. const n4 = template('<span> </span>')() as any
  597. const x4 = child(n4) as any
  598. renderEffect(() =>
  599. setText(x4, toDisplayString(for_item0.value)),
  600. )
  601. return n4
  602. },
  603. )
  604. return n2
  605. },
  606. })
  607. },
  608. }).render()
  609. expect(html()).toBe('<span>1</span><!--for--><!--slot-->')
  610. items.value.pop()
  611. await nextTick()
  612. expect(html()).toBe('fallback<!--for--><!--slot-->')
  613. items.value.pop()
  614. await nextTick()
  615. expect(html()).toBe('fallback<!--for--><!--slot-->')
  616. items.value.push(2)
  617. await nextTick()
  618. expect(html()).toBe('<span>2</span><!--for--><!--slot-->')
  619. })
  620. test('render fallback with v-for (empty source)', async () => {
  621. const Child = {
  622. setup() {
  623. return createSlot('default', null, () =>
  624. document.createTextNode('fallback'),
  625. )
  626. },
  627. }
  628. const items = ref<number[]>([])
  629. const { html } = define({
  630. setup() {
  631. return createComponent(Child, null, {
  632. default: () => {
  633. const n2 = createFor(
  634. () => items.value,
  635. for_item0 => {
  636. const n4 = template('<span> </span>')() as any
  637. const x4 = child(n4) as any
  638. renderEffect(() =>
  639. setText(x4, toDisplayString(for_item0.value)),
  640. )
  641. return n4
  642. },
  643. )
  644. return n2
  645. },
  646. })
  647. },
  648. }).render()
  649. expect(html()).toBe('fallback<!--for--><!--slot-->')
  650. items.value.push(1)
  651. await nextTick()
  652. expect(html()).toBe('<span>1</span><!--for--><!--slot-->')
  653. items.value.pop()
  654. await nextTick()
  655. expect(html()).toBe('fallback<!--for--><!--slot-->')
  656. items.value.pop()
  657. await nextTick()
  658. expect(html()).toBe('fallback<!--for--><!--slot-->')
  659. items.value.push(2)
  660. await nextTick()
  661. expect(html()).toBe('<span>2</span><!--for--><!--slot-->')
  662. })
  663. test('work with v-once', async () => {
  664. const Child = defineVaporComponent({
  665. setup() {
  666. return createSlot(
  667. 'default',
  668. null,
  669. undefined,
  670. undefined,
  671. true /* once */,
  672. )
  673. },
  674. })
  675. const count = ref(0)
  676. const { html } = define({
  677. setup() {
  678. return createComponent(Child, null, {
  679. default: () => {
  680. const n3 = template('<div> </div>')() as any
  681. const x3 = txt(n3) as any
  682. renderEffect(() => setText(x3, toDisplayString(count.value)))
  683. return n3
  684. },
  685. })
  686. },
  687. }).render()
  688. expect(html()).toBe('<div>0</div><!--slot-->')
  689. // expect no changes due to v-once
  690. count.value++
  691. await nextTick()
  692. expect(html()).toBe('<div>0</div><!--slot-->')
  693. })
  694. })
  695. describe('forwarded slot', () => {
  696. test('should work', async () => {
  697. const Child = defineVaporComponent({
  698. setup() {
  699. return createSlot('foo', null)
  700. },
  701. })
  702. const Parent = defineVaporComponent({
  703. setup() {
  704. const n2 = createComponent(
  705. Child,
  706. null,
  707. {
  708. foo: withVaporCtx(() => {
  709. return createSlot('foo', null)
  710. }),
  711. },
  712. true,
  713. )
  714. return n2
  715. },
  716. })
  717. const foo = ref('foo')
  718. const { host } = define({
  719. setup() {
  720. const n2 = createComponent(
  721. Parent,
  722. null,
  723. {
  724. foo: () => {
  725. const n0 = template(' ')() as any
  726. renderEffect(() => setText(n0, foo.value))
  727. return n0
  728. },
  729. },
  730. true,
  731. )
  732. return n2
  733. },
  734. }).render()
  735. expect(host.innerHTML).toBe('foo<!--slot--><!--slot-->')
  736. foo.value = 'bar'
  737. await nextTick()
  738. expect(host.innerHTML).toBe('bar<!--slot--><!--slot-->')
  739. })
  740. test('mixed with non-forwarded slot', async () => {
  741. const Child = defineVaporComponent({
  742. setup() {
  743. return [createSlot('foo', null)]
  744. },
  745. })
  746. const Parent = defineVaporComponent({
  747. setup() {
  748. const n2 = createComponent(Child, null, {
  749. foo: withVaporCtx(() => {
  750. const n0 = createSlot('foo', null)
  751. return n0
  752. }),
  753. })
  754. const n3 = createSlot('default', null)
  755. return [n2, n3]
  756. },
  757. })
  758. const foo = ref('foo')
  759. const { host } = define({
  760. setup() {
  761. const n2 = createComponent(
  762. Parent,
  763. null,
  764. {
  765. foo: () => {
  766. const n0 = template(' ')() as any
  767. renderEffect(() => setText(n0, foo.value))
  768. return n0
  769. },
  770. default: () => {
  771. const n3 = template(' ')() as any
  772. renderEffect(() => setText(n3, foo.value))
  773. return n3
  774. },
  775. },
  776. true,
  777. )
  778. return n2
  779. },
  780. }).render()
  781. expect(host.innerHTML).toBe('foo<!--slot--><!--slot-->foo<!--slot-->')
  782. foo.value = 'bar'
  783. await nextTick()
  784. expect(host.innerHTML).toBe('bar<!--slot--><!--slot-->bar<!--slot-->')
  785. })
  786. test('forwarded slot with fallback', async () => {
  787. const Child = defineVaporComponent({
  788. setup() {
  789. return createSlot('default', null, () => template('child fallback')())
  790. },
  791. })
  792. const Parent = defineVaporComponent({
  793. setup() {
  794. const n2 = createComponent(Child, null, {
  795. default: withVaporCtx(() => {
  796. const n0 = createSlot('default', null, () => {
  797. return template('<!-- <div></div> -->')()
  798. })
  799. return n0
  800. }),
  801. })
  802. return n2
  803. },
  804. })
  805. const { html } = define({
  806. setup() {
  807. return createComponent(Parent, null, {
  808. default: () => template('<!-- <div>App</div> -->')(),
  809. })
  810. },
  811. }).render()
  812. expect(html()).toBe('child fallback<!--slot--><!--slot-->')
  813. })
  814. test('named forwarded slot with v-if', async () => {
  815. const Child = defineVaporComponent({
  816. setup() {
  817. return createSlot('default', null)
  818. },
  819. })
  820. const Parent = defineVaporComponent({
  821. props: {
  822. show: Boolean,
  823. },
  824. setup(props) {
  825. const n6 = createComponent(
  826. Child,
  827. null,
  828. {
  829. default: withVaporCtx(() => {
  830. const n0 = createIf(
  831. () => props.show,
  832. () => {
  833. const n5 = template('<div></div>')() as any
  834. setInsertionState(n5, null, true)
  835. createSlot('header', null, () => {
  836. const n4 = template('default header')()
  837. return n4
  838. })
  839. return n5
  840. },
  841. )
  842. return n0
  843. }),
  844. },
  845. true,
  846. )
  847. return n6
  848. },
  849. })
  850. const show = ref(false)
  851. const { html } = define({
  852. setup() {
  853. return createComponent(
  854. Parent,
  855. {
  856. show: () => show.value,
  857. },
  858. {
  859. header: () => template('custom header')(),
  860. },
  861. )
  862. },
  863. }).render()
  864. expect(html()).toBe('<!--if--><!--slot-->')
  865. show.value = true
  866. await nextTick()
  867. expect(html()).toBe(
  868. '<div>custom header<!--slot--></div><!--if--><!--slot-->',
  869. )
  870. show.value = false
  871. await nextTick()
  872. expect(html()).toBe('<!--if--><!--slot-->')
  873. })
  874. test('forwarded slot with fallback (v-if)', async () => {
  875. const Child = defineVaporComponent({
  876. setup() {
  877. return createSlot('default', null, () => template('child fallback')())
  878. },
  879. })
  880. const show = ref(false)
  881. const Parent = defineVaporComponent({
  882. setup() {
  883. const n2 = createComponent(Child, null, {
  884. default: withVaporCtx(() => {
  885. const n0 = createSlot('default', null, () => {
  886. const n2 = createIf(
  887. () => show.value,
  888. () => {
  889. const n4 = template('<div>if content</div>')()
  890. return n4
  891. },
  892. )
  893. return n2
  894. })
  895. return n0
  896. }),
  897. })
  898. return n2
  899. },
  900. })
  901. const { html } = define({
  902. setup() {
  903. return createComponent(Parent, null, {
  904. default: () => template('<!-- <div>App</div> -->')(),
  905. })
  906. },
  907. }).render()
  908. expect(html()).toBe('child fallback<!--if--><!--slot--><!--slot-->')
  909. show.value = true
  910. await nextTick()
  911. expect(html()).toBe(
  912. '<div>if content</div><!--if--><!--slot--><!--slot-->',
  913. )
  914. })
  915. test('forwarded slot with fallback (v-for)', async () => {
  916. const Child = defineVaporComponent({
  917. setup() {
  918. return createSlot('default', null, () => template('child fallback')())
  919. },
  920. })
  921. const items = ref<number[]>([])
  922. const Parent = defineVaporComponent({
  923. setup() {
  924. const n2 = createComponent(Child, null, {
  925. default: withVaporCtx(() => {
  926. const n0 = createSlot('default', null, () => {
  927. const n2 = createFor(
  928. () => items.value,
  929. for_item0 => {
  930. const n4 = template('<span> </span>')() as any
  931. const x4 = child(n4) as any
  932. renderEffect(() =>
  933. setText(x4, toDisplayString(for_item0.value)),
  934. )
  935. return n4
  936. },
  937. )
  938. return n2
  939. })
  940. return n0
  941. }),
  942. })
  943. return n2
  944. },
  945. })
  946. const { html } = define({
  947. setup() {
  948. return createComponent(Parent, null, {
  949. default: () => template('<!-- <div>App</div> -->')(),
  950. })
  951. },
  952. }).render()
  953. expect(html()).toBe('child fallback<!--for--><!--slot--><!--slot-->')
  954. items.value.push(1)
  955. await nextTick()
  956. expect(html()).toBe('<span>1</span><!--for--><!--slot--><!--slot-->')
  957. items.value.pop()
  958. await nextTick()
  959. expect(html()).toBe('child fallback<!--for--><!--slot--><!--slot-->')
  960. })
  961. test('consecutive slots with insertion state', async () => {
  962. const { component: Child } = define({
  963. setup() {
  964. const n2 = template('<div><div>baz</div></div>', true)() as any
  965. setInsertionState(n2, 0)
  966. createSlot('default', null)
  967. setInsertionState(n2, 0)
  968. createSlot('foo', null)
  969. return n2
  970. },
  971. })
  972. const { html } = define({
  973. setup() {
  974. return createComponent(Child, null, {
  975. default: () => template('default')(),
  976. foo: () => template('foo')(),
  977. })
  978. },
  979. }).render()
  980. expect(html()).toBe(
  981. `<div>` +
  982. `default<!--slot-->` +
  983. `foo<!--slot-->` +
  984. `<div>baz</div>` +
  985. `</div>`,
  986. )
  987. })
  988. describe('vdom interop', () => {
  989. const createVaporSlot = (fallbackText = 'fallback') => {
  990. return defineVaporComponent({
  991. setup() {
  992. const n0 = createSlot('foo', null, () => {
  993. const n2 = template(`<div>${fallbackText}</div>`)()
  994. return n2
  995. })
  996. return n0
  997. },
  998. })
  999. }
  1000. const createVdomSlot = (fallbackText = 'fallback') => {
  1001. return {
  1002. render(this: any) {
  1003. return renderSlot(this.$slots, 'foo', {}, () => [
  1004. h('div', fallbackText),
  1005. ])
  1006. },
  1007. }
  1008. }
  1009. const createVaporForwardedSlot = (
  1010. targetComponent: any,
  1011. fallbackText?: string,
  1012. ) => {
  1013. return defineVaporComponent({
  1014. setup() {
  1015. const n2 = createComponent(
  1016. targetComponent,
  1017. null,
  1018. {
  1019. foo: withVaporCtx(() => {
  1020. return fallbackText
  1021. ? createSlot('foo', null, () => {
  1022. const n2 = template(`<div>${fallbackText}</div>`)()
  1023. return n2
  1024. })
  1025. : createSlot('foo', null)
  1026. }),
  1027. },
  1028. true,
  1029. )
  1030. return n2
  1031. },
  1032. })
  1033. }
  1034. const createVdomForwardedSlot = (
  1035. targetComponent: any,
  1036. fallbackText?: string,
  1037. ) => {
  1038. return {
  1039. render(this: any) {
  1040. return h(targetComponent, null, {
  1041. foo: () => [
  1042. fallbackText
  1043. ? renderSlot(this.$slots, 'foo', {}, () => [
  1044. h('div', fallbackText),
  1045. ])
  1046. : renderSlot(this.$slots, 'foo'),
  1047. ],
  1048. _: 3 /* FORWARDED */,
  1049. })
  1050. },
  1051. }
  1052. }
  1053. const createMultipleVaporForwardedSlots = (
  1054. targetComponent: any,
  1055. count: number,
  1056. ) => {
  1057. let current = targetComponent
  1058. for (let i = 0; i < count; i++) {
  1059. current = createVaporForwardedSlot(current)
  1060. }
  1061. return current
  1062. }
  1063. const createMultipleVdomForwardedSlots = (
  1064. targetComponent: any,
  1065. count: number,
  1066. ) => {
  1067. let current = targetComponent
  1068. for (let i = 0; i < count; i++) {
  1069. current = createVdomForwardedSlot(current)
  1070. }
  1071. return current
  1072. }
  1073. const createTestApp = (
  1074. rootComponent: any,
  1075. foo: Ref<string>,
  1076. show: Ref<boolean>,
  1077. ) => {
  1078. return {
  1079. setup() {
  1080. return () =>
  1081. h(
  1082. rootComponent,
  1083. null,
  1084. createSlots({ _: 2 /* DYNAMIC */ } as any, [
  1085. show.value
  1086. ? {
  1087. name: 'foo',
  1088. fn: () => [h('span', foo.value)],
  1089. key: '0',
  1090. }
  1091. : undefined,
  1092. ]),
  1093. )
  1094. },
  1095. }
  1096. }
  1097. const createEmptyTestApp = (rootComponent: any) => {
  1098. return {
  1099. setup() {
  1100. return () => h(rootComponent)
  1101. },
  1102. }
  1103. }
  1104. test('vdom slot > vapor forwarded slot > vapor slot', async () => {
  1105. const foo = ref('foo')
  1106. const show = ref(true)
  1107. const VaporSlot = createVaporSlot()
  1108. const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
  1109. const App = createTestApp(VaporForwardedSlot, foo, show)
  1110. const root = document.createElement('div')
  1111. createApp(App).use(vaporInteropPlugin).mount(root)
  1112. expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
  1113. foo.value = 'bar'
  1114. await nextTick()
  1115. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1116. show.value = false
  1117. await nextTick()
  1118. expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
  1119. })
  1120. test('vdom slot > vapor forwarded slot(with fallback) > vapor slot', async () => {
  1121. const foo = ref('foo')
  1122. const show = ref(true)
  1123. const VaporSlot = createVaporSlot()
  1124. const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
  1125. VaporSlot,
  1126. 'forwarded fallback',
  1127. )
  1128. const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
  1129. const root = document.createElement('div')
  1130. createApp(App).use(vaporInteropPlugin).mount(root)
  1131. expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
  1132. foo.value = 'bar'
  1133. await nextTick()
  1134. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1135. show.value = false
  1136. await nextTick()
  1137. expect(root.innerHTML).toBe('<div>forwarded fallback</div><!--slot-->')
  1138. })
  1139. test('vdom slot > vapor forwarded slot > vdom slot', async () => {
  1140. const foo = ref('foo')
  1141. const show = ref(true)
  1142. const VdomSlot = createVdomSlot()
  1143. const VaporForwardedSlot = createVaporForwardedSlot(VdomSlot)
  1144. const App = createTestApp(VaporForwardedSlot, foo, show)
  1145. const root = document.createElement('div')
  1146. createApp(App).use(vaporInteropPlugin).mount(root)
  1147. expect(root.innerHTML).toBe('<span>foo</span>')
  1148. foo.value = 'bar'
  1149. await nextTick()
  1150. expect(root.innerHTML).toBe('<span>bar</span>')
  1151. show.value = false
  1152. await nextTick()
  1153. expect(root.innerHTML).toBe('<div>fallback</div>')
  1154. show.value = true
  1155. await nextTick()
  1156. expect(root.innerHTML).toBe('<span>bar</span>')
  1157. })
  1158. test('vdom slot > vapor forwarded slot(with fallback) > vdom slot', async () => {
  1159. const foo = ref('foo')
  1160. const show = ref(true)
  1161. const VdomSlot = createVdomSlot()
  1162. const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
  1163. VdomSlot,
  1164. 'forwarded fallback',
  1165. )
  1166. const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
  1167. const root = document.createElement('div')
  1168. createApp(App).use(vaporInteropPlugin).mount(root)
  1169. expect(root.innerHTML).toBe('<span>foo</span>')
  1170. foo.value = 'bar'
  1171. await nextTick()
  1172. expect(root.innerHTML).toBe('<span>bar</span>')
  1173. show.value = false
  1174. await nextTick()
  1175. expect(root.innerHTML).toBe('<div>forwarded fallback</div>')
  1176. })
  1177. test('vdom slot > vapor forwarded slot > vdom forwarded slot > vapor slot', async () => {
  1178. const foo = ref('foo')
  1179. const show = ref(true)
  1180. const VaporSlot = createVaporSlot()
  1181. const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
  1182. const VaporForwardedSlot = createVaporForwardedSlot(VdomForwardedSlot)
  1183. const App = createTestApp(VaporForwardedSlot, foo, show)
  1184. const root = document.createElement('div')
  1185. createApp(App).use(vaporInteropPlugin).mount(root)
  1186. expect(root.innerHTML).toBe('<span>foo</span>')
  1187. foo.value = 'bar'
  1188. await nextTick()
  1189. expect(root.innerHTML).toBe('<span>bar</span>')
  1190. show.value = false
  1191. await nextTick()
  1192. expect(root.innerHTML).toBe('<div>fallback</div>')
  1193. show.value = true
  1194. await nextTick()
  1195. expect(root.innerHTML).toBe('<span>bar</span>')
  1196. })
  1197. test('vdom slot > vapor forwarded slot(with fallback) > vdom forwarded slot > vapor slot', async () => {
  1198. const foo = ref('foo')
  1199. const show = ref(true)
  1200. const VaporSlot = createVaporSlot()
  1201. const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
  1202. const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
  1203. VdomForwardedSlot,
  1204. 'forwarded fallback',
  1205. )
  1206. const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
  1207. const root = document.createElement('div')
  1208. createApp(App).use(vaporInteropPlugin).mount(root)
  1209. expect(root.innerHTML).toBe('<span>foo</span>')
  1210. foo.value = 'bar'
  1211. await nextTick()
  1212. expect(root.innerHTML).toBe('<span>bar</span>')
  1213. show.value = false
  1214. await nextTick()
  1215. expect(root.innerHTML).toBe('<div>forwarded fallback</div>')
  1216. show.value = true
  1217. await nextTick()
  1218. expect(root.innerHTML).toBe('<span>bar</span>')
  1219. })
  1220. test('vdom slot > vapor forwarded slot > vdom forwarded slot(with fallback) > vapor slot', async () => {
  1221. const foo = ref('foo')
  1222. const show = ref(true)
  1223. const VaporSlot = createVaporSlot()
  1224. const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
  1225. VaporSlot,
  1226. 'vdom fallback',
  1227. )
  1228. const VaporForwardedSlot = createVaporForwardedSlot(
  1229. VdomForwardedSlotWithFallback,
  1230. )
  1231. const App = createTestApp(VaporForwardedSlot, foo, show)
  1232. const root = document.createElement('div')
  1233. createApp(App).use(vaporInteropPlugin).mount(root)
  1234. expect(root.innerHTML).toBe('<span>foo</span>')
  1235. foo.value = 'bar'
  1236. await nextTick()
  1237. expect(root.innerHTML).toBe('<span>bar</span>')
  1238. show.value = false
  1239. await nextTick()
  1240. expect(root.innerHTML).toBe('<div>vdom fallback</div>')
  1241. show.value = true
  1242. await nextTick()
  1243. expect(root.innerHTML).toBe('<span>bar</span>')
  1244. })
  1245. test('vdom slot(empty) > vapor forwarded slot > vdom forwarded slot(with fallback) > vapor slot', async () => {
  1246. const VaporSlot = createVaporSlot()
  1247. const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
  1248. VaporSlot,
  1249. 'vdom fallback',
  1250. )
  1251. const VaporForwardedSlot = createVaporForwardedSlot(
  1252. VdomForwardedSlotWithFallback,
  1253. )
  1254. const App = createEmptyTestApp(VaporForwardedSlot)
  1255. const root = document.createElement('div')
  1256. createApp(App).use(vaporInteropPlugin).mount(root)
  1257. expect(root.innerHTML).toBe('<div>vdom fallback</div>')
  1258. })
  1259. test('vdom slot > vapor forwarded slot > vdom forwarded slot > vdom slot', async () => {
  1260. const foo = ref('foo')
  1261. const show = ref(true)
  1262. const VdomSlot = createVdomSlot()
  1263. const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
  1264. const VaporForwardedSlot = createVaporForwardedSlot(VdomForwardedSlot)
  1265. const App = createTestApp(VaporForwardedSlot, foo, show)
  1266. const root = document.createElement('div')
  1267. createApp(App).use(vaporInteropPlugin).mount(root)
  1268. expect(root.innerHTML).toBe('<span>foo</span>')
  1269. foo.value = 'bar'
  1270. await nextTick()
  1271. expect(root.innerHTML).toBe('<span>bar</span>')
  1272. show.value = false
  1273. await nextTick()
  1274. expect(root.innerHTML).toBe('<div>fallback</div>')
  1275. show.value = true
  1276. await nextTick()
  1277. expect(root.innerHTML).toBe('<span>bar</span>')
  1278. })
  1279. test('vdom slot > vapor forwarded slot(with fallback) > vdom forwarded slot > vdom slot', async () => {
  1280. const foo = ref('foo')
  1281. const show = ref(true)
  1282. const VdomSlot = createVdomSlot()
  1283. const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
  1284. const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
  1285. VdomForwardedSlot,
  1286. 'vapor fallback',
  1287. )
  1288. const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
  1289. const root = document.createElement('div')
  1290. createApp(App).use(vaporInteropPlugin).mount(root)
  1291. expect(root.innerHTML).toBe('<span>foo</span>')
  1292. foo.value = 'bar'
  1293. await nextTick()
  1294. expect(root.innerHTML).toBe('<span>bar</span>')
  1295. show.value = false
  1296. await nextTick()
  1297. expect(root.innerHTML).toBe('<div>vapor fallback</div>')
  1298. show.value = true
  1299. await nextTick()
  1300. expect(root.innerHTML).toBe('<span>bar</span>')
  1301. })
  1302. test('vdom slot > vapor forwarded slot > vdom forwarded slot(with fallback) > vdom slot', async () => {
  1303. const foo = ref('foo')
  1304. const show = ref(true)
  1305. const VdomSlot = createVdomSlot()
  1306. const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
  1307. VdomSlot,
  1308. 'vdom fallback',
  1309. )
  1310. const VaporForwardedSlot = createVaporForwardedSlot(
  1311. VdomForwardedSlotWithFallback,
  1312. )
  1313. const App = createTestApp(VaporForwardedSlot, foo, show)
  1314. const root = document.createElement('div')
  1315. createApp(App).use(vaporInteropPlugin).mount(root)
  1316. expect(root.innerHTML).toBe('<span>foo</span>')
  1317. foo.value = 'bar'
  1318. await nextTick()
  1319. expect(root.innerHTML).toBe('<span>bar</span>')
  1320. show.value = false
  1321. await nextTick()
  1322. expect(root.innerHTML).toBe('<div>vdom fallback</div>')
  1323. show.value = true
  1324. await nextTick()
  1325. expect(root.innerHTML).toBe('<span>bar</span>')
  1326. })
  1327. test('vdom slot > vapor forwarded slot (multiple) > vdom forwarded slot > vdom slot', async () => {
  1328. const foo = ref('foo')
  1329. const show = ref(true)
  1330. const VdomSlot = createVdomSlot()
  1331. const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
  1332. const VaporForwardedSlot = createMultipleVaporForwardedSlots(
  1333. VdomForwardedSlot,
  1334. 3,
  1335. )
  1336. const App = createTestApp(VaporForwardedSlot, foo, show)
  1337. const root = document.createElement('div')
  1338. createApp(App).use(vaporInteropPlugin).mount(root)
  1339. expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
  1340. foo.value = 'bar'
  1341. await nextTick()
  1342. expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
  1343. show.value = false
  1344. await nextTick()
  1345. expect(root.innerHTML).toBe('<div>fallback</div><!--slot--><!--slot-->')
  1346. show.value = true
  1347. await nextTick()
  1348. expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
  1349. })
  1350. test('vdom slot > vapor forwarded slot (multiple) > vdom forwarded slot(with fallback) > vdom slot', async () => {
  1351. const foo = ref('foo')
  1352. const show = ref(true)
  1353. const VdomSlot = createVdomSlot()
  1354. const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
  1355. VdomSlot,
  1356. 'vdom fallback',
  1357. )
  1358. const VaporForwardedSlot = createMultipleVaporForwardedSlots(
  1359. VdomForwardedSlotWithFallback,
  1360. 3,
  1361. )
  1362. const App = createTestApp(VaporForwardedSlot, foo, show)
  1363. const root = document.createElement('div')
  1364. createApp(App).use(vaporInteropPlugin).mount(root)
  1365. expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
  1366. foo.value = 'bar'
  1367. await nextTick()
  1368. expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
  1369. show.value = false
  1370. await nextTick()
  1371. expect(root.innerHTML).toBe(
  1372. '<div>vdom fallback</div><!--slot--><!--slot-->',
  1373. )
  1374. show.value = true
  1375. await nextTick()
  1376. expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
  1377. })
  1378. test('vdom slot > vdom forwarded slot > vapor slot', async () => {
  1379. const foo = ref('foo')
  1380. const show = ref(true)
  1381. const VaporSlot = createVaporSlot()
  1382. const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
  1383. const App = createTestApp(VdomForwardedSlot, foo, show)
  1384. const root = document.createElement('div')
  1385. createApp(App).use(vaporInteropPlugin).mount(root)
  1386. expect(root.innerHTML).toBe('<span>foo</span>')
  1387. foo.value = 'bar'
  1388. await nextTick()
  1389. expect(root.innerHTML).toBe('<span>bar</span>')
  1390. show.value = false
  1391. await nextTick()
  1392. expect(root.innerHTML).toBe('<div>fallback</div>')
  1393. show.value = true
  1394. await nextTick()
  1395. expect(root.innerHTML).toBe('<span>bar</span>')
  1396. })
  1397. test('vdom slot > vdom forwarded slot > vapor forwarded slot > vapor slot', async () => {
  1398. const foo = ref('foo')
  1399. const show = ref(true)
  1400. const VaporSlot = createVaporSlot()
  1401. const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
  1402. const VdomForwardedSlot = createVdomForwardedSlot(VaporForwardedSlot)
  1403. const App = createTestApp(VdomForwardedSlot, foo, show)
  1404. const root = document.createElement('div')
  1405. createApp(App).use(vaporInteropPlugin).mount(root)
  1406. expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
  1407. foo.value = 'bar'
  1408. await nextTick()
  1409. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1410. show.value = false
  1411. await nextTick()
  1412. expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
  1413. show.value = true
  1414. await nextTick()
  1415. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1416. })
  1417. test('vdom slot > vdom forwarded slot (multiple) > vapor forwarded slot > vdom slot', async () => {
  1418. const foo = ref('foo')
  1419. const show = ref(true)
  1420. const VaporSlot = createVaporSlot()
  1421. const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
  1422. const VdomForwardedSlot = createMultipleVdomForwardedSlots(
  1423. VaporForwardedSlot,
  1424. 3,
  1425. )
  1426. const App = createTestApp(VdomForwardedSlot, foo, show)
  1427. const root = document.createElement('div')
  1428. createApp(App).use(vaporInteropPlugin).mount(root)
  1429. expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
  1430. foo.value = 'bar'
  1431. await nextTick()
  1432. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1433. show.value = false
  1434. await nextTick()
  1435. expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
  1436. show.value = true
  1437. await nextTick()
  1438. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1439. })
  1440. test('vdom slot > vdom forwarded slot (multiple) > vapor forwarded slot(with fallback) > vdom slot', async () => {
  1441. const foo = ref('foo')
  1442. const show = ref(true)
  1443. const VaporSlot = createVaporSlot()
  1444. const VaporForwardedSlot = createVaporForwardedSlot(
  1445. VaporSlot,
  1446. 'vapor fallback',
  1447. )
  1448. const VdomForwardedSlot = createMultipleVdomForwardedSlots(
  1449. VaporForwardedSlot,
  1450. 3,
  1451. )
  1452. const App = createTestApp(VdomForwardedSlot, foo, show)
  1453. const root = document.createElement('div')
  1454. createApp(App).use(vaporInteropPlugin).mount(root)
  1455. expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
  1456. foo.value = 'bar'
  1457. await nextTick()
  1458. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1459. show.value = false
  1460. await nextTick()
  1461. expect(root.innerHTML).toBe('<div>vapor fallback</div><!--slot-->')
  1462. show.value = true
  1463. await nextTick()
  1464. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1465. })
  1466. test('vdom slot > vapor forwarded slot > vapor forwarded slot > vdom slot', async () => {
  1467. const foo = ref('foo')
  1468. const show = ref(true)
  1469. const VdomSlot = createVdomSlot()
  1470. const VaporForwardedSlot1 = createMultipleVaporForwardedSlots(
  1471. VdomSlot,
  1472. 2,
  1473. )
  1474. const App = createTestApp(VaporForwardedSlot1, foo, show)
  1475. const root = document.createElement('div')
  1476. createApp(App).use(vaporInteropPlugin).mount(root)
  1477. expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
  1478. foo.value = 'bar'
  1479. await nextTick()
  1480. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1481. show.value = false
  1482. await nextTick()
  1483. expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
  1484. show.value = true
  1485. await nextTick()
  1486. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1487. })
  1488. test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot > vdom slot', async () => {
  1489. const foo = ref('foo')
  1490. const show = ref(true)
  1491. const VdomSlot = createVdomSlot()
  1492. const VaporForwardedSlot2 = createVaporForwardedSlot(VdomSlot)
  1493. const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
  1494. VaporForwardedSlot2,
  1495. 'vapor1 fallback',
  1496. )
  1497. const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
  1498. const root = document.createElement('div')
  1499. createApp(App).use(vaporInteropPlugin).mount(root)
  1500. expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
  1501. foo.value = 'bar'
  1502. await nextTick()
  1503. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1504. show.value = false
  1505. await nextTick()
  1506. expect(root.innerHTML).toBe('<div>vapor1 fallback</div><!--slot-->')
  1507. show.value = true
  1508. await nextTick()
  1509. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1510. })
  1511. test('vdom slot > vapor forwarded slot > vapor forwarded slot(with fallback) > vdom slot', async () => {
  1512. const foo = ref('foo')
  1513. const show = ref(true)
  1514. const VdomSlot = createVdomSlot()
  1515. const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
  1516. VdomSlot,
  1517. 'vapor2 fallback',
  1518. )
  1519. const VaporForwardedSlot1 = createVaporForwardedSlot(
  1520. VaporForwardedSlot2WithFallback,
  1521. )
  1522. const App = createTestApp(VaporForwardedSlot1, foo, show)
  1523. const root = document.createElement('div')
  1524. createApp(App).use(vaporInteropPlugin).mount(root)
  1525. expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
  1526. foo.value = 'bar'
  1527. await nextTick()
  1528. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1529. show.value = false
  1530. await nextTick()
  1531. expect(root.innerHTML).toBe('<div>vapor2 fallback</div><!--slot-->')
  1532. show.value = true
  1533. await nextTick()
  1534. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1535. })
  1536. test('vdom slot > vapor forwarded slot > vapor forwarded slot > vapor slot', async () => {
  1537. const foo = ref('foo')
  1538. const show = ref(true)
  1539. const VaporSlot = createVaporSlot()
  1540. const VaporForwardedSlot2 = createVaporForwardedSlot(VaporSlot)
  1541. const VaporForwardedSlot1 =
  1542. createVaporForwardedSlot(VaporForwardedSlot2)
  1543. const App = createTestApp(VaporForwardedSlot1, foo, show)
  1544. const root = document.createElement('div')
  1545. createApp(App).use(vaporInteropPlugin).mount(root)
  1546. expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
  1547. foo.value = 'bar'
  1548. await nextTick()
  1549. expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
  1550. show.value = false
  1551. await nextTick()
  1552. expect(root.innerHTML).toBe('<div>fallback</div><!--slot--><!--slot-->')
  1553. show.value = true
  1554. await nextTick()
  1555. expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
  1556. })
  1557. test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot(with fallback) > vdom slot', async () => {
  1558. const foo = ref('foo')
  1559. const show = ref(true)
  1560. const VdomSlot = createVdomSlot()
  1561. const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
  1562. VdomSlot,
  1563. 'vapor2 fallback',
  1564. )
  1565. const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
  1566. VaporForwardedSlot2WithFallback,
  1567. 'vapor1 fallback',
  1568. )
  1569. const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
  1570. const root = document.createElement('div')
  1571. createApp(App).use(vaporInteropPlugin).mount(root)
  1572. expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
  1573. foo.value = 'bar'
  1574. await nextTick()
  1575. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1576. show.value = false
  1577. await nextTick()
  1578. expect(root.innerHTML).toBe('<div>vapor1 fallback</div><!--slot-->')
  1579. show.value = true
  1580. await nextTick()
  1581. expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
  1582. })
  1583. test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot(with fallback) > vapor slot', async () => {
  1584. const foo = ref('foo')
  1585. const show = ref(true)
  1586. const VaporSlot = createVaporSlot()
  1587. const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
  1588. VaporSlot,
  1589. 'vapor2 fallback',
  1590. )
  1591. const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
  1592. VaporForwardedSlot2WithFallback,
  1593. 'vapor1 fallback',
  1594. )
  1595. const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
  1596. const root = document.createElement('div')
  1597. createApp(App).use(vaporInteropPlugin).mount(root)
  1598. expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
  1599. foo.value = 'bar'
  1600. await nextTick()
  1601. expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
  1602. show.value = false
  1603. await nextTick()
  1604. expect(root.innerHTML).toBe(
  1605. '<div>vapor1 fallback</div><!--slot--><!--slot-->',
  1606. )
  1607. show.value = true
  1608. await nextTick()
  1609. expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
  1610. })
  1611. test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) > vapor slot', async () => {
  1612. const foo = ref('foo')
  1613. const show = ref(true)
  1614. const VaporSlot = createVaporSlot()
  1615. const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
  1616. VaporSlot,
  1617. 'vdom2 fallback',
  1618. )
  1619. const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
  1620. VdomForwardedSlot2WithFallback,
  1621. 'vdom1 fallback',
  1622. )
  1623. const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
  1624. const root = document.createElement('div')
  1625. createApp(App).use(vaporInteropPlugin).mount(root)
  1626. expect(root.innerHTML).toBe('<span>foo</span>')
  1627. foo.value = 'bar'
  1628. await nextTick()
  1629. expect(root.innerHTML).toBe('<span>bar</span>')
  1630. show.value = false
  1631. await nextTick()
  1632. expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
  1633. show.value = true
  1634. await nextTick()
  1635. expect(root.innerHTML).toBe('<span>bar</span>')
  1636. })
  1637. test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) > vdom slot', async () => {
  1638. const foo = ref('foo')
  1639. const show = ref(true)
  1640. const VdomSlot = createVdomSlot()
  1641. const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
  1642. VdomSlot,
  1643. 'vdom2 fallback',
  1644. )
  1645. const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
  1646. VdomForwardedSlot2WithFallback,
  1647. 'vdom1 fallback',
  1648. )
  1649. const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
  1650. const root = document.createElement('div')
  1651. createApp(App).use(vaporInteropPlugin).mount(root)
  1652. expect(root.innerHTML).toBe('<span>foo</span>')
  1653. foo.value = 'bar'
  1654. await nextTick()
  1655. expect(root.innerHTML).toBe('<span>bar</span>')
  1656. show.value = false
  1657. await nextTick()
  1658. expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
  1659. show.value = true
  1660. await nextTick()
  1661. expect(root.innerHTML).toBe('<span>bar</span>')
  1662. })
  1663. test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) (multiple) > vapor slot', async () => {
  1664. const foo = ref('foo')
  1665. const show = ref(true)
  1666. const VaporSlot = createVaporSlot()
  1667. const VdomForwardedSlot3WithFallback = createVdomForwardedSlot(
  1668. VaporSlot,
  1669. 'vdom3 fallback',
  1670. )
  1671. const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
  1672. VdomForwardedSlot3WithFallback,
  1673. 'vdom2 fallback',
  1674. )
  1675. const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
  1676. VdomForwardedSlot2WithFallback,
  1677. 'vdom1 fallback',
  1678. )
  1679. const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
  1680. const root = document.createElement('div')
  1681. createApp(App).use(vaporInteropPlugin).mount(root)
  1682. expect(root.innerHTML).toBe('<span>foo</span>')
  1683. foo.value = 'bar'
  1684. await nextTick()
  1685. expect(root.innerHTML).toBe('<span>bar</span>')
  1686. show.value = false
  1687. await nextTick()
  1688. expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
  1689. show.value = true
  1690. await nextTick()
  1691. expect(root.innerHTML).toBe('<span>bar</span>')
  1692. })
  1693. })
  1694. })
  1695. describe('createForSlots', () => {
  1696. test('should work', async () => {
  1697. const loop = ref([1, 2, 3])
  1698. let instance: any
  1699. const Child = () => {
  1700. instance = currentInstance
  1701. return template('child')()
  1702. }
  1703. const { render } = define({
  1704. setup() {
  1705. return createComponent(Child, null, {
  1706. $: [
  1707. () =>
  1708. createForSlots(loop.value, (item, i) => ({
  1709. name: item,
  1710. fn: () => template(item + i)(),
  1711. })),
  1712. ],
  1713. })
  1714. },
  1715. })
  1716. render()
  1717. expect(instance.slots).toHaveProperty('1')
  1718. expect(instance.slots).toHaveProperty('2')
  1719. expect(instance.slots).toHaveProperty('3')
  1720. loop.value.push(4)
  1721. await nextTick()
  1722. expect(instance.slots).toHaveProperty('4')
  1723. loop.value.shift()
  1724. await nextTick()
  1725. expect(instance.slots).not.toHaveProperty('1')
  1726. })
  1727. test('should cache dynamic slot source result', async () => {
  1728. const items = ref([1, 2, 3])
  1729. let callCount = 0
  1730. const getItems = () => {
  1731. callCount++
  1732. return items.value
  1733. }
  1734. let instance: any
  1735. const Child = defineVaporComponent(() => {
  1736. instance = currentInstance
  1737. // Create multiple slots to trigger multiple getSlot calls
  1738. const n1 = template('<div></div>')()
  1739. const n2 = template('<div></div>')()
  1740. const n3 = template('<div></div>')()
  1741. insert(createSlot('slot1'), n1 as any as ParentNode)
  1742. insert(createSlot('slot2'), n2 as any as ParentNode)
  1743. insert(createSlot('slot3'), n3 as any as ParentNode)
  1744. return [n1, n2, n3]
  1745. })
  1746. define({
  1747. setup() {
  1748. return createComponent(Child, null, {
  1749. $: [
  1750. () =>
  1751. createForSlots(getItems(), (item, i) => ({
  1752. name: 'slot' + item,
  1753. fn: () => template(String(item))(),
  1754. })),
  1755. ],
  1756. })
  1757. },
  1758. }).render()
  1759. // getItems should only be called once
  1760. expect(callCount).toBe(1)
  1761. expect(instance.slots).toHaveProperty('slot1')
  1762. expect(instance.slots).toHaveProperty('slot2')
  1763. expect(instance.slots).toHaveProperty('slot3')
  1764. })
  1765. test('should update when source changes', async () => {
  1766. const items = ref([1, 2])
  1767. let callCount = 0
  1768. const getItems = () => {
  1769. callCount++
  1770. return items.value
  1771. }
  1772. let instance: any
  1773. const Child = defineVaporComponent(() => {
  1774. instance = currentInstance
  1775. const n1 = template('<div></div>')()
  1776. const n2 = template('<div></div>')()
  1777. const n3 = template('<div></div>')()
  1778. insert(createSlot('slot1'), n1 as any as ParentNode)
  1779. insert(createSlot('slot2'), n2 as any as ParentNode)
  1780. insert(createSlot('slot3'), n3 as any as ParentNode)
  1781. return [n1, n2, n3]
  1782. })
  1783. define({
  1784. setup() {
  1785. return createComponent(Child, null, {
  1786. $: [
  1787. () =>
  1788. createForSlots(getItems(), (item, i) => ({
  1789. name: 'slot' + item,
  1790. fn: () => template(String(item))(),
  1791. })),
  1792. ],
  1793. })
  1794. },
  1795. }).render()
  1796. expect(callCount).toBe(1)
  1797. expect(instance.slots).toHaveProperty('slot1')
  1798. expect(instance.slots).toHaveProperty('slot2')
  1799. expect(instance.slots).not.toHaveProperty('slot3')
  1800. // Update items
  1801. items.value.push(3)
  1802. await nextTick()
  1803. // Should be called again after source changes
  1804. expect(callCount).toBe(2)
  1805. expect(instance.slots).toHaveProperty('slot1')
  1806. expect(instance.slots).toHaveProperty('slot2')
  1807. expect(instance.slots).toHaveProperty('slot3')
  1808. })
  1809. test('should render slots correctly with caching', async () => {
  1810. const items = ref([1, 2, 3, 4, 5])
  1811. const Child = defineVaporComponent(() => {
  1812. const containers: any[] = []
  1813. for (let i = 1; i <= 5; i++) {
  1814. const n = template('<div></div>')()
  1815. insert(createSlot('slot' + i), n as any as ParentNode)
  1816. containers.push(n)
  1817. }
  1818. return containers
  1819. })
  1820. const { host } = define({
  1821. setup() {
  1822. return createComponent(Child, null, {
  1823. $: [
  1824. () =>
  1825. createForSlots(items.value, item => ({
  1826. name: 'slot' + item,
  1827. fn: () => template('content' + item)(),
  1828. })),
  1829. ],
  1830. })
  1831. },
  1832. }).render()
  1833. expect(host.innerHTML).toBe(
  1834. '<div>content1<!--slot--></div>' +
  1835. '<div>content2<!--slot--></div>' +
  1836. '<div>content3<!--slot--></div>' +
  1837. '<div>content4<!--slot--></div>' +
  1838. '<div>content5<!--slot--></div>',
  1839. )
  1840. // Update items
  1841. items.value = [2, 4]
  1842. await nextTick()
  1843. expect(host.innerHTML).toBe(
  1844. '<div><!--slot--></div>' +
  1845. '<div>content2<!--slot--></div>' +
  1846. '<div><!--slot--></div>' +
  1847. '<div>content4<!--slot--></div>' +
  1848. '<div><!--slot--></div>',
  1849. )
  1850. })
  1851. test('should work with null and undefined', async () => {
  1852. const loop = ref<number[] | null | undefined>(undefined)
  1853. let instance: any
  1854. const Child = () => {
  1855. instance = currentInstance
  1856. return template('child')()
  1857. }
  1858. const { render } = define({
  1859. setup() {
  1860. return createComponent(Child, null, {
  1861. $: [
  1862. () =>
  1863. createForSlots(loop.value as any, (item, i) => ({
  1864. name: item,
  1865. fn: () => template(item + i)(),
  1866. })),
  1867. ],
  1868. })
  1869. },
  1870. })
  1871. render()
  1872. expect(instance.slots).toEqual({})
  1873. loop.value = [1]
  1874. await nextTick()
  1875. expect(instance.slots).toHaveProperty('1')
  1876. loop.value = null
  1877. await nextTick()
  1878. expect(instance.slots).toEqual({})
  1879. })
  1880. })
  1881. })