apiCreateDynamicComponent.spec.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import { ref, shallowRef } from '@vue/reactivity'
  2. import { nextTick, resolveDynamicComponent } from '@vue/runtime-dom'
  3. import {
  4. createComponentWithFallback,
  5. createDynamicComponent,
  6. defineVaporComponent,
  7. renderEffect,
  8. setHtml,
  9. setInsertionState,
  10. template,
  11. } from '../src'
  12. import { makeRender } from './_utils'
  13. const define = makeRender()
  14. describe('api: createDynamicComponent', () => {
  15. const A = () => document.createTextNode('AAA')
  16. const B = () => document.createTextNode('BBB')
  17. test('direct value', async () => {
  18. const val = shallowRef<any>(A)
  19. const { html } = define({
  20. setup() {
  21. return createDynamicComponent(() => val.value)
  22. },
  23. }).render()
  24. expect(html()).toBe('AAA<!--dynamic-component-->')
  25. val.value = B
  26. await nextTick()
  27. expect(html()).toBe('BBB<!--dynamic-component-->')
  28. // fallback
  29. val.value = 'foo'
  30. await nextTick()
  31. expect(html()).toBe('<foo></foo><!--dynamic-component-->')
  32. })
  33. test('global registration', async () => {
  34. const val = shallowRef('foo')
  35. const { app, html, mount } = define({
  36. setup() {
  37. return createDynamicComponent(() => val.value)
  38. },
  39. }).create()
  40. app.component('foo', A)
  41. app.component('bar', B)
  42. mount()
  43. expect(html()).toBe('AAA<!--dynamic-component-->')
  44. val.value = 'bar'
  45. await nextTick()
  46. expect(html()).toBe('BBB<!--dynamic-component-->')
  47. // fallback
  48. val.value = 'baz'
  49. await nextTick()
  50. expect(html()).toBe('<baz></baz><!--dynamic-component-->')
  51. })
  52. test('with v-once', async () => {
  53. const val = shallowRef<any>(A)
  54. const { html } = define({
  55. setup() {
  56. return createDynamicComponent(() => val.value, null, null, true, true)
  57. },
  58. }).render()
  59. expect(html()).toBe('AAA<!--dynamic-component-->')
  60. val.value = B
  61. await nextTick()
  62. expect(html()).toBe('AAA<!--dynamic-component-->') // still AAA
  63. })
  64. test('fallback with v-once', async () => {
  65. const val = shallowRef<any>('button')
  66. const id = ref(0)
  67. const { html } = define({
  68. setup() {
  69. return createDynamicComponent(
  70. () => val.value,
  71. { id: () => id.value },
  72. null,
  73. true,
  74. true,
  75. )
  76. },
  77. }).render()
  78. expect(html()).toBe('<button id="0"></button><!--dynamic-component-->')
  79. id.value++
  80. await nextTick()
  81. expect(html()).toBe('<button id="0"></button><!--dynamic-component-->')
  82. })
  83. test('render fallback with insertionState', async () => {
  84. const { html, mount } = define({
  85. setup() {
  86. const html = ref('hi')
  87. const n1 = template('<div></div>', true)() as any
  88. setInsertionState(n1)
  89. const n0 = createComponentWithFallback(
  90. resolveDynamicComponent('button') as any,
  91. ) as any
  92. renderEffect(() => setHtml(n0, html.value))
  93. return n1
  94. },
  95. }).create()
  96. mount()
  97. expect(html()).toBe('<div><button>hi</button></div>')
  98. })
  99. test('switch dynamic component children', async () => {
  100. const CompA = defineVaporComponent({
  101. setup() {
  102. return template('<div>A</div>')()
  103. },
  104. })
  105. const CompB = defineVaporComponent({
  106. setup() {
  107. return template('<div>B</div>')()
  108. },
  109. })
  110. const current = shallowRef(CompA)
  111. const { html } = define({
  112. setup() {
  113. const t1 = template('<div></div>')
  114. const n2 = t1() as any
  115. setInsertionState(n2)
  116. createDynamicComponent(() => current.value)
  117. return n2
  118. },
  119. }).render()
  120. expect(html()).toBe('<div><div>A</div><!--dynamic-component--></div>')
  121. current.value = CompB
  122. await nextTick()
  123. expect(html()).toBe('<div><div>B</div><!--dynamic-component--></div>')
  124. })
  125. test('fallback with dynamic slots', async () => {
  126. const slotName = ref('default')
  127. const { html } = define({
  128. setup() {
  129. return createDynamicComponent(() => 'div', null, {
  130. $: [
  131. () => ({
  132. name: slotName.value,
  133. fn: () => template('<span>hi</span>')(),
  134. }),
  135. ] as any,
  136. })
  137. },
  138. }).render()
  139. expect(html()).toBe(
  140. '<div><span>hi</span><!--slot--></div><!--dynamic-component-->',
  141. )
  142. // update slot name
  143. slotName.value = 'custom'
  144. await nextTick()
  145. expect(html()).toBe('<div><!--slot--></div><!--dynamic-component-->')
  146. slotName.value = 'default'
  147. await nextTick()
  148. expect(html()).toBe(
  149. '<div><span>hi</span><!--slot--></div><!--dynamic-component-->',
  150. )
  151. })
  152. test('accept blocks', async () => {
  153. const { html } = define({
  154. setup() {
  155. const n0 = template('<a>router link</a>')()
  156. return createDynamicComponent(() => n0)
  157. },
  158. }).render()
  159. expect(html()).toBe('<a>router link</a><!--dynamic-component-->')
  160. })
  161. })