Skip to content

Commit c99dd2e

Browse files
tanhauhaudummdidumm
andauthoredMar 14, 2023
fix: binding group with if block (#8373)
Fixes #8372 --------- Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
1 parent c7dcfac commit c99dd2e

File tree

9 files changed

+128
-16
lines changed

9 files changed

+128
-16
lines changed
 

‎src/compiler/compile/render_dom/Block.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ export default class Block {
505505

506506
render_binding_groups() {
507507
for (const binding_group of this.binding_groups) {
508-
binding_group.render();
508+
binding_group.render(this);
509509
}
510510
}
511511
}

‎src/compiler/compile/render_dom/Renderer.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ export interface BindingGroup {
2727
contexts: string[];
2828
list_dependencies: Set<string>;
2929
keypath: string;
30-
elements: Identifier[];
31-
render: () => void;
30+
add_element: (block: Block, element: Identifier) => void;
31+
render: (block: Block) => void;
3232
}
3333

3434
export default class Renderer {

‎src/compiler/compile/render_dom/wrappers/Element/Binding.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export default class BindingWrapper {
153153
case 'group':
154154
{
155155
block.renderer.add_to_context('$$binding_groups');
156-
this.binding_group.elements.push(this.parent.var);
156+
this.binding_group.add_element(block, this.parent.var);
157157

158158
if ((this.parent as ElementWrapper).has_dynamic_value) {
159159
update_or_condition = (this.parent as ElementWrapper).dynamic_value_condition;
@@ -323,7 +323,11 @@ function get_binding_group(renderer: Renderer, binding: BindingWrapper, block: B
323323
parent = parent.parent;
324324
}
325325

326-
const elements = [];
326+
/**
327+
* When using bind:group with logic blocks, the inputs with bind:group may be scattered across different blocks.
328+
* This therefore keeps track of all the <input> elements that have the same bind:group within the same block.
329+
*/
330+
const elements = new Map<Block, any>();
327331

328332
contexts.forEach(context => {
329333
renderer.add_to_context(context, true);
@@ -343,8 +347,13 @@ function get_binding_group(renderer: Renderer, binding: BindingWrapper, block: B
343347
contexts,
344348
list_dependencies,
345349
keypath,
346-
elements,
347-
render() {
350+
add_element(block, element) {
351+
if (!elements.has(block)) {
352+
elements.set(block, []);
353+
}
354+
elements.get(block).push(element);
355+
},
356+
render(block) {
348357
const local_name = block.get_unique_name('binding_group');
349358
const binding_group = block.renderer.reference('$$binding_groups');
350359
block.add_variable(local_name);
@@ -362,7 +371,7 @@ function get_binding_group(renderer: Renderer, binding: BindingWrapper, block: B
362371
);
363372
}
364373
block.chunks.hydrate.push(
365-
b`${local_name}.p(${elements})`
374+
b`${local_name}.p(${elements.get(block)})`
366375
);
367376
block.chunks.destroy.push(
368377
b`${local_name}.r()`

‎src/runtime/internal/dom.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ export function get_binding_group_value(group, __value, checked) {
359359
return Array.from(value);
360360
}
361361

362-
export function init_binding_group(group) {
362+
export function init_binding_group(group: HTMLInputElement[]) {
363363
let _inputs: HTMLInputElement[];
364364
return {
365365
/* push */ p(...inputs: HTMLInputElement[]) {

‎test/runtime-puppeteer/index.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,7 @@ async function launchPuppeteer() {
5656

5757
const assert = fs.readFileSync(`${__dirname}/assert.js`, 'utf-8');
5858

59-
describe('runtime (puppeteer)', function() {
60-
// Note: Increase the timeout in preparation for restarting Chromium due to SIGSEGV.
61-
this.timeout(10000);
59+
describe('runtime (puppeteer)', () => {
6260
before(async () => {
6361
svelte = loadSvelte(false);
6462
console.log('[runtime-puppeteer] Loaded Svelte');
@@ -75,7 +73,7 @@ describe('runtime (puppeteer)', function() {
7573

7674
const failed = new Set();
7775

78-
function runTest(dir, hydrate) {
76+
function runTest(dir, hydrate, is_first_run) {
7977
if (dir[0] === '.') return;
8078
// MEMO: puppeteer can not execute Chromium properly with Node8,10 on Linux at GitHub actions.
8179
const { version } = process;
@@ -254,11 +252,14 @@ describe('runtime (puppeteer)', function() {
254252
prettyPrintPuppeteerAssertionError(err.message);
255253
assertWarnings();
256254
});
257-
});
255+
}).timeout(is_first_run ? 20000 : 10000);
258256
}
259257

258+
// Increase the timeout on the first run in preparation for restarting Chromium due to SIGSEGV.
259+
let first_run = true;
260260
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
261-
runTest(dir, false);
262-
runTest(dir, true);
261+
runTest(dir, false, first_run);
262+
runTest(dir, true, first_run);
263+
first_run = false;
263264
});
264265
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
export default {
2+
async test({ assert, target, component, window }) {
3+
const button = target.querySelector('button');
4+
const clickEvent = new window.Event('click');
5+
const changeEvent = new window.Event('change');
6+
7+
const [input1, input2] = target.querySelectorAll('input[type="checkbox"]');
8+
function validate_inputs(v1, v2) {
9+
assert.equal(input1.checked, v1);
10+
assert.equal(input2.checked, v2);
11+
}
12+
13+
assert.deepEqual(component.test, []);
14+
validate_inputs(false, false);
15+
16+
component.test = ['a', 'b'];
17+
validate_inputs(true, true);
18+
19+
input1.checked = false;
20+
await input1.dispatchEvent(changeEvent);
21+
assert.deepEqual(component.test, ['b']);
22+
23+
input2.checked = false;
24+
await input2.dispatchEvent(changeEvent);
25+
assert.deepEqual(component.test, []);
26+
27+
input1.checked = true;
28+
input2.checked = true;
29+
await input1.dispatchEvent(changeEvent);
30+
await input2.dispatchEvent(changeEvent);
31+
assert.deepEqual(component.test, ['b', 'a']);
32+
33+
await button.dispatchEvent(clickEvent);
34+
assert.deepEqual(component.test, ['b', 'a']); // should it be ['a'] only? valid arguments for both outcomes
35+
36+
input1.checked = false;
37+
await input1.dispatchEvent(changeEvent);
38+
assert.deepEqual(component.test, []);
39+
}
40+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script>
2+
export let test = [];
3+
let hidden = false
4+
</script>
5+
6+
<button on:click={() => hidden = !hidden}>
7+
{hidden ? "show" : "hide"} b
8+
</button>
9+
10+
<label>a <input type="checkbox" bind:group={test} value="a" /></label>
11+
{#if !hidden}
12+
<label>b <input type="checkbox" bind:group={test} value="b" /></label>
13+
{/if}
14+
<label>c <input value="just here, so b is not the last input" /></label>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
export default {
2+
async test({ assert, target, component, window }) {
3+
const button = target.querySelector('button');
4+
const clickEvent = new window.Event('click');
5+
const changeEvent = new window.Event('change');
6+
7+
const [input1, input2] = target.querySelectorAll('input[type="radio"]');
8+
function validate_inputs(v1, v2) {
9+
assert.equal(input1.checked, v1);
10+
assert.equal(input2.checked, v2);
11+
}
12+
13+
component.test = 'a';
14+
validate_inputs(true, false);
15+
16+
component.test = 'b';
17+
validate_inputs(false, true);
18+
19+
input1.checked = true;
20+
await input1.dispatchEvent(changeEvent);
21+
assert.deepEqual(component.test, 'a');
22+
23+
input2.checked = true;
24+
await input2.dispatchEvent(changeEvent);
25+
assert.deepEqual(component.test, 'b');
26+
27+
await button.dispatchEvent(clickEvent);
28+
assert.deepEqual(component.test, 'b'); // should it be undefined? valid arguments for both outcomes
29+
30+
input1.checked = true;
31+
await input1.dispatchEvent(changeEvent);
32+
assert.deepEqual(component.test, 'a');
33+
}
34+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script>
2+
export let test;
3+
let hidden = false
4+
</script>
5+
6+
<button on:click={() => hidden = !hidden}>
7+
{hidden ? "show" : "hide"} b
8+
</button>
9+
10+
<label>a <input type="radio" bind:group={test} value="a" /></label>
11+
{#if !hidden}
12+
<label>b <input type="radio" bind:group={test} value="b" /></label>
13+
{/if}
14+
<label>c <input value="just here, so b is not the last input" /></label>

0 commit comments

Comments
 (0)