Skip to content

Commit

Permalink
set select multiple value with spread (sveltejs#4894)
Browse files Browse the repository at this point in the history
  • Loading branch information
tanhauhau committed May 26, 2020
1 parent d61a7a0 commit 24ef4e1
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

* Update `<select>` with `bind:value` when the available `<option>`s change ([#1764](https://github.com/sveltejs/svelte/issues/1764))
* Fix inconsistencies when setting a two-way bound `<input>` to `undefined` ([#3569](https://github.com/sveltejs/svelte/issues/3569))
* Fix setting `<select multiple>` when there are spread attributes ([#4392](https://github.com/sveltejs/svelte/issues/4392))
* Fix resize listening on certain older browsers ([#4752](https://github.com/sveltejs/svelte/issues/4752))
* Add `a11y-no-onchange` warning ([#4788](https://github.com/sveltejs/svelte/pull/4788))
* Add `muted` binding for media elements ([#2998](https://github.com/sveltejs/svelte/issues/2998))
Expand Down
25 changes: 6 additions & 19 deletions src/compiler/compile/render_dom/wrappers/Element/Attribute.ts
Expand Up @@ -102,25 +102,12 @@ export default class AttributeWrapper {
} else if (is_select_value_attribute) {
// annoying special case
const is_multiple_select = element.node.get_static_attribute_value('multiple');
const i = block.get_unique_name('i');
const option = block.get_unique_name('option');

const if_statement = is_multiple_select
? b`
${option}.selected = ~${last}.indexOf(${option}.__value);`
: b`
if (${option}.__value === ${last}) {
${option}.selected = true;
${{ type: 'BreakStatement' }};
}`; // TODO the BreakStatement is gross, but it's unsyntactic otherwise...

updater = b`
for (var ${i} = 0; ${i} < ${element.var}.options.length; ${i} += 1) {
var ${option} = ${element.var}.options[${i}];
${if_statement}
}
`;

if (is_multiple_select) {
updater = b`@select_options(${element.var}, ${last});`;
} else {
updater = b`@select_option(${element.var}, ${last});`;
}

block.chunks.mount.push(b`
${last} = ${value};
Expand Down
19 changes: 18 additions & 1 deletion src/compiler/compile/render_dom/wrappers/Element/index.ts
Expand Up @@ -705,10 +705,27 @@ export default class ElementWrapper extends Wrapper {
);

block.chunks.update.push(b`
${fn}(${this.var}, @get_spread_update(${levels}, [
${fn}(${this.var}, ${data} = @get_spread_update(${levels}, [
${updates}
]));
`);

// handle edge cases for elements
if (this.node.name === 'select') {
const dependencies = new Set();
for (const attr of this.attributes) {
for (const dep of attr.node.dependencies) {
dependencies.add(dep);
}
}

block.chunks.mount.push(b`
if (${data}.multiple) @select_options(${this.var}, ${data}.value);
`);
block.chunks.update.push(b`
if (${block.renderer.dirty(Array.from(dependencies))} && ${data}.multiple) @select_options(${this.var}, ${data}.value);
`);
}
}

add_transitions(
Expand Down
22 changes: 4 additions & 18 deletions test/js/samples/select-dynamic-value/expected.js
Expand Up @@ -7,7 +7,8 @@ import {
init,
insert,
noop,
safe_not_equal
safe_not_equal,
select_option
} from "svelte/internal";

function create_fragment(ctx) {
Expand All @@ -33,26 +34,11 @@ function create_fragment(ctx) {
append(select, option0);
append(select, option1);
select_value_value = /*current*/ ctx[0];

for (var i = 0; i < select.options.length; i += 1) {
var option = select.options[i];

if (option.__value === select_value_value) {
option.selected = true;
break;
}
}
select_option(select, select_value_value);
},
p(ctx, [dirty]) {
if (dirty & /*current*/ 1 && select_value_value !== (select_value_value = /*current*/ ctx[0])) {
for (var i = 0; i < select.options.length; i += 1) {
var option = select.options[i];

if (option.__value === select_value_value) {
option.selected = true;
break;
}
}
select_option(select, select_value_value);
}
},
i: noop,
Expand Down
@@ -0,0 +1,39 @@
export default {
async test({ assert, component, target, window }) {
const [input1, input2] = target.querySelectorAll('input');
const select = target.querySelector('select');
const [option1, option2] = select.childNodes;

let selections = Array.from(select.selectedOptions);
assert.equal(selections.length, 2);
assert.ok(selections.includes(option1));
assert.ok(selections.includes(option2));

const event = new window.Event('change');

input1.checked = false;
await input1.dispatchEvent(event);

selections = Array.from(select.selectedOptions);
assert.equal(selections.length, 1);
assert.ok(!selections.includes(option1));
assert.ok(selections.includes(option2));

input2.checked = false;
await input2.dispatchEvent(event);
input1.checked = true;
await input1.dispatchEvent(event);

selections = Array.from(select.selectedOptions);
assert.equal(selections.length, 1);
assert.ok(selections.includes(option1));
assert.ok(!selections.includes(option2));

component.spread = { value: ['Hello', 'World'] };

selections = Array.from(select.selectedOptions);
assert.equal(selections.length, 2);
assert.ok(selections.includes(option1));
assert.ok(selections.includes(option2));
}
};
@@ -0,0 +1,12 @@
<script>
let value = ['Hello', 'World'];
export let spread = {};
</script>

<select multiple {value} {...spread}>
<option>Hello</option>
<option>World</option>
</select>

<input type="checkbox" value="Hello" bind:group={value}>
<input type="checkbox" value="World" bind:group={value}>

0 comments on commit 24ef4e1

Please sign in to comment.