app.mount(container)
expect(container.innerHTML).toBe(
`<ce-shadow-root-false-optimized data-v-app="">` +
- `<div>false</div><!--v-if-->` +
+ `<div>false</div><!--v-if--><!--v-if-->` +
`</ce-shadow-root-false-optimized>`,
)
await nextTick()
expect(container.innerHTML).toBe(
`<ce-shadow-root-false-optimized data-v-app="">` +
- `<div>false</div><!--v-if-->` +
+ `<div>false</div><!--v-if--><!--v-if-->` +
`</ce-shadow-root-false-optimized>`,
)
await nextTick()
expect(container.innerHTML).toBe(
`<ce-shadow-root-false-optimized data-v-app="" is-shown="">` +
- `<div><div>true</div><div>hi</div></div>` +
+ `<!--v-if--><div><div>true</div><div>hi</div></div>` +
`</ce-shadow-root-false-optimized>`,
)
})
- test.todo('update slotted v-if nodes w/ shadowRoot false', async () => {
+ test('update slotted v-if nodes w/ shadowRoot false', async () => {
const E = defineCustomElement(
defineComponent({
props: {
const app = createApp(App)
app.mount(container)
expect(container.innerHTML).toBe(
- `<ce-shadow-root-false data-v-app=""><div>false</div><!--v-if--></ce-shadow-root-false>`,
+ `<ce-shadow-root-false data-v-app="">` +
+ `<div>false</div><!--v-if--><!--v-if-->` +
+ `</ce-shadow-root-false>`,
)
click()
await nextTick()
expect(container.innerHTML).toBe(
- `<ce-shadow-root-false data-v-app="" is-shown=""><div><div>true</div><!--v-if--></div></ce-shadow-root-false>`,
+ `<ce-shadow-root-false data-v-app="" is-shown="">` +
+ `<div><div>true</div><!--v-if--></div>` +
+ `</ce-shadow-root-false>`,
)
click()
await nextTick()
expect(container.innerHTML).toBe(
- `<ce-shadow-root-false data-v-app=""><div>false</div><!--v-if--></ce-shadow-root-false>`,
+ `<ce-shadow-root-false data-v-app="">` +
+ `<div>false</div><!--v-if--><!--v-if-->` +
+ `</ce-shadow-root-false>`,
)
click()
await nextTick()
expect(container.innerHTML).toBe(
- `<ce-shadow-root-false data-v-app="" is-shown=""><div><div>true</div><div>hi</div></div></ce-shadow-root-false>`,
+ `<ce-shadow-root-false data-v-app="" is-shown="">` +
+ `<!--v-if--><div><div>true</div><div>hi</div></div>` +
+ `</ce-shadow-root-false>`,
)
})
app.mount(container)
expect(container.innerHTML).toBe(
`<ce-with-fallback-shadow-root-false-optimized data-v-app="">` +
- `fallback` +
+ `<!--v-if-->fallback` +
`</ce-with-fallback-shadow-root-false-optimized>`,
)
await nextTick()
expect(container.innerHTML).toBe(
`<ce-with-fallback-shadow-root-false-optimized data-v-app="">` +
- `fallback<!--v-if-->` +
+ `<!--v-if-->fallback` +
`</ce-with-fallback-shadow-root-false-optimized>`,
)
})
- test.todo(
- 'switch between slotted and fallback nodes w/ shadowRoot false',
- async () => {
- const E = defineCustomElement(
- defineComponent({
- render() {
- return renderSlot(this.$slots, 'foo', {}, () => [
- createTextVNode('fallback'),
- ])
- },
- }),
- { shadowRoot: false },
- )
- customElements.define('ce-with-fallback-shadow-root-false', E)
-
- const Comp = defineComponent({
+ test('switch between slotted and fallback nodes w/ shadowRoot false', async () => {
+ const E = defineCustomElement(
+ defineComponent({
render() {
- return h('ce-with-fallback-shadow-root-false', null, [
- this.$slots.foo
- ? h('div', { key: 0, slot: 'foo' }, [
- renderSlot(this.$slots, 'foo'),
- ])
- : createCommentVNode('v-if', true),
- renderSlot(this.$slots, 'default'),
+ return renderSlot(this.$slots, 'foo', {}, () => [
+ createTextVNode('fallback'),
])
},
- })
+ }),
+ { shadowRoot: false },
+ )
+ customElements.define('ce-with-fallback-shadow-root-false', E)
- const isShown = ref(false)
- const App = defineComponent({
- components: { Comp },
- render() {
- return h(
- Comp,
- null,
- createSlots(
- { _: 2 /* DYNAMIC */ } as any,
- [
- isShown.value
- ? {
- name: 'foo',
- fn: withCtx(() => [createTextVNode('foo')]),
- key: '0',
- }
- : undefined,
- ] as any,
- ),
- )
- },
- })
+ const Comp = defineComponent({
+ render() {
+ return h('ce-with-fallback-shadow-root-false', null, [
+ this.$slots.foo
+ ? h('div', { key: 0, slot: 'foo' }, [
+ renderSlot(this.$slots, 'foo'),
+ ])
+ : createCommentVNode('v-if', true),
+ renderSlot(this.$slots, 'default'),
+ ])
+ },
+ })
- const container = document.createElement('div')
- document.body.appendChild(container)
-
- const app = createApp(App)
- app.mount(container)
- expect(container.innerHTML).toBe(
- `<ce-with-fallback-shadow-root-false data-v-app="">` +
- `fallback` +
- `</ce-with-fallback-shadow-root-false>`,
- )
-
- isShown.value = true
- await nextTick()
- expect(container.innerHTML).toBe(
- `<ce-with-fallback-shadow-root-false data-v-app="">` +
- `<div slot="foo">foo</div>` +
- `</ce-with-fallback-shadow-root-false>`,
- )
-
- isShown.value = false
- await nextTick()
- expect(container.innerHTML).toBe(
- `<ce-with-fallback-shadow-root-false data-v-app="">` +
- `fallback<!--v-if-->` +
- `</ce-with-fallback-shadow-root-false>`,
- )
- },
- )
+ const isShown = ref(false)
+ const App = defineComponent({
+ components: { Comp },
+ render() {
+ return h(
+ Comp,
+ null,
+ createSlots(
+ { _: 2 /* DYNAMIC */ } as any,
+ [
+ isShown.value
+ ? {
+ name: 'foo',
+ fn: withCtx(() => [createTextVNode('foo')]),
+ key: '0',
+ }
+ : undefined,
+ ] as any,
+ ),
+ )
+ },
+ })
+
+ const container = document.createElement('div')
+ document.body.appendChild(container)
+
+ const app = createApp(App)
+ app.mount(container)
+ expect(container.innerHTML).toBe(
+ `<ce-with-fallback-shadow-root-false data-v-app="">` +
+ `<!--v-if-->fallback` +
+ `</ce-with-fallback-shadow-root-false>`,
+ )
+
+ isShown.value = true
+ await nextTick()
+ expect(container.innerHTML).toBe(
+ `<ce-with-fallback-shadow-root-false data-v-app="">` +
+ `<div slot="foo">foo</div>` +
+ `</ce-with-fallback-shadow-root-false>`,
+ )
+
+ isShown.value = false
+ await nextTick()
+ expect(container.innerHTML).toBe(
+ `<ce-with-fallback-shadow-root-false data-v-app="">` +
+ `<!--v-if-->fallback` +
+ `</ce-with-fallback-shadow-root-false>`,
+ )
+ })
})
describe('helpers', () => {
private _slots?: Record<string, Node[]>
private _slotFallbacks?: Record<string, Node[]>
private _slotAnchors?: Map<string, Node>
+ private _slotNames: Set<string> | undefined
constructor(
/**
const slotName =
(n.nodeType === 1 && (n as Element).getAttribute('slot')) || 'default'
;(slots[slotName] || (slots[slotName] = [])).push(n)
+ ;(this._slotNames || (this._slotNames = new Set())).add(slotName)
const next = n.nextSibling
- // store the parentNode reference since node will be removed
- // but it is needed during patching
- ;(n as any).$parentNode = n.parentNode
if (remove) this.removeChild(n)
n = next
}
private _renderSlots() {
const outlets = (this._teleportTarget || this).querySelectorAll('slot')
const scopeId = this._instance!.type.__scopeId
- this._slotAnchors = new Map()
const processedSlots = new Set<string>()
for (let i = 0; i < outlets.length; i++) {
// insert an anchor to facilitate updates
const anchor = document.createTextNode('')
- this._slotAnchors.set(slotName, anchor)
+ ;(this._slotAnchors || (this._slotAnchors = new Map())).set(
+ slotName,
+ anchor,
+ )
parent.insertBefore(anchor, o)
if (content) {
if (!processedSlots.has('default')) {
let content = this._slots!['default']
if (content) {
- // TODO
- content = content.filter(
- n => !(n.nodeType === 8 && (n as Comment).data === 'v-if'),
+ let anchor
+ // if the default slot is not the first one, insert it behind the previous slot
+ if (this._slotAnchors) {
+ const slotNames = Array.from(this._slotNames!)
+ const defaultSlotIndex = slotNames.indexOf('default')
+ if (defaultSlotIndex > 0) {
+ const prevSlotAnchor = this._slotAnchors.get(
+ slotNames[defaultSlotIndex - 1],
+ )
+ if (prevSlotAnchor) anchor = prevSlotAnchor.nextSibling
+ }
+ }
+
+ insertSlottedContent(
+ content,
+ scopeId,
+ this._root,
+ anchor || this.firstChild,
)
- insertSlottedContent(content, scopeId, this, this.firstChild)
}
}
}
const fallbackNodes = this._slotFallbacks![name]
if (fallbackNodes) {
// render fallback nodes for removed slots
- if (!newSlotNames.includes(name)) {
- const anchor = this._slotAnchors!.get(name)!
+ if (!newSlotNames.includes(name) && this._slotAnchors) {
+ const anchor = this._slotAnchors.get(name)!
fallbackNodes.forEach(fallbackNode =>
this.insertBefore(fallbackNode, anchor),
)
;(child as Element).setAttribute(id, '')
}
}
+ ;(n as any).$parentNode = parent
parent.insertBefore(n, anchor)
}
}