mirror of https://github.com/sveltejs/svelte
fix: correctly set custom element props (#14508)
fixes #14391 In #13337 the "when to set as a property" logic for custom elements was adjusted. A bug was introduced during this, and it consists of several parts, of which the latter I'm not sure what's the best solution, hence opening this to discuss. The problem is that during set_custom_element_data, get_setters is the only check done to differentiate between setting the value as a prop (has a setter) or as an attribute (doesn't have a setter). The solution is to take into account whether or not the custom element is already registered, and defer getting (and caching) its setters until then. Instead, fall back to a "an object is always set as a prop" heuristic.pull/14663/head
parent
cb5734ae66
commit
a2539cfe1f
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: take into account registration state when setting custom element props
|
@ -0,0 +1,25 @@
|
||||
import { flushSync } from 'svelte';
|
||||
import { test } from '../../assert';
|
||||
|
||||
const tick = () => Promise.resolve();
|
||||
|
||||
// Check that rendering a custom element and setting a property before it is registered
|
||||
// does not break the "when to set this as a property" logic
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
target.innerHTML = '<custom-element></custom-element>';
|
||||
await tick();
|
||||
await tick();
|
||||
|
||||
const ce_root = target.querySelector('custom-element').shadowRoot;
|
||||
|
||||
ce_root.querySelector('button')?.click();
|
||||
flushSync();
|
||||
await tick();
|
||||
await tick();
|
||||
|
||||
const inner_ce_root = ce_root.querySelectorAll('set-property-before-mounted');
|
||||
assert.htmlEqual(inner_ce_root[0].shadowRoot.innerHTML, 'object|{"foo":"bar"}');
|
||||
assert.htmlEqual(inner_ce_root[1].shadowRoot.innerHTML, 'object|{"foo":"bar"}');
|
||||
}
|
||||
});
|
@ -0,0 +1,31 @@
|
||||
<svelte:options customElement="custom-element" />
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
class CustomElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
Object.defineProperty(this, 'property', {
|
||||
set: (value) => {
|
||||
this.shadowRoot.innerHTML = typeof value + '|' + JSON.stringify(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
customElements.define('set-property-before-mounted', CustomElement);
|
||||
});
|
||||
|
||||
let property = $state();
|
||||
</script>
|
||||
|
||||
<button onclick={() => (property = { foo: 'bar' })}>Update</button>
|
||||
<!-- one that's there before it's registered -->
|
||||
<set-property-before-mounted {property}></set-property-before-mounted>
|
||||
<!-- and one that's after registration but sets property to an object right away -->
|
||||
{#if property}
|
||||
<set-property-before-mounted {property}></set-property-before-mounted>
|
||||
{/if}
|
Loading…
Reference in new issue