diff --git a/CHANGELOG.md b/CHANGELOG.md index f4da829b69..512303ef50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* Fix checkbox `bind:group` when multiple options have the same value ([#4397](https://github.com/sveltejs/svelte/issues/4397)) * Fix `bind:this` to the value of an `{#each}` block ([#4517](https://github.com/sveltejs/svelte/issues/4517)) * Fix reactivity when assigning to contextual `{#each}` variable ([#4574](https://github.com/sveltejs/svelte/issues/4574), [#4744](https://github.com/sveltejs/svelte/issues/4744)) * Fix binding to contextual `{#each}` values that shadow outer names ([#4757](https://github.com/sveltejs/svelte/issues/4757)) diff --git a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts index 298d351515..c45ede16e8 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts @@ -335,7 +335,7 @@ function get_value_from_dom( if (name === 'group') { const binding_group = get_binding_group(renderer, binding.node.expression.node); if (type === 'checkbox') { - return x`@get_binding_group_value($$binding_groups[${binding_group}])`; + return x`@get_binding_group_value($$binding_groups[${binding_group}], this.__value, this.checked)`; } return x`this.__value`; diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index 08e82c0e56..0cd670fcb9 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -126,12 +126,15 @@ export function xlink_attr(node, attribute, value) { node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value); } -export function get_binding_group_value(group) { - const value = []; +export function get_binding_group_value(group, __value, checked) { + const value = new Set(); for (let i = 0; i < group.length; i += 1) { - if (group[i].checked) value.push(group[i].__value); + if (group[i].checked) value.add(group[i].__value); } - return value; + if (!checked) { + value.delete(__value); + } + return Array.from(value); } export function to_number(value) { diff --git a/test/runtime/samples/binding-input-group-duplicate-value/_config.js b/test/runtime/samples/binding-input-group-duplicate-value/_config.js new file mode 100644 index 0000000000..7aea9142fa --- /dev/null +++ b/test/runtime/samples/binding-input-group-duplicate-value/_config.js @@ -0,0 +1,81 @@ +export default { + html: ` +

Checked:

+ +
+ + a
+ b
+ c
+ d
+ +
+ + a
+ b
+ c
+ d
+ `, + + async test({ assert, component, target, window }) { + const inputs = target.querySelectorAll("input"); + const p = target.querySelector("p"); + + assert.equal(inputs[0].checked, false); + assert.equal(inputs[1].checked, false); + assert.equal(inputs[2].checked, false); + assert.equal(inputs[3].checked, false); + + assert.equal(inputs[4].checked, false); + assert.equal(inputs[5].checked, false); + assert.equal(inputs[6].checked, false); + assert.equal(inputs[7].checked, false); + + const event = new window.Event("change"); + + inputs[0].checked = true; + await inputs[0].dispatchEvent(event); + + assert.htmlEqual(p.innerHTML, `Checked: a`); + + assert.equal(inputs[0].checked, true); + assert.equal(inputs[1].checked, false); + assert.equal(inputs[2].checked, false); + assert.equal(inputs[3].checked, false); + + assert.equal(inputs[4].checked, true); + assert.equal(inputs[5].checked, false); + assert.equal(inputs[6].checked, false); + assert.equal(inputs[7].checked, false); + + inputs[3].checked = true; + await inputs[3].dispatchEvent(event); + + assert.htmlEqual(p.innerHTML, `Checked: a,d`); + + assert.equal(inputs[0].checked, true); + assert.equal(inputs[1].checked, false); + assert.equal(inputs[2].checked, false); + assert.equal(inputs[3].checked, true); + + assert.equal(inputs[4].checked, true); + assert.equal(inputs[5].checked, false); + assert.equal(inputs[6].checked, false); + assert.equal(inputs[7].checked, true); + + inputs[4].checked = false; + await inputs[4].dispatchEvent(event); + + assert.htmlEqual(p.innerHTML, `Checked: d`); + + assert.equal(inputs[0].checked, false); + assert.equal(inputs[1].checked, false); + assert.equal(inputs[2].checked, false); + assert.equal(inputs[3].checked, true); + + assert.equal(inputs[4].checked, false); + assert.equal(inputs[5].checked, false); + assert.equal(inputs[6].checked, false); + assert.equal(inputs[7].checked, true); + }, +}; diff --git a/test/runtime/samples/binding-input-group-duplicate-value/main.svelte b/test/runtime/samples/binding-input-group-duplicate-value/main.svelte new file mode 100644 index 0000000000..153e559d23 --- /dev/null +++ b/test/runtime/samples/binding-input-group-duplicate-value/main.svelte @@ -0,0 +1,19 @@ + + +

Checked: {foo}

+ +
+ +a
+b
+c
+d
+ +
+ +a
+b
+c
+d
\ No newline at end of file