Skip to content

Commit d38820f

Browse files
committed
fix: make defaultValue work with spread
1 parent 8bee23e commit d38820f

File tree

4 files changed

+482
-1
lines changed

4 files changed

+482
-1
lines changed

.changeset/silent-tips-cover.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: make `defaultValue` work with spread

packages/svelte/src/internal/client/dom/elements/attributes.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,15 +347,27 @@ export function set_attributes(
347347
} else if (key === '__value' || (key === 'value' && value != null)) {
348348
// @ts-ignore
349349
element.value = element[key] = element.__value = value;
350+
} else if (key === 'defaultValue') {
351+
/** @type {HTMLInputElement} */ (element).defaultValue = value;
352+
} else if (key === 'defaultChecked') {
353+
/** @type {HTMLInputElement} */ (element).defaultChecked = value;
354+
} else if (key === 'selected' && is_option_element) {
355+
set_selected(/** @type {HTMLOptionElement} */ (element), value);
350356
} else {
351357
var name = key;
352358
if (!preserve_attribute_case) {
353359
name = normalize_attribute(name);
354360
}
355-
356361
if (value == null && !is_custom_element) {
357362
attributes[key] = null;
363+
let default_value_reset = /**@type {HTMLInputElement}*/ (element).defaultValue;
364+
let default_checked_reset = /**@type {HTMLInputElement}*/ (element).defaultChecked;
358365
element.removeAttribute(key);
366+
if (key === 'value') {
367+
/**@type {HTMLInputElement}*/ (element).defaultValue = default_value_reset;
368+
} else if (key === 'checked') {
369+
/**@type {HTMLInputElement}*/ (element).defaultChecked = default_checked_reset;
370+
}
359371
} else if (setters.includes(name) && (is_custom_element || typeof value !== 'string')) {
360372
// @ts-ignore
361373
element[name] = value;
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
import { test } from '../../test';
2+
import { flushSync } from 'svelte';
3+
4+
export default test({
5+
async test({ assert, target }) {
6+
/**
7+
* @param {NodeListOf<any>} inputs
8+
* @param {string} field
9+
* @param {any | any[]} value
10+
*/
11+
function check_inputs(inputs, field, value) {
12+
for (let i = 0; i < inputs.length; i++) {
13+
assert.equal(inputs[i][field], Array.isArray(value) ? value[i] : value, `field ${i}`);
14+
}
15+
}
16+
17+
/**
18+
* @param {any} input
19+
* @param {string} field
20+
* @param {any} value
21+
*/
22+
function set_input(input, field, value) {
23+
input[field] = value;
24+
input.dispatchEvent(
25+
new Event(typeof value === 'boolean' ? 'change' : 'input', { bubbles: true })
26+
);
27+
}
28+
29+
/**
30+
* @param {HTMLOptionElement} option
31+
*/
32+
function select_option(option) {
33+
option.selected = true;
34+
option.dispatchEvent(new Event('change', { bubbles: true }));
35+
}
36+
37+
const after_reset = [];
38+
39+
const reset = /** @type {HTMLInputElement} */ (target.querySelector('input[type=reset]'));
40+
const [test1, test2, test3, test4, test5, test6, test7, test14] =
41+
target.querySelectorAll('div');
42+
const [test8, test9, test10, test11] = target.querySelectorAll('select');
43+
const [
44+
test1_span,
45+
test2_span,
46+
test3_span,
47+
test4_span,
48+
test5_span,
49+
test6_span,
50+
test7_span,
51+
test8_span,
52+
test9_span,
53+
test10_span,
54+
test11_span
55+
] = target.querySelectorAll('span');
56+
57+
{
58+
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
59+
const inputs = test1.querySelectorAll('input, textarea');
60+
check_inputs(inputs, 'value', 'x');
61+
assert.htmlEqual(test1_span.innerHTML, 'x x x x');
62+
63+
for (const input of inputs) {
64+
set_input(input, 'value', 'foo');
65+
}
66+
flushSync();
67+
check_inputs(inputs, 'value', 'foo');
68+
assert.htmlEqual(test1_span.innerHTML, 'foo foo foo foo');
69+
70+
after_reset.push(() => {
71+
console.log('-------------');
72+
check_inputs(inputs, 'value', 'x');
73+
assert.htmlEqual(test1_span.innerHTML, 'x x x x');
74+
});
75+
}
76+
77+
{
78+
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
79+
const inputs = test2.querySelectorAll('input, textarea');
80+
check_inputs(inputs, 'value', 'x');
81+
assert.htmlEqual(test2_span.innerHTML, 'x x x x');
82+
83+
for (const input of inputs) {
84+
set_input(input, 'value', 'foo');
85+
}
86+
flushSync();
87+
check_inputs(inputs, 'value', 'foo');
88+
assert.htmlEqual(test2_span.innerHTML, 'foo foo foo foo');
89+
90+
after_reset.push(() => {
91+
console.log('-------------');
92+
check_inputs(inputs, 'value', 'x');
93+
assert.htmlEqual(test2_span.innerHTML, 'x x x x');
94+
});
95+
}
96+
97+
{
98+
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
99+
const inputs = test3.querySelectorAll('input, textarea');
100+
check_inputs(inputs, 'value', 'y');
101+
assert.htmlEqual(test3_span.innerHTML, 'y y y y');
102+
103+
for (const input of inputs) {
104+
set_input(input, 'value', 'foo');
105+
}
106+
flushSync();
107+
check_inputs(inputs, 'value', 'foo');
108+
assert.htmlEqual(test3_span.innerHTML, 'foo foo foo foo');
109+
110+
after_reset.push(() => {
111+
check_inputs(inputs, 'value', 'x');
112+
assert.htmlEqual(test3_span.innerHTML, 'x x x x');
113+
});
114+
}
115+
116+
{
117+
/** @type {NodeListOf<HTMLInputElement>} */
118+
const inputs = test4.querySelectorAll('input');
119+
check_inputs(inputs, 'checked', true);
120+
assert.htmlEqual(test4_span.innerHTML, 'true true');
121+
122+
for (const input of inputs) {
123+
set_input(input, 'checked', false);
124+
}
125+
flushSync();
126+
check_inputs(inputs, 'checked', false);
127+
assert.htmlEqual(test4_span.innerHTML, 'false false');
128+
129+
after_reset.push(() => {
130+
check_inputs(inputs, 'checked', true);
131+
assert.htmlEqual(test4_span.innerHTML, 'true true');
132+
});
133+
}
134+
135+
{
136+
/** @type {NodeListOf<HTMLInputElement>} */
137+
const inputs = test5.querySelectorAll('input');
138+
check_inputs(inputs, 'checked', true);
139+
assert.htmlEqual(test5_span.innerHTML, 'true true');
140+
141+
for (const input of inputs) {
142+
set_input(input, 'checked', false);
143+
}
144+
flushSync();
145+
check_inputs(inputs, 'checked', false);
146+
assert.htmlEqual(test5_span.innerHTML, 'false false');
147+
148+
after_reset.push(() => {
149+
check_inputs(inputs, 'checked', true);
150+
assert.htmlEqual(test5_span.innerHTML, 'true true');
151+
});
152+
}
153+
154+
{
155+
/** @type {NodeListOf<HTMLInputElement>} */
156+
const inputs = test6.querySelectorAll('input');
157+
check_inputs(inputs, 'checked', false);
158+
assert.htmlEqual(test6_span.innerHTML, 'false false');
159+
160+
after_reset.push(() => {
161+
check_inputs(inputs, 'checked', true);
162+
assert.htmlEqual(test6_span.innerHTML, 'true true');
163+
});
164+
}
165+
{
166+
/** @type {NodeListOf<HTMLInputElement>} */
167+
const inputs = test7.querySelectorAll('input');
168+
check_inputs(inputs, 'checked', true);
169+
assert.htmlEqual(test7_span.innerHTML, 'true');
170+
171+
after_reset.push(() => {
172+
check_inputs(inputs, 'checked', false);
173+
assert.htmlEqual(test7_span.innerHTML, 'false');
174+
});
175+
}
176+
177+
{
178+
/** @type {NodeListOf<HTMLOptionElement>} */
179+
const options = test8.querySelectorAll('option');
180+
check_inputs(options, 'selected', [false, true, false]);
181+
assert.htmlEqual(test8_span.innerHTML, 'b');
182+
183+
select_option(options[2]);
184+
flushSync();
185+
check_inputs(options, 'selected', [false, false, true]);
186+
assert.htmlEqual(test8_span.innerHTML, 'c');
187+
188+
after_reset.push(() => {
189+
check_inputs(options, 'selected', [false, true, false]);
190+
assert.htmlEqual(test8_span.innerHTML, 'b');
191+
});
192+
}
193+
194+
{
195+
/** @type {NodeListOf<HTMLOptionElement>} */
196+
const options = test9.querySelectorAll('option');
197+
check_inputs(options, 'selected', [false, true, false]);
198+
assert.htmlEqual(test9_span.innerHTML, 'b');
199+
200+
select_option(options[2]);
201+
flushSync();
202+
check_inputs(options, 'selected', [false, false, true]);
203+
assert.htmlEqual(test9_span.innerHTML, 'c');
204+
205+
after_reset.push(() => {
206+
check_inputs(options, 'selected', [false, true, false]);
207+
assert.htmlEqual(test9_span.innerHTML, 'b');
208+
});
209+
}
210+
211+
{
212+
/** @type {NodeListOf<HTMLOptionElement>} */
213+
const options = test10.querySelectorAll('option');
214+
check_inputs(options, 'selected', [false, false, true]);
215+
assert.htmlEqual(test10_span.innerHTML, 'c');
216+
217+
select_option(options[0]);
218+
flushSync();
219+
check_inputs(options, 'selected', [true, false, false]);
220+
assert.htmlEqual(test10_span.innerHTML, 'a');
221+
222+
after_reset.push(() => {
223+
check_inputs(options, 'selected', [false, true, false]);
224+
assert.htmlEqual(test10_span.innerHTML, 'b');
225+
});
226+
}
227+
228+
{
229+
/** @type {NodeListOf<HTMLOptionElement>} */
230+
const options = test11.querySelectorAll('option');
231+
check_inputs(options, 'selected', [false, false, true]);
232+
assert.htmlEqual(test11_span.innerHTML, 'c');
233+
234+
select_option(options[0]);
235+
flushSync();
236+
check_inputs(options, 'selected', [true, false, false]);
237+
assert.htmlEqual(test11_span.innerHTML, 'a');
238+
239+
after_reset.push(() => {
240+
check_inputs(options, 'selected', [false, true, false]);
241+
assert.htmlEqual(test11_span.innerHTML, 'b');
242+
});
243+
}
244+
245+
{
246+
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
247+
const inputs = test14.querySelectorAll('input, textarea');
248+
assert.equal(inputs[0].value, 'x');
249+
assert.equal(/** @type {HTMLInputElement} */ (inputs[1]).checked, true);
250+
// this is still missing...i have no idea how to fix this lol
251+
// assert.equal(inputs[2].value, 'x');
252+
253+
after_reset.push(() => {
254+
assert.equal(inputs[0].value, 'y');
255+
assert.equal(/** @type {HTMLInputElement} */ (inputs[1]).checked, false);
256+
assert.equal(inputs[2].value, 'y');
257+
});
258+
}
259+
260+
reset.click();
261+
await Promise.resolve();
262+
flushSync();
263+
after_reset.forEach((fn) => fn());
264+
}
265+
});

0 commit comments

Comments
 (0)