diff --git a/.changeset/ninety-dingos-walk.md b/.changeset/ninety-dingos-walk.md new file mode 100644 index 0000000000..9a86f55552 --- /dev/null +++ b/.changeset/ninety-dingos-walk.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: keep fallback value after spread update not setting that prop diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 00e6ee9911..cd28dea29c 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1430,6 +1430,7 @@ export function prop_source(props_obj, key, default_value, call_default_value) { .i; let ignore_next1 = false; let ignore_next2 = false; + let did_update_to_defined = !should_set_default_value; let mount = true; sync_effect(() => { @@ -1445,8 +1446,13 @@ export function prop_source(props_obj, key, default_value, call_default_value) { return; } - if (not_equal(immutable, propagating_value, source_signal.v)) { + if ( + // Ensure that updates from undefined to undefined are ignored + (did_update_to_defined || propagating_value !== undefined) && + not_equal(immutable, propagating_value, source_signal.v) + ) { ignore_next2 = true; + did_update_to_defined = true; // TODO figure out why we need it this way and the explain in a comment; // some tests fail is we just do set_signal_value(source_signal, propagating_value) untrack(() => set_signal_value(source_signal, propagating_value)); @@ -1469,6 +1475,7 @@ export function prop_source(props_obj, key, default_value, call_default_value) { if (not_equal(immutable, propagating_value, possible_signal.v)) { ignore_next1 = true; + did_update_to_defined = true; untrack(() => update_bound_prop(propagating_value)); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/props-spread-fallback/Component.svelte b/packages/svelte/tests/runtime-runes/samples/props-spread-fallback/Component.svelte new file mode 100644 index 0000000000..6346eb5748 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-spread-fallback/Component.svelte @@ -0,0 +1,5 @@ + + +
{propA} {propB}
diff --git a/packages/svelte/tests/runtime-runes/samples/props-spread-fallback/_config.js b/packages/svelte/tests/runtime-runes/samples/props-spread-fallback/_config.js new file mode 100644 index 0000000000..f6be4fbae4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-spread-fallback/_config.js @@ -0,0 +1,55 @@ +import { test } from '../../test'; + +// Tests that fallback values are kept as long as the prop is not defined in the case of a spread +export default test({ + accessors: false, // so that propA actually becomes $.prop and not $.prop_source + html: ` + + +true fallback
+ `, + + async test({ assert, target }) { + const [propA, propB] = target.querySelectorAll('button'); + + await propA.click(); + assert.htmlEqual( + target.innerHTML, + ` + + +false fallback
+ ` + ); + + await propB.click(); + assert.htmlEqual( + target.innerHTML, + ` + + +false defined
+ ` + ); + + await propA.click(); + assert.htmlEqual( + target.innerHTML, + ` + + +true defined
+ ` + ); + + await propB.click(); + assert.htmlEqual( + target.innerHTML, + ` + + +true
+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/props-spread-fallback/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-spread-fallback/main.svelte new file mode 100644 index 0000000000..e3fd384d58 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-spread-fallback/main.svelte @@ -0,0 +1,12 @@ + + + + +