Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions packages/runtime-vapor/__tests__/componentSlots.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,5 +502,123 @@ describe('component: slots', () => {
await nextTick()
expect(host.innerHTML).toBe('<div><h1></h1><!--slot--></div>')
})

test('render fallback when slot content is not valid', async () => {
const Child = {
setup() {
return createSlot('default', null, () =>
document.createTextNode('fallback'),
)
},
}

const { html } = define({
setup() {
return createComponent(Child, null, {
default: () => {
return template('<!--comment-->')()
},
})
},
}).render()

expect(html()).toBe('fallback<!--slot-->')
})

test('render fallback when v-if condition is false', async () => {
const Child = {
setup() {
return createSlot('default', null, () =>
document.createTextNode('fallback'),
)
},
}

const toggle = ref(false)

const { html } = define({
setup() {
return createComponent(Child, null, {
default: () => {
return createIf(
() => toggle.value,
() => {
return document.createTextNode('content')
},
)
},
})
},
}).render()

expect(html()).toBe('fallback<!--if--><!--slot-->')

toggle.value = true
await nextTick()
expect(html()).toBe('content<!--if--><!--slot-->')

toggle.value = false
await nextTick()
expect(html()).toBe('fallback<!--if--><!--slot-->')
})

test('render fallback with nested v-if ', async () => {
const Child = {
setup() {
return createSlot('default', null, () =>
document.createTextNode('fallback'),
)
},
}

const outerShow = ref(false)
const innerShow = ref(false)

const { html } = define({
setup() {
return createComponent(Child, null, {
default: () => {
return createIf(
() => outerShow.value,
() => {
return createIf(
() => innerShow.value,
() => {
return document.createTextNode('content')
},
)
},
)
},
})
},
}).render()

expect(html()).toBe('fallback<!--if--><!--slot-->')

outerShow.value = true
await nextTick()
expect(html()).toBe('fallback<!--if--><!--if--><!--slot-->')

innerShow.value = true
await nextTick()
expect(html()).toBe('content<!--if--><!--if--><!--slot-->')

innerShow.value = false
await nextTick()
expect(html()).toBe('fallback<!--if--><!--if--><!--slot-->')

outerShow.value = false
await nextTick()
expect(html()).toBe('fallback<!--if--><!--slot-->')

outerShow.value = true
await nextTick()
expect(html()).toBe('fallback<!--if--><!--if--><!--slot-->')

innerShow.value = true
await nextTick()
expect(html()).toBe('content<!--if--><!--if--><!--slot-->')
})
})
})
27 changes: 24 additions & 3 deletions packages/runtime-vapor/src/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,37 @@ export class DynamicFragment extends VaporFragment {

if (this.fallback && !isValidBlock(this.nodes)) {
parent && remove(this.nodes, parent)
this.nodes =
(this.scope || (this.scope = new EffectScope())).run(this.fallback) ||
[]
// handle nested dynamic fragment
if (isFragment(this.nodes)) {
renderFallback(this.nodes, this.fallback, key)
} else {
this.nodes =
(this.scope || (this.scope = new EffectScope())).run(this.fallback) ||
[]
}
parent && insert(this.nodes, parent, this.anchor)
}

setActiveSub(prevSub)
}
}

function renderFallback(
fragment: VaporFragment,
fallback: BlockFn,
key: any,
): void {
if (fragment instanceof DynamicFragment) {
const nodes = fragment.nodes
if (isFragment(nodes)) {
renderFallback(nodes, fallback, key)
} else {
if (!fragment.fallback) fragment.fallback = fallback
fragment.update(fragment.fallback, key)
}
}
}

export function isFragment(val: NonNullable<unknown>): val is VaporFragment {
return val instanceof VaporFragment
}
Expand Down
1 change: 1 addition & 0 deletions packages/runtime-vapor/src/componentSlots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export function createSlot(
const renderSlot = () => {
const slot = getSlot(rawSlots, isFunction(name) ? name() : name)
if (slot) {
fragment.fallback = fallback
// create and cache bound version of the slot to make it stable
// so that we avoid unnecessary updates if it resolves to the same slot
fragment.update(
Expand Down