fix: allow slot attribute inside snippets (#12188)

Someone could render a snippet into a custom element, and therefore elements with slot attributes should be allowed within them and be treated as regular elements
closes #12158
pull/12195/head
Simon H 5 months ago committed by GitHub
parent 48d1ef96a6
commit a4e994bdf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: allow slot attribute inside snippets

@ -256,8 +256,16 @@ function validate_attribute_name(attribute) {
* @param {boolean} is_component * @param {boolean} is_component
*/ */
function validate_slot_attribute(context, attribute, is_component = false) { function validate_slot_attribute(context, attribute, is_component = false) {
const parent = context.path.at(-2);
let owner = undefined; let owner = undefined;
if (parent?.type === 'SnippetBlock') {
if (!is_text_attribute(attribute)) {
e.slot_attribute_invalid(attribute);
}
return;
}
let i = context.path.length; let i = context.path.length;
while (i--) { while (i--) {
const ancestor = context.path[i]; const ancestor = context.path[i];
@ -283,7 +291,7 @@ function validate_slot_attribute(context, attribute, is_component = false) {
owner.type === 'SvelteComponent' || owner.type === 'SvelteComponent' ||
owner.type === 'SvelteSelf' owner.type === 'SvelteSelf'
) { ) {
if (owner !== context.path.at(-2)) { if (owner !== parent) {
e.slot_attribute_invalid_placement(attribute); e.slot_attribute_invalid_placement(attribute);
} }

@ -0,0 +1,18 @@
<script context="module">
if (!customElements.get('my-custom-element')) {
customElements.define('my-custom-element', class extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = '|<slot></slot>|<slot name="slot"></slot>|';
}
});
}
</script>
<script>
const { children } = $props();
</script>
<my-custom-element>
{@render children()}
</my-custom-element>

@ -0,0 +1,22 @@
import { test } from '../../test';
export default test({
mode: ['client', 'server'],
html: `<my-custom-element>Default <span slot="slot">Slotted</span></my-custom-element>`,
test({ target, assert }) {
const shadowRoot = /** @type {ShadowRoot} */ (
target.querySelector('my-custom-element')?.shadowRoot
);
const [defaultSlot, namedSlot] = shadowRoot.querySelectorAll('slot');
const assignedDefaultNodes = defaultSlot.assignedNodes();
const assignedNamedNodes = namedSlot.assignedNodes();
assert.equal(assignedDefaultNodes.length, 1);
assert.equal(assignedNamedNodes.length, 1);
assert.htmlEqual(assignedDefaultNodes[0].textContent || '', `Default`);
assert.htmlEqual(
/** @type {HTMLElement} */ (assignedNamedNodes[0]).outerHTML,
`<span slot="slot">Slotted</span>`
);
}
});

@ -0,0 +1,10 @@
<script>
import Component from './Component.svelte';
</script>
<Component>
{#snippet children()}
Default
<span slot="slot">Slotted</span>
{/snippet}
</Component>
Loading…
Cancel
Save