Skip to content

Commit

Permalink
fix: treat undefined and null the same for the initial input value (
Browse files Browse the repository at this point in the history
#14562)

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

Fixes #14558

* test

* same for checked
  • Loading branch information
dummdidumm authored Dec 5, 2024
1 parent 1ffce92 commit 73b3cf7
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 66 deletions.
5 changes: 5 additions & 0 deletions .changeset/cool-clocks-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: treat `undefined` and `null` the same for the initial input value
20 changes: 17 additions & 3 deletions packages/svelte/src/internal/client/dom/elements/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -48,7 +49,9 @@ export default test({
test6_span,
test7_span,
test8_span,
test9_span
test9_span,
test10_span,
test11_span
] = target.querySelectorAll('span');

{
Expand All @@ -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');
Expand All @@ -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');
Expand Down
Loading

0 comments on commit 73b3cf7

Please sign in to comment.