diff --git a/.changeset/lazy-spiders-think.md b/.changeset/lazy-spiders-think.md new file mode 100644 index 0000000000..a0e130fc9c --- /dev/null +++ b/.changeset/lazy-spiders-think.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: handle dynamic selects with falsy select values diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index d5a06d4cc8..c741053724 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -881,9 +881,10 @@ export function bind_window_size(type, update) { const callback = () => update(window[type]); listen_to_events(window, ['resize'], callback); } + /** * Finds the containing ` + + + + + + `, + + test({ assert, component, target }) { + assert.equal(component.selected, 'a'); + const select = target.querySelector('select'); + ok(select); + const options = [...target.querySelectorAll('option')]; + + // first enabled option should be selected + assert.equal(select.value, 'a'); + assert.ok(options[1].selected); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-select-initial-value-undefined-3/main.svelte b/packages/svelte/tests/runtime-legacy/samples/binding-select-initial-value-undefined-3/main.svelte new file mode 100644 index 0000000000..0e2c268631 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/binding-select-initial-value-undefined-3/main.svelte @@ -0,0 +1,12 @@ + + +

selected: {selected}

+ + diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-select-unmatched-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-select-unmatched-2/_config.js new file mode 100644 index 0000000000..85d61eeeec --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/binding-select-unmatched-2/_config.js @@ -0,0 +1,112 @@ +import { ok, test } from '../../test'; + +// // same test as the previous one, but with a dynamic each block +export default test({ + html: ` +

selected:

+ + + +

selected:

+ `, + + async test({ assert, component, target }) { + const select = target.querySelector('select'); + ok(select); + + const options = [...target.querySelectorAll('option')]; + + assert.equal(component.selected, null); + + // no option should be selected since none of the options matches the bound value + assert.equal(select.value, ''); + assert.equal(select.selectedIndex, -1); + assert.ok(!options[0].selected); + + component.selected = 'a'; // first option should now be selected + assert.equal(select.value, 'a'); + assert.ok(options[0].selected); + + assert.htmlEqual( + target.innerHTML, + ` +

selected: a

+ + + +

selected: a

+ ` + ); + + component.selected = 'd'; // doesn't match an option + + // now no option should be selected again + assert.equal(select.value, ''); + assert.equal(select.selectedIndex, -1); + assert.ok(!options[0].selected); + + assert.htmlEqual( + target.innerHTML, + ` +

selected: d

+ + + +

selected: d

+ ` + ); + + component.selected = 'b'; // second option should now be selected + assert.equal(select.value, 'b'); + assert.ok(options[1].selected); + + assert.htmlEqual( + target.innerHTML, + ` +

selected: b

+ + + +

selected: b

+ ` + ); + + component.selected = undefined; // also doesn't match an option + + // now no option should be selected again + assert.equal(select.value, ''); + assert.equal(select.selectedIndex, -1); + assert.ok(!options[0].selected); + + assert.htmlEqual( + target.innerHTML, + ` +

selected:

+ + + +

selected:

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-select-unmatched-2/main.svelte b/packages/svelte/tests/runtime-legacy/samples/binding-select-unmatched-2/main.svelte new file mode 100644 index 0000000000..8acdb5ddae --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/binding-select-unmatched-2/main.svelte @@ -0,0 +1,14 @@ + + +

selected: {selected}

+ + + +

selected: {selected}