mirror of https://github.com/sveltejs/svelte
Merge 72eef03b23 into 44a7813730
commit
b932e6bdf5
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': minor
|
||||
---
|
||||
|
||||
feat: support `defaultValue` on `<select>`
|
||||
@ -0,0 +1,55 @@
|
||||
import { test } from '../../test';
|
||||
import { flushSync } from 'svelte';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
/**
|
||||
* @param {NodeListOf<any>} options
|
||||
* @param {any[]} selected
|
||||
*/
|
||||
function check_options(options, selected) {
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
assert.equal(options[i].selected, selected[i], `option ${i}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLSelectElement} select
|
||||
* @param {number[]} indexes
|
||||
*/
|
||||
function select_options(select, indexes) {
|
||||
const options = select.querySelectorAll('option');
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
options[i].selected = indexes.includes(i);
|
||||
}
|
||||
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
const reset = /** @type {HTMLInputElement} */ (target.querySelector('input[type=reset]'));
|
||||
const [test1, test2] = target.querySelectorAll('select');
|
||||
const span = /** @type {HTMLSpanElement} */ (target.querySelector('span'));
|
||||
|
||||
// every option named by defaultValue is selected, not just none of them
|
||||
check_options(test1.querySelectorAll('option'), [true, false, true]);
|
||||
|
||||
// an explicit value still wins over defaultValue
|
||||
check_options(test2.querySelectorAll('option'), [false, false, true]);
|
||||
assert.htmlEqual(span.innerHTML, 'c');
|
||||
|
||||
// change both selections, then reset the form
|
||||
select_options(test1, [1]);
|
||||
select_options(test2, [0, 1]);
|
||||
flushSync();
|
||||
|
||||
assert.htmlEqual(span.innerHTML, 'a,b');
|
||||
|
||||
reset.click();
|
||||
await Promise.resolve();
|
||||
flushSync();
|
||||
|
||||
// reset restores the default selection in both cases
|
||||
check_options(test1.querySelectorAll('option'), [true, false, true]);
|
||||
check_options(test2.querySelectorAll('option'), [true, false, true]);
|
||||
assert.htmlEqual(span.innerHTML, 'a,c');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,27 @@
|
||||
<script>
|
||||
let selected = $state(['c']);
|
||||
|
||||
let defaultValue = $state(['a', 'c']);
|
||||
</script>
|
||||
|
||||
<form>
|
||||
<!-- defaultValue=[a, c], no value: the default selection takes effect -->
|
||||
<select multiple {defaultValue}>
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<!-- defaultValue=[a, c], value=[c]: the explicit value wins -->
|
||||
<select multiple {defaultValue} bind:value={selected}>
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<input type="reset" value="Reset" />
|
||||
</form>
|
||||
|
||||
<p>
|
||||
<span class="test-1">{selected.join(',')}</span>
|
||||
</p>
|
||||
@ -0,0 +1,56 @@
|
||||
import { test } from '../../test';
|
||||
import { flushSync } from 'svelte';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
/**
|
||||
* @param {NodeListOf<any>} options
|
||||
* @param {any[]} selected
|
||||
*/
|
||||
function check_options(options, selected) {
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
assert.equal(options[i].selected, selected[i], `option ${i}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLOptionElement} option
|
||||
*/
|
||||
function select_option(option) {
|
||||
option.selected = true;
|
||||
option.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
const reset = /** @type {HTMLInputElement} */ (target.querySelector('input[type=reset]'));
|
||||
const [test1, test2] = target.querySelectorAll('select');
|
||||
const [test1_span] = target.querySelectorAll('span');
|
||||
|
||||
// a spread carrying defaultValue selects the matching option
|
||||
{
|
||||
const options = test1.querySelectorAll('option');
|
||||
check_options(options, [false, true, false]);
|
||||
assert.htmlEqual(test1_span.innerHTML, 'b');
|
||||
}
|
||||
|
||||
// value in the same spread still wins over defaultValue
|
||||
{
|
||||
const options = test2.querySelectorAll('option');
|
||||
check_options(options, [false, false, true]);
|
||||
}
|
||||
|
||||
// changing the selection and resetting goes back to defaultValue
|
||||
select_option(test1.querySelectorAll('option')[2]);
|
||||
select_option(test2.querySelectorAll('option')[0]);
|
||||
flushSync();
|
||||
|
||||
assert.htmlEqual(test1_span.innerHTML, 'c');
|
||||
|
||||
reset.click();
|
||||
await Promise.resolve();
|
||||
flushSync();
|
||||
|
||||
check_options(test1.querySelectorAll('option'), [false, true, false]);
|
||||
check_options(test2.querySelectorAll('option'), [false, true, false]);
|
||||
assert.htmlEqual(test1_span.innerHTML, 'b');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,26 @@
|
||||
<script>
|
||||
let props1 = $state({ defaultValue: 'b' });
|
||||
let props2 = $state({ defaultValue: 'b', value: 'c' });
|
||||
|
||||
let selected = $state();
|
||||
</script>
|
||||
|
||||
<form>
|
||||
<!-- spread carrying defaultValue, no value -->
|
||||
<select {...props1} bind:value={selected}>
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<!-- spread carrying both, value wins -->
|
||||
<select {...props2}>
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<input type="reset" value="Reset" />
|
||||
</form>
|
||||
|
||||
<p><span class="test-1">{selected}</span></p>
|
||||
@ -0,0 +1,70 @@
|
||||
import { test } from '../../test';
|
||||
import { flushSync } from 'svelte';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
/**
|
||||
* @param {NodeListOf<any>} options
|
||||
* @param {any[]} selected
|
||||
*/
|
||||
function check_options(options, selected) {
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
assert.equal(options[i].selected, selected[i], `option ${i}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLOptionElement} option
|
||||
*/
|
||||
function select_option(option) {
|
||||
option.selected = true;
|
||||
option.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
const reset = /** @type {HTMLInputElement} */ (target.querySelector('input[type=reset]'));
|
||||
const [test1, test2, test3] = target.querySelectorAll('select');
|
||||
const [test1_span, test2_span, test3_span] = target.querySelectorAll('span');
|
||||
|
||||
// defaultValue selects the matching option when no value is given
|
||||
{
|
||||
const options = test1.querySelectorAll('option');
|
||||
check_options(options, [false, true, false]);
|
||||
assert.htmlEqual(test1_span.innerHTML, 'b');
|
||||
}
|
||||
|
||||
// an explicit value wins over defaultValue, but reset goes back to defaultValue
|
||||
{
|
||||
const options = test2.querySelectorAll('option');
|
||||
check_options(options, [false, false, true]);
|
||||
assert.htmlEqual(test2_span.innerHTML, 'c');
|
||||
}
|
||||
|
||||
// static defaultValue behaves the same as the dynamic one
|
||||
{
|
||||
const options = test3.querySelectorAll('option');
|
||||
check_options(options, [false, true, false]);
|
||||
assert.htmlEqual(test3_span.innerHTML, 'b');
|
||||
}
|
||||
|
||||
// change the selection, then reset the form
|
||||
select_option(test1.querySelectorAll('option')[2]);
|
||||
select_option(test2.querySelectorAll('option')[0]);
|
||||
select_option(test3.querySelectorAll('option')[2]);
|
||||
flushSync();
|
||||
|
||||
assert.htmlEqual(test1_span.innerHTML, 'c');
|
||||
assert.htmlEqual(test2_span.innerHTML, 'a');
|
||||
assert.htmlEqual(test3_span.innerHTML, 'c');
|
||||
|
||||
reset.click();
|
||||
await Promise.resolve();
|
||||
flushSync();
|
||||
|
||||
check_options(test1.querySelectorAll('option'), [false, true, false]);
|
||||
check_options(test2.querySelectorAll('option'), [false, true, false]);
|
||||
check_options(test3.querySelectorAll('option'), [false, true, false]);
|
||||
assert.htmlEqual(test1_span.innerHTML, 'b');
|
||||
assert.htmlEqual(test2_span.innerHTML, 'b');
|
||||
assert.htmlEqual(test3_span.innerHTML, 'b');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,38 @@
|
||||
<script>
|
||||
let selected1 = $state();
|
||||
let selected2 = $state('c');
|
||||
let selected3 = $state();
|
||||
|
||||
let defaultValue = $state('b');
|
||||
</script>
|
||||
|
||||
<form>
|
||||
<!-- defaultValue=b, value=undefined -->
|
||||
<select {defaultValue} bind:value={selected1}>
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<!-- defaultValue=b, value=c -->
|
||||
<select {defaultValue} bind:value={selected2}>
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<!-- static defaultValue -->
|
||||
<select defaultValue="b" bind:value={selected3}>
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<input type="reset" value="Reset" />
|
||||
</form>
|
||||
|
||||
<p>
|
||||
<span class="test-1">{selected1}</span>
|
||||
<span class="test-2">{selected2}</span>
|
||||
<span class="test-3">{selected3}</span>
|
||||
</p>
|
||||
@ -0,0 +1,12 @@
|
||||
<select>
|
||||
<option value="a">A</option>
|
||||
<option selected="" value="b">B</option>
|
||||
</select>
|
||||
<select>
|
||||
<option value="a">A</option>
|
||||
<option selected="" value="b">B</option>
|
||||
</select>
|
||||
<select>
|
||||
<option selected="" value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
</select>
|
||||
@ -0,0 +1,18 @@
|
||||
<script>
|
||||
const props = { defaultValue: 'b' };
|
||||
</script>
|
||||
|
||||
<select defaultValue="b">
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
</select>
|
||||
|
||||
<select {...props}>
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
</select>
|
||||
|
||||
<select defaultValue="b" value="a">
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
</select>
|
||||
Loading…
Reference in new issue