fix: more robust rendering of Svelte custom element slots (#18710)

Instead of checking the given nodes at startup and only injecting those
into the inner Svelte component constructor, we are now detecting (via
the `$$host` property) whether or not the component is rendered as a
custom element, and in that case create all slots as real slots right
away.

Fixes #13638
Fixes #8997 (therefore closes #8999)
pull/18714/head
Simon H 4 days ago committed by GitHub
parent c894afd8c7
commit 135f1ec004
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: more robust rendering of Svelte custom element slots

@ -1,4 +1,6 @@
import { hydrate_next, hydrating } from '../hydration.js';
import { create_element, create_text } from '../operations.js';
import { append } from '../template.js';
/**
* @param {Comment} anchor
@ -12,6 +14,23 @@ export function slot(anchor, $$props, name, slot_props, fallback_fn) {
hydrate_next();
}
// Custom element slots are native DOM slots.
// Use the stored reference because the shadow root may be closed.
if ($$props.$$host?.$$shadowRoot) {
const element = create_element('slot');
if (name !== 'default') element.name = name;
append(anchor, element);
if (fallback_fn !== null) {
const fallback_anchor = create_text();
element.append(fallback_anchor);
fallback_fn(fallback_anchor);
}
return;
}
var slot_fn = $$props.$$slots?.[name];
// Interop: Can use snippets to fill slots
var is_interop = false;

@ -14,8 +14,8 @@ export default test({
assert.htmlEqual(
ce.shadowRoot.innerHTML,
`
<slot></slot>
<p>named fallback</p>
<slot>fallback</slot>
<slot name="named"><p>named fallback</p></slot>
`
);
@ -23,8 +23,8 @@ export default test({
assert.htmlEqual(
ce.shadowRoot.innerHTML,
`
<slot></slot>
<p>named fallback</p>
<slot>fallback</slot>
<slot name="named"><p>named fallback</p></slot>
`
);
}

@ -3,10 +3,7 @@ const tick = () => Promise.resolve();
export default test({
async test({ assert, target }) {
target.innerHTML = `
<custom-element>
<strong>slotted</strong>
</custom-element>`;
target.innerHTML = '<custom-element></custom-element>';
await tick();
await tick();
@ -16,7 +13,26 @@ export default test({
const div = el.shadowRoot.children[0];
const [slot0, slot1] = div.children;
assert.equal(slot0.assignedNodes()[1], target.querySelector('strong'));
assert.equal(slot1.innerHTML, 'foo fallback content');
assert.equal(slot0.localName, 'slot');
assert.equal(slot0.assignedNodes().length, 0);
assert.equal(slot0.innerHTML, '<p>default fallback content</p>');
assert.equal(slot1.localName, 'slot');
assert.equal(slot1.name, 'foo');
assert.equal(slot1.assignedNodes().length, 0);
assert.equal(slot1.innerHTML, '<p>foo fallback content</p>');
const default_content = document.createElement('strong');
default_content.textContent = 'default content';
el.append(default_content);
const named_content = document.createElement('strong');
named_content.slot = 'foo';
named_content.textContent = 'named content';
el.append(named_content);
assert.equal(slot0.assignedNodes().length, 1);
assert.equal(slot0.assignedNodes()[0], default_content);
assert.equal(slot1.assignedNodes().length, 1);
assert.equal(slot1.assignedNodes()[0], named_content);
}
});

@ -3,7 +3,7 @@ const tick = () => Promise.resolve();
export default test({
async test({ assert, target }) {
target.innerHTML = '<custom-element name="world"></custom-element>';
target.innerHTML = '<custom-element name="world"><span>slotted</span></custom-element>';
await tick();
await tick();
@ -15,5 +15,6 @@ export default test({
assert.equal(el.shadowRoot, null);
assert.equal(h1.innerHTML, 'Hello world!');
assert.equal(getComputedStyle(h1).color, 'rgb(255, 0, 0)');
assert.equal(el.querySelector('slot').innerHTML, '');
}
});

@ -5,6 +5,7 @@
</script>
<h1>Hello {name}!</h1>
<slot>fallback</slot>
<style>
h1 {

Loading…
Cancel
Save