diff --git a/CHANGELOG.md b/CHANGELOG.md
index 58472baf57..26c50c86be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
* Fix location of automatically declared reactive variables ([#5749](https://github.com/sveltejs/svelte/issues/5749))
* Warn when using `className` or `htmlFor` attributes ([#5777](https://github.com/sveltejs/svelte/issues/5777))
+* Fix checkbox `bind:group` in keyed `{#each}` where the array can be reordered ([#5779](https://github.com/sveltejs/svelte/issues/5779))
* Fix checkbox `bind:group` in nested `{#each}` contexts ([#5811](https://github.com/sveltejs/svelte/issues/5811))
* Add graphics roles as known ARIA roles ([#5822](https://github.com/sveltejs/svelte/pull/5822))
diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.ts b/src/compiler/compile/render_dom/wrappers/EachBlock.ts
index ad5c5518d1..2c0f5f68b4 100644
--- a/src/compiler/compile/render_dom/wrappers/EachBlock.ts
+++ b/src/compiler/compile/render_dom/wrappers/EachBlock.ts
@@ -447,6 +447,8 @@ export default class EachBlockWrapper extends Wrapper {
: '@destroy_block';
if (this.dependencies.size) {
+ this.block.maintain_context = true;
+
this.updates.push(b`
const ${this.vars.each_block_value} = ${snippet};
${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`}
diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts
index d90cf0da98..269edcfca8 100644
--- a/src/compiler/compile/render_dom/wrappers/Element/index.ts
+++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts
@@ -851,7 +851,21 @@ export default class ElementWrapper extends Wrapper {
${outro && b`@add_transform(${this.var}, ${rect});`}
`);
- const params = this.node.animation.expression ? this.node.animation.expression.manipulate(block) : x`{}`;
+ let params;
+ if (this.node.animation.expression) {
+ params = this.node.animation.expression.manipulate(block);
+
+ if (this.node.animation.expression.dynamic_dependencies().length) {
+ // if `params` is dynamic, calculate params ahead of time in the `.r()` method
+ const params_var = block.get_unique_name('params');
+ block.add_variable(params_var);
+
+ block.chunks.measure.push(b`${params_var} = ${params};`);
+ params = params_var;
+ }
+ } else {
+ params = x`{}`;
+ }
const name = this.renderer.reference(this.node.animation.name);
diff --git a/test/js/samples/each-block-keyed-animated/expected.js b/test/js/samples/each-block-keyed-animated/expected.js
index 46ef63ee7f..96e75c61d3 100644
--- a/test/js/samples/each-block-keyed-animated/expected.js
+++ b/test/js/samples/each-block-keyed-animated/expected.js
@@ -43,7 +43,8 @@ function create_each_block(key_1, ctx) {
insert(target, div, anchor);
append(div, t);
},
- p(ctx, dirty) {
+ p(new_ctx, dirty) {
+ ctx = new_ctx;
if (dirty & /*things*/ 1 && t_value !== (t_value = /*thing*/ ctx[1].name + "")) set_data(t, t_value);
},
r() {
diff --git a/test/js/samples/each-block-keyed/expected.js b/test/js/samples/each-block-keyed/expected.js
index 71853cf295..a01bec628b 100644
--- a/test/js/samples/each-block-keyed/expected.js
+++ b/test/js/samples/each-block-keyed/expected.js
@@ -39,7 +39,8 @@ function create_each_block(key_1, ctx) {
insert(target, div, anchor);
append(div, t);
},
- p(ctx, dirty) {
+ p(new_ctx, dirty) {
+ ctx = new_ctx;
if (dirty & /*things*/ 1 && t_value !== (t_value = /*thing*/ ctx[1].name + "")) set_data(t, t_value);
},
d(detaching) {
diff --git a/test/runtime/samples/animation-js-delay/_config.js b/test/runtime/samples/animation-js-delay/_config.js
index 931d37817b..2cee51121f 100644
--- a/test/runtime/samples/animation-js-delay/_config.js
+++ b/test/runtime/samples/animation-js-delay/_config.js
@@ -18,7 +18,7 @@ export default {
`,
test({ assert, component, target, window, raf }) {
- let divs = document.querySelectorAll('div');
+ let divs = window.document.querySelectorAll('div');
divs.forEach(div => {
div.getBoundingClientRect = function() {
const index = [...this.parentNode.children].indexOf(this);
@@ -41,7 +41,7 @@ export default {
{ id: 1, name: 'a' }
];
- divs = document.querySelectorAll('div');
+ divs = window.document.querySelectorAll('div');
assert.equal(divs[0].dy, 120);
assert.equal(divs[4].dy, -120);
@@ -56,5 +56,29 @@ export default {
raf.tick(150);
assert.equal(divs[0].dy, 0);
assert.equal(divs[4].dy, 0);
+
+ component.things = [
+ { id: 1, name: 'a' },
+ { id: 2, name: 'b' },
+ { id: 3, name: 'c' },
+ { id: 4, name: 'd' },
+ { id: 5, name: 'e' }
+ ];
+
+ divs = document.querySelectorAll('div');
+ assert.equal(divs[0].dy, 120);
+ assert.equal(divs[4].dy, -120);
+
+ raf.tick(200);
+ assert.equal(divs[0].dy, 108);
+ assert.equal(divs[4].dy, -60);
+
+ raf.tick(250);
+ assert.equal(divs[0].dy, 48);
+ assert.equal(divs[4].dy, 0);
+
+ raf.tick(300);
+ assert.equal(divs[0].dy, 0);
+ assert.equal(divs[4].dy, 0);
}
};
diff --git a/test/runtime/samples/each-block-keyed-bind-group/_config.js b/test/runtime/samples/each-block-keyed-bind-group/_config.js
new file mode 100644
index 0000000000..0172c2efe7
--- /dev/null
+++ b/test/runtime/samples/each-block-keyed-bind-group/_config.js
@@ -0,0 +1,65 @@
+export default {
+ html: `
+
+
+
+
+
+ `,
+
+ async test({ assert, target, window }) {
+ const [input1, input2, input3, input4, input5] = target.querySelectorAll('input');
+ const event = new window.Event('change');
+
+ input3.checked = true;
+ await input3.dispatchEvent(event);
+
+ assert.htmlEqual(target.innerHTML, `
+
+
+
+
+
+ `);
+
+ assert.equal(input1.checked, false);
+ assert.equal(input2.checked, false);
+ assert.equal(input3.checked, true);
+ assert.equal(input4.checked, false);
+ assert.equal(input5.checked, false);
+
+ input4.checked = true;
+ await input4.dispatchEvent(event);
+
+ assert.htmlEqual(target.innerHTML, `
+
+
+
+
+
+ `);
+
+ assert.equal(input1.checked, false);
+ assert.equal(input2.checked, false);
+ assert.equal(input3.checked, true);
+ assert.equal(input4.checked, true);
+ assert.equal(input5.checked, false);
+
+ input3.checked = false;
+ await input3.dispatchEvent(event);
+
+ assert.htmlEqual(target.innerHTML, `
+
+
+
+
+
+ `);
+
+ assert.equal(input1.checked, false);
+ assert.equal(input2.checked, false);
+ assert.equal(input3.checked, false);
+ assert.equal(input4.checked, true);
+ assert.equal(input5.checked, false);
+ }
+};
diff --git a/test/runtime/samples/each-block-keyed-bind-group/main.svelte b/test/runtime/samples/each-block-keyed-bind-group/main.svelte
new file mode 100644
index 0000000000..80c0db4f83
--- /dev/null
+++ b/test/runtime/samples/each-block-keyed-bind-group/main.svelte
@@ -0,0 +1,21 @@
+
+
+{#each flavours as flavour (flavour)}
+
+{/each}