import { ref, shallowRef } from '@vue/reactivity' import { nextTick, resolveDynamicComponent } from '@vue/runtime-dom' import { createComponentWithFallback, createDynamicComponent, defineVaporComponent, renderEffect, setHtml, setInsertionState, template, } from '../src' import { makeRender } from './_utils' const define = makeRender() describe('api: createDynamicComponent', () => { const A = () => document.createTextNode('AAA') const B = () => document.createTextNode('BBB') test('direct value', async () => { const val = shallowRef(A) const { html } = define({ setup() { return createDynamicComponent(() => val.value) }, }).render() expect(html()).toBe('AAA') val.value = B await nextTick() expect(html()).toBe('BBB') // fallback val.value = 'foo' await nextTick() expect(html()).toBe('') }) test('global registration', async () => { const val = shallowRef('foo') const { app, html, mount } = define({ setup() { return createDynamicComponent(() => val.value) }, }).create() app.component('foo', A) app.component('bar', B) mount() expect(html()).toBe('AAA') val.value = 'bar' await nextTick() expect(html()).toBe('BBB') // fallback val.value = 'baz' await nextTick() expect(html()).toBe('') }) test('with v-once', async () => { const val = shallowRef(A) const { html } = define({ setup() { return createDynamicComponent(() => val.value, null, null, true, true) }, }).render() expect(html()).toBe('AAA') val.value = B await nextTick() expect(html()).toBe('AAA') // still AAA }) test('fallback with v-once', async () => { const val = shallowRef('button') const id = ref(0) const { html } = define({ setup() { return createDynamicComponent( () => val.value, { id: () => id.value }, null, true, true, ) }, }).render() expect(html()).toBe('') id.value++ await nextTick() expect(html()).toBe('') }) test('render fallback with insertionState', async () => { const { html, mount } = define({ setup() { const html = ref('hi') const n1 = template('
', true)() as any setInsertionState(n1) const n0 = createComponentWithFallback( resolveDynamicComponent('button') as any, ) as any renderEffect(() => setHtml(n0, html.value)) return n1 }, }).create() mount() expect(html()).toBe('
') }) test('switch dynamic component children', async () => { const CompA = defineVaporComponent({ setup() { return template('
A
')() }, }) const CompB = defineVaporComponent({ setup() { return template('
B
')() }, }) const current = shallowRef(CompA) const { html } = define({ setup() { const t1 = template('
') const n2 = t1() as any setInsertionState(n2) createDynamicComponent(() => current.value) return n2 }, }).render() expect(html()).toBe('
A
') current.value = CompB await nextTick() expect(html()).toBe('
B
') }) test('fallback with dynamic slots', async () => { const slotName = ref('default') const { html } = define({ setup() { return createDynamicComponent(() => 'div', null, { $: [ () => ({ name: slotName.value, fn: () => template('hi')(), }), ] as any, }) }, }).render() expect(html()).toBe( '
hi
', ) // update slot name slotName.value = 'custom' await nextTick() expect(html()).toBe('
') slotName.value = 'default' await nextTick() expect(html()).toBe( '
hi
', ) }) test('accept blocks', async () => { const { html } = define({ setup() { const n0 = template('router link')() return createDynamicComponent(() => n0) }, }).render() expect(html()).toBe('router link') }) })