From 63669330f69fecaeaacba5d35faf180c3f5b9a41 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sat, 2 Jan 2021 09:28:11 +0800 Subject: [PATCH] maintain context for keyed each (#5840) --- CHANGELOG.md | 1 + .../compile/render_dom/wrappers/EachBlock.ts | 2 + .../render_dom/wrappers/Element/index.ts | 16 ++++- .../each-block-keyed-animated/expected.js | 3 +- test/js/samples/each-block-keyed/expected.js | 3 +- .../samples/animation-js-delay/_config.js | 28 +++++++- .../each-block-keyed-bind-group/_config.js | 65 +++++++++++++++++++ .../each-block-keyed-bind-group/main.svelte | 21 ++++++ 8 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 test/runtime/samples/each-block-keyed-bind-group/_config.js create mode 100644 test/runtime/samples/each-block-keyed-bind-group/main.svelte 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}