From 864de81bb67372af9783081b039d946dcdb5aabf Mon Sep 17 00:00:00 2001 From: Nic Polumeyv Date: Wed, 26 Aug 2026 04:10:48 -0400 Subject: [PATCH] fix: keep the current selection of a ``, + * by the compiled output or by `attribute_effect` for spreads. * @param {HTMLSelectElement} select */ export function init_select(select) { @@ -107,7 +127,7 @@ export function init_select(select) { if (entries.every(is_selectedcontent_mutation)) return; if ('__defaultValue' in select) { - set_default_select_value(select, select.__defaultValue, false); + apply_default_select_value(select, false); } if ('__value' in select) { @@ -204,8 +224,6 @@ export function bind_select_value(select, get, set = get) { select.__value = value; mounting = false; }); - - init_select(select); } /** @param {HTMLOptionElement} option */ diff --git a/packages/svelte/tests/runtime-runes/samples/form-default-value-select/_config.js b/packages/svelte/tests/runtime-runes/samples/form-default-value-select/_config.js index 5d0c964969..784d20a0ce 100644 --- a/packages/svelte/tests/runtime-runes/samples/form-default-value-select/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/form-default-value-select/_config.js @@ -62,7 +62,7 @@ export default test({ button('add').click(); flushSync(); await Promise.resolve(); - check(selects[3], [true, false]); + check(selects[3], [false, true]); assert.equal(selects[3].options[1].defaultSelected, true); reset.click(); diff --git a/packages/svelte/tests/runtime-runes/samples/select-default-value-preserve-selection/_config.js b/packages/svelte/tests/runtime-runes/samples/select-default-value-preserve-selection/_config.js new file mode 100644 index 0000000000..d409732ab8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/select-default-value-preserve-selection/_config.js @@ -0,0 +1,52 @@ +import { flushSync } from 'svelte'; +import { ok, test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [nothing, unmatched, spread, late, touched] = target.querySelectorAll('select'); + const [change_default, change_spread, load, add] = target.querySelectorAll('button'); + const p = target.querySelector('p'); + ok(p); + + assert.equal(nothing.selectedIndex, -1); + assert.equal(unmatched.selectedIndex, -1); + assert.equal(spread.selectedIndex, -1); + assert.equal(late.selectedIndex, -1); + assert.equal(touched.value, 'b'); + + change_default.click(); + change_spread.click(); + flushSync(); + + assert.equal(nothing.selectedIndex, -1); + assert.equal(unmatched.selectedIndex, -1); + assert.equal(spread.selectedIndex, -1); + assert.equal(spread.className, 'two'); + assert.htmlEqual(p.innerHTML, 'zzz null'); + assert.deepEqual( + [...nothing.options].map((option) => option.defaultSelected), + [true, false] + ); + + // a default change never moves the current selection + assert.equal(touched.value, 'b'); + + // a default whose option arrives later selects it + load.click(); + flushSync(); + await Promise.resolve(); + assert.equal(late.value, 'b'); + + // a user selection survives option mutations and default changes + touched.options[2].selected = true; + touched.dispatchEvent(new Event('change', { bubbles: true })); + add.click(); + flushSync(); + await Promise.resolve(); + assert.equal(touched.value, 'c'); + assert.deepEqual( + [...touched.options].map((option) => option.defaultSelected), + [true, false, false, false] + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/select-default-value-preserve-selection/main.svelte b/packages/svelte/tests/runtime-runes/samples/select-default-value-preserve-selection/main.svelte new file mode 100644 index 0000000000..c7fdea482c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/select-default-value-preserve-selection/main.svelte @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + +

{unmatched} {String(nothing)}

diff --git a/packages/svelte/tests/runtime-runes/samples/select-default-value-single-observer/_config.js b/packages/svelte/tests/runtime-runes/samples/select-default-value-single-observer/_config.js new file mode 100644 index 0000000000..411a7868f7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/select-default-value-single-observer/_config.js @@ -0,0 +1,30 @@ +import { test } from '../../test'; + +let observers = 0; +const MutationObserver = globalThis.MutationObserver; + +export default test({ + before_test() { + observers = 0; + globalThis.MutationObserver = class extends MutationObserver { + /** @param {MutationCallback} callback */ + constructor(callback) { + super(callback); + observers++; + } + }; + }, + + after_test() { + globalThis.MutationObserver = MutationObserver; + }, + + test({ assert, target }) { + const selects = target.querySelectorAll('select'); + for (const select of selects) { + assert.equal(select.selectedIndex, 0); + assert.equal(select.options[1].defaultSelected, true); + } + assert.equal(observers, selects.length); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/select-default-value-single-observer/main.svelte b/packages/svelte/tests/runtime-runes/samples/select-default-value-single-observer/main.svelte new file mode 100644 index 0000000000..7754b3c6fd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/select-default-value-single-observer/main.svelte @@ -0,0 +1,21 @@ + + + + + + +