fix: spreading style is not consistent with attribute (#15323)

* style must be set via set_attribute

* test

* changeset

* add empty string and null in test

* explanatory comment

* this is now redundant, set_attribute takes care of it

* drive-by

* tweak changeset

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/15453/head
adiGuba 7 months ago committed by GitHub
parent 1efad3f6e2
commit 2f685c1dba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: always use `setAttribute` when setting `style`

@ -218,6 +218,7 @@ export function set_custom_element_data(node, prop, value) {
// or effect
var previous_reaction = active_reaction;
var previous_effect = active_effect;
// If we're hydrating but the custom element is from Svelte, and it already scaffolded,
// then it might run block logic in hydration mode, which we have to prevent.
let was_hydrating = hydrating;
@ -227,17 +228,20 @@ export function set_custom_element_data(node, prop, value) {
set_active_reaction(null);
set_active_effect(null);
try {
if (
// `style` should use `set_attribute` rather than the setter
prop !== 'style' &&
// Don't compute setters for custom elements while they aren't registered yet,
// because during their upgrade/instantiation they might add more setters.
// Instead, fall back to a simple "an object, then set as property" heuristic.
setters_cache.has(node.nodeName) ||
(setters_cache.has(node.nodeName) ||
// customElements may not be available in browser extension contexts
!customElements ||
customElements.get(node.tagName.toLowerCase())
? get_setters(node).includes(prop)
: value && typeof value === 'object'
: value && typeof value === 'object')
) {
// @ts-expect-error
node[prop] = value;
@ -378,8 +382,9 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal
// @ts-ignore
element[`__${event_name}`] = undefined;
}
} else if (key === 'style' && value != null) {
element.style.cssText = value + '';
} else if (key === 'style') {
// avoid using the setter
set_attribute(element, key, value);
} else if (key === 'autofocus') {
autofocus(/** @type {HTMLElement} */ (element), Boolean(value));
} else if (!is_custom_element && (key === '__value' || (key === 'value' && value != null))) {
@ -428,10 +433,6 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal
set_attribute(element, name, value, skip_warning);
}
}
if (key === 'style' && '__styles' in element) {
// reset styles to force style: directive to update
element.__styles = {};
}
}
if (is_hydrating_custom_element) {

@ -0,0 +1,64 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
const style_1 = 'invalid-key:0; margin:4px;;color: green ;color:blue ';
const style_2 = ' other-key : 0 ; padding:2px; COLOR:green; color: blue';
// https://github.com/sveltejs/svelte/issues/15309
export default test({
props: {
style: style_1
},
html: `
<div style="${style_1}"></div>
<div style="${style_1}"></div>
<custom-element style="${style_1}"></custom-element>
<custom-element style="${style_1}"></custom-element>
`,
async test({ assert, target, component }) {
component.style = style_2;
flushSync();
assert.htmlEqual(
target.innerHTML,
`
<div style="${style_2}"></div>
<div style="${style_2}"></div>
<custom-element style="${style_2}"></custom-element>
<custom-element style="${style_2}"></custom-element>
`
);
component.style = '';
flushSync();
assert.htmlEqual(
target.innerHTML,
`
<div style=""></div>
<div style=""></div>
<custom-element style=""></custom-element>
<custom-element style=""></custom-element>
`
);
component.style = null;
flushSync();
assert.htmlEqual(
target.innerHTML,
`
<div></div>
<div></div>
<custom-element></custom-element>
<custom-element></custom-element>
`
);
}
});

@ -0,0 +1,9 @@
<script>
let { style } = $props();
</script>
<div {style}></div>
<div {...{style}}></div>
<custom-element {style}></custom-element>
<custom-element {...{style}}></custom-element>
Loading…
Cancel
Save