fix: treat `undefined` and `null` the same for the initial input value (#14562)

* fix: treat `undefined` and `null` the same for the initial input value

Fixes #14558

* test

* same for checked
pull/14570/head
Simon H 3 weeks ago committed by GitHub
parent 1ffce92d90
commit 73b3cf72d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: treat `undefined` and `null` the same for the initial input value

@ -60,13 +60,19 @@ export function remove_input_defaults(input) {
export function set_value(element, value) {
// @ts-expect-error
var attributes = (element.__attributes ??= {});
if (
attributes.value === (attributes.value = value) ||
attributes.value ===
(attributes.value =
// treat null and undefined the same for the initial value
value ?? undefined) ||
// @ts-expect-error
// `progress` elements always need their value set when its `0`
(element.value === value && (value !== 0 || element.nodeName !== 'PROGRESS'))
)
) {
return;
}
// @ts-expect-error
element.value = value;
}
@ -79,7 +85,15 @@ export function set_checked(element, checked) {
// @ts-expect-error
var attributes = (element.__attributes ??= {});
if (attributes.checked === (attributes.checked = checked)) return;
if (
attributes.checked ===
(attributes.checked =
// treat null and undefined the same for the initial value
checked ?? undefined)
) {
return;
}
// @ts-expect-error
element.checked = checked;
}

@ -37,8 +37,9 @@ export default test({
const after_reset = [];
const reset = /** @type {HTMLInputElement} */ (target.querySelector('input[type=reset]'));
const [test1, test2, test3, test4, test5, test12] = target.querySelectorAll('div');
const [test6, test7, test8, test9] = target.querySelectorAll('select');
const [test1, test2, test3, test4, test5, test6, test7, test14] =
target.querySelectorAll('div');
const [test8, test9, test10, test11] = target.querySelectorAll('select');
const [
test1_span,
test2_span,
@ -48,7 +49,9 @@ export default test({
test6_span,
test7_span,
test8_span,
test9_span
test9_span,
test10_span,
test11_span
] = target.querySelectorAll('span');
{
@ -74,8 +77,8 @@ export default test({
{
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
const inputs = test2.querySelectorAll('input, textarea');
check_inputs(inputs, 'value', 'y');
assert.htmlEqual(test2_span.innerHTML, 'y y y y');
check_inputs(inputs, 'value', 'x');
assert.htmlEqual(test2_span.innerHTML, 'x x x x');
for (const input of inputs) {
set_input(input, 'value', 'foo');
@ -85,125 +88,164 @@ export default test({
assert.htmlEqual(test2_span.innerHTML, 'foo foo foo foo');
after_reset.push(() => {
console.log('-------------');
check_inputs(inputs, 'value', 'x');
assert.htmlEqual(test2_span.innerHTML, 'x x x x');
});
}
{
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
const inputs = test3.querySelectorAll('input, textarea');
check_inputs(inputs, 'value', 'y');
assert.htmlEqual(test3_span.innerHTML, 'y y y y');
for (const input of inputs) {
set_input(input, 'value', 'foo');
}
flushSync();
check_inputs(inputs, 'value', 'foo');
assert.htmlEqual(test3_span.innerHTML, 'foo foo foo foo');
after_reset.push(() => {
check_inputs(inputs, 'value', 'x');
assert.htmlEqual(test3_span.innerHTML, 'x x x x');
});
}
{
/** @type {NodeListOf<HTMLInputElement>} */
const inputs = test3.querySelectorAll('input');
const inputs = test4.querySelectorAll('input');
check_inputs(inputs, 'checked', true);
assert.htmlEqual(test3_span.innerHTML, 'true true');
assert.htmlEqual(test4_span.innerHTML, 'true true');
for (const input of inputs) {
set_input(input, 'checked', false);
}
flushSync();
check_inputs(inputs, 'checked', false);
assert.htmlEqual(test3_span.innerHTML, 'false false');
assert.htmlEqual(test4_span.innerHTML, 'false false');
after_reset.push(() => {
check_inputs(inputs, 'checked', true);
assert.htmlEqual(test3_span.innerHTML, 'true true');
assert.htmlEqual(test4_span.innerHTML, 'true true');
});
}
{
/** @type {NodeListOf<HTMLInputElement>} */
const inputs = test4.querySelectorAll('input');
const inputs = test5.querySelectorAll('input');
check_inputs(inputs, 'checked', true);
assert.htmlEqual(test5_span.innerHTML, 'true true');
for (const input of inputs) {
set_input(input, 'checked', false);
}
flushSync();
check_inputs(inputs, 'checked', false);
assert.htmlEqual(test4_span.innerHTML, 'false false');
assert.htmlEqual(test5_span.innerHTML, 'false false');
after_reset.push(() => {
check_inputs(inputs, 'checked', true);
assert.htmlEqual(test4_span.innerHTML, 'true true');
assert.htmlEqual(test5_span.innerHTML, 'true true');
});
}
{
/** @type {NodeListOf<HTMLInputElement>} */
const inputs = test5.querySelectorAll('input');
const inputs = test6.querySelectorAll('input');
check_inputs(inputs, 'checked', false);
assert.htmlEqual(test6_span.innerHTML, 'false false');
after_reset.push(() => {
check_inputs(inputs, 'checked', true);
assert.htmlEqual(test6_span.innerHTML, 'true true');
});
}
{
/** @type {NodeListOf<HTMLInputElement>} */
const inputs = test7.querySelectorAll('input');
check_inputs(inputs, 'checked', true);
assert.htmlEqual(test5_span.innerHTML, 'true');
assert.htmlEqual(test7_span.innerHTML, 'true');
after_reset.push(() => {
check_inputs(inputs, 'checked', false);
assert.htmlEqual(test5_span.innerHTML, 'false');
assert.htmlEqual(test7_span.innerHTML, 'false');
});
}
{
/** @type {NodeListOf<HTMLOptionElement>} */
const options = test6.querySelectorAll('option');
const options = test8.querySelectorAll('option');
check_inputs(options, 'selected', [false, true, false]);
assert.htmlEqual(test6_span.innerHTML, 'b');
assert.htmlEqual(test8_span.innerHTML, 'b');
select_option(options[2]);
flushSync();
check_inputs(options, 'selected', [false, false, true]);
assert.htmlEqual(test6_span.innerHTML, 'c');
assert.htmlEqual(test8_span.innerHTML, 'c');
after_reset.push(() => {
check_inputs(options, 'selected', [false, true, false]);
assert.htmlEqual(test6_span.innerHTML, 'b');
assert.htmlEqual(test8_span.innerHTML, 'b');
});
}
{
/** @type {NodeListOf<HTMLOptionElement>} */
const options = test7.querySelectorAll('option');
const options = test9.querySelectorAll('option');
check_inputs(options, 'selected', [false, true, false]);
assert.htmlEqual(test7_span.innerHTML, 'b');
assert.htmlEqual(test9_span.innerHTML, 'b');
select_option(options[2]);
flushSync();
check_inputs(options, 'selected', [false, false, true]);
assert.htmlEqual(test7_span.innerHTML, 'c');
assert.htmlEqual(test9_span.innerHTML, 'c');
after_reset.push(() => {
check_inputs(options, 'selected', [false, true, false]);
assert.htmlEqual(test7_span.innerHTML, 'b');
assert.htmlEqual(test9_span.innerHTML, 'b');
});
}
{
/** @type {NodeListOf<HTMLOptionElement>} */
const options = test8.querySelectorAll('option');
const options = test10.querySelectorAll('option');
check_inputs(options, 'selected', [false, false, true]);
assert.htmlEqual(test8_span.innerHTML, 'c');
assert.htmlEqual(test10_span.innerHTML, 'c');
select_option(options[0]);
flushSync();
check_inputs(options, 'selected', [true, false, false]);
assert.htmlEqual(test8_span.innerHTML, 'a');
assert.htmlEqual(test10_span.innerHTML, 'a');
after_reset.push(() => {
check_inputs(options, 'selected', [false, true, false]);
assert.htmlEqual(test8_span.innerHTML, 'b');
assert.htmlEqual(test10_span.innerHTML, 'b');
});
}
{
/** @type {NodeListOf<HTMLOptionElement>} */
const options = test9.querySelectorAll('option');
const options = test11.querySelectorAll('option');
check_inputs(options, 'selected', [false, false, true]);
assert.htmlEqual(test9_span.innerHTML, 'c');
assert.htmlEqual(test11_span.innerHTML, 'c');
select_option(options[0]);
flushSync();
check_inputs(options, 'selected', [true, false, false]);
assert.htmlEqual(test9_span.innerHTML, 'a');
assert.htmlEqual(test11_span.innerHTML, 'a');
after_reset.push(() => {
check_inputs(options, 'selected', [false, true, false]);
assert.htmlEqual(test9_span.innerHTML, 'b');
assert.htmlEqual(test11_span.innerHTML, 'b');
});
}
{
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
const inputs = test12.querySelectorAll('input, textarea');
const inputs = test14.querySelectorAll('input, textarea');
assert.equal(inputs[0].value, 'x');
assert.equal(/** @type {HTMLInputElement} */ (inputs[1]).checked, true);
assert.equal(inputs[2].value, 'x');

@ -7,25 +7,37 @@
let value6 = $state();
let value7 = $state();
let value8 = $state();
let value9 = $state('y');
let value10 = $state('y');
let value11 = $state('y');
let value12 = $state('y');
let value13 = $state('y');
let value14 = $state('y');
let value15 = $state('y');
let value16 = $state('y');
let value9 = $state(null);
let value10 = $state(null);
let value11 = $state(null);
let value12 = $state(null);
let value13 = $state(null);
let value14 = $state(null);
let value15 = $state(null);
let value16 = $state(null);
let value17 = $state('y');
let value18 = $state('y');
let value19 = $state('y');
let value20 = $state('y');
let value21 = $state('y');
let value22 = $state('y');
let value23 = $state('y');
let value24 = $state('y');
let checked1 = $state();
let checked2 = $state();
let checked3 = $state();
let checked4 = $state();
let checked5 = $state(false);
let checked6 = $state(false);
let checked7 = $state(false);
let checked8 = $state(false);
let checked9 = $state(true);
let checked10 = $state(true);
let checked5 = $state(null);
let checked6 = $state(null);
let checked7 = $state(null);
let checked8 = $state(null);
let checked9 = $state(false);
let checked10 = $state(false);
let checked11 = $state(false);
let checked12 = $state(false);
let checked13 = $state(true);
let checked14 = $state(true);
let selected1 = $state();
@ -53,7 +65,7 @@
<textarea defaultValue="x" bind:value={value8}></textarea>
</div>
<!-- defaultValue=x, value=y -->
<!-- defaultValue=x, value=null -->
<div class="test-2">
<input {defaultValue} bind:value={value9} />
<input {defaultValue} value={value10} />
@ -65,27 +77,47 @@
<textarea defaultValue="x" bind:value={value16}></textarea>
</div>
<!-- defaultValue=x, value=y -->
<div class="test-3">
<input {defaultValue} bind:value={value17} />
<input {defaultValue} value={value18} />
<input defaultValue="x" value={value19} />
<input defaultValue="x" bind:value={value20} />
<textarea {defaultValue} value={value21}></textarea>
<textarea {defaultValue} bind:value={value22}></textarea>
<textarea defaultValue="x" value={value23}></textarea>
<textarea defaultValue="x" bind:value={value24}></textarea>
</div>
<p>Input checked</p>
<!-- defaultChecked=true, checked=undefined -->
<div class="test-3">
<div class="test-4">
<input type="checkbox" {defaultChecked} checked={checked1} />
<input type="checkbox" {defaultChecked} bind:checked={checked2} />
<input type="checkbox" defaultChecked checked={checked3} />
<input type="checkbox" defaultChecked bind:checked={checked4} />
</div>
<!-- defaultChecked=true, checked=false -->
<div class="test-4">
<!-- defaultChecked=true, checked=null -->
<div class="test-5">
<input type="checkbox" {defaultChecked} checked={checked5} />
<input type="checkbox" {defaultChecked} bind:checked={checked6} />
<input type="checkbox" defaultChecked checked={checked7} />
<input type="checkbox" defaultChecked bind:checked={checked8} />
</div>
<!-- defaultChecked=true, checked=false -->
<div class="test-6">
<input type="checkbox" {defaultChecked} checked={checked9} />
<input type="checkbox" {defaultChecked} bind:checked={checked10} />
<input type="checkbox" defaultChecked checked={checked11} />
<input type="checkbox" defaultChecked bind:checked={checked12} />
</div>
<!-- defaultChecked=false, checked=true -->
<div class="test-5">
<input type="checkbox" defaultChecked={false} checked={checked9} />
<input type="checkbox" defaultChecked={false} bind:checked={checked10} />
<div class="test-7">
<input type="checkbox" defaultChecked={false} checked={checked13} />
<input type="checkbox" defaultChecked={false} bind:checked={checked14} />
</div>
<!-- no support for bind:group; too complex + we may deprecate it in favor of bind:checked={get,set} -->
@ -138,7 +170,7 @@
</select>
<p>Static values</p>
<div class="test-12">
<div class="test-14">
<input value="x" defaultValue="y" />
<input type="checkbox" checked defaultChecked={false} />
<textarea defaultValue="y">x</textarea>
@ -151,13 +183,15 @@
Bound values:
<span class="test-1">{value1} {value3} {value6} {value8}</span>
<span class="test-2">{value9} {value12} {value14} {value16}</span>
<span class="test-3">{checked2} {checked4}</span>
<span class="test-4">{checked6} {checked8}</span>
<span class="test-5">{checked10}</span>
<span class="test-6">{selected1}</span>
<span class="test-7">{selected2}</span>
<span class="test-8">{selected3}</span>
<span class="test-9">{selected4}</span>
<span class="test-10">{selected5}</span>
<span class="test-11">{selected6}</span>
<span class="test-3">{value17} {value20} {value22} {value24}</span>
<span class="test-4">{checked2} {checked4}</span>
<span class="test-5">{checked6} {checked8}</span>
<span class="test-6">{checked10} {checked12}</span>
<span class="test-7">{checked14}</span>
<span class="test-8">{selected1}</span>
<span class="test-9">{selected2}</span>
<span class="test-10">{selected3}</span>
<span class="test-11">{selected4}</span>
<span class="test-12">{selected5}</span>
<span class="test-13">{selected6}</span>
</p>

Loading…
Cancel
Save