fix: preserve nullish option values when using spread attributes on `<option>`

pull/18426/head
ddoemonn 1 month ago
parent a9f48540e2
commit 63ff1e4181

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: preserve nullish option values when using spread attributes on `<option>`

@ -360,7 +360,9 @@ function set_attributes(
// a very rare edge case, and removing the attribute altogether isn't possible either
// for the <option value={undefined}> case, so we're not losing any functionality here.
// @ts-ignore
element.value = element.__value = '';
element.value = '';
// @ts-ignore
element.__value = value;
current[key] = value;
continue;
}

@ -0,0 +1,9 @@
<script>
let { value = $bindable(), options } = $props();
</script>
<select bind:value>
{#each options as { value, label, ...restprops }}
<option {value} {...restprops}>{label}</option>
{/each}
</select>

@ -0,0 +1,9 @@
<script>
let { value = $bindable(), options } = $props();
</script>
<select bind:value>
{#each options as { value, label }}
<option {value}>{label}</option>
{/each}
</select>

@ -0,0 +1,49 @@
import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
async test({ assert, target, component }) {
const [select0, select1] = target.querySelectorAll('select');
ok(select0);
ok(select1);
/**
* @param {HTMLOptionElement} option
*/
function get_option_value(option) {
if ('__value' in option) {
// @ts-ignore
return option.__value;
}
return option.value;
}
/**
* @param {HTMLSelectElement} select
* @param {number} index
*/
function select_at(select, index) {
select.selectedIndex = index;
select.dispatchEvent(new Event('change', { bubbles: true }));
flushSync();
}
for (let i = 0; i < select0.options.length; i += 1) {
assert.deepEqual(
get_option_value(select1.options[i]),
get_option_value(select0.options[i]),
`option __value should match at index ${i}`
);
}
// nullish values all serialize to '' in the DOM, so select a distinct option first
select_at(select0, 6);
select_at(select1, 6);
select_at(select0, 0);
select_at(select1, 0);
assert.equal(component.value0, undefined);
assert.equal(component.value1, undefined);
}
});

@ -0,0 +1,23 @@
<script>
import SelectOptionWithoutSpread from './SelectOptionWithoutSpread.svelte';
import SelectOptionWithSpread from './SelectOptionWithSpread.svelte';
let value0 = $state();
let value1 = $state();
const options = [
{ value: undefined, label: 'undefined' },
{ value: null, label: 'null' },
{ value: '', label: '""' },
{ value: false, label: 'false' },
{ value: true, label: 'true' },
{ value: 0, label: '0' },
{ value: 1, label: '1' },
{ value: '0', label: '"0"' },
{ value: '1', label: '"1"' },
{ value: { prop: 1 }, label: '{ prop: 1 }' }
];
</script>
<SelectOptionWithoutSpread bind:value={value0} {options} />
<SelectOptionWithSpread bind:value={value1} {options} />
Loading…
Cancel
Save