fix bind:group in each (#4868)

pull/5000/head
Tan Li Hau 4 years ago committed by GitHub
parent 90794167fe
commit 38de3b2e9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,9 @@
# Svelte changelog
## Unreleased
* Fix `bind:group` inside `{#each}` ([#3243](https://github.com/sveltejs/svelte/issues/3243))
## 3.23.1
* Fix checkbox `bind:group` when multiple options have the same value ([#4397](https://github.com/sveltejs/svelte/issues/4397))

@ -21,6 +21,7 @@ export default class EachBlock extends AbstractBlock {
contexts: Context[];
has_animation: boolean;
has_binding = false;
has_index_binding = false;
else?: ElseBlock;

@ -32,7 +32,7 @@ export default class Renderer {
blocks: Array<Block | Node | Node[]> = [];
readonly: Set<string> = new Set();
meta_bindings: Array<Node | Node[]> = []; // initial values for e.g. window.innerWidth, if there's a <svelte:window> meta tag
binding_groups: string[] = [];
binding_groups: Map<string, { binding_group: (to_reference?: boolean) => Node; is_context: boolean; contexts: string[]; index: number }> = new Map();
block: Block;
fragment: FragmentWrapper;
@ -63,7 +63,7 @@ export default class Renderer {
this.add_to_context('$$slots');
}
if (this.binding_groups.length > 0) {
if (this.binding_groups.size > 0) {
this.add_to_context('$$binding_groups');
}

@ -416,7 +416,7 @@ export default function dom(
${component.slots.size || component.compile_options.dev ? b`let { $$slots = {}, $$scope } = $$props;` : null}
${component.compile_options.dev && b`@validate_slots('${component.tag}', $$slots, [${[...component.slots.keys()].map(key => `'${key}'`).join(',')}]);`}
${renderer.binding_groups.length > 0 && b`const $$binding_groups = [${renderer.binding_groups.map(_ => x`[]`)}];`}
${renderer.binding_groups.size > 0 && b`const $$binding_groups = [${[...renderer.binding_groups.keys()].map(_ => x`[]`)}];`}
${component.partly_hoisted}

@ -201,7 +201,7 @@ export default class EachBlockWrapper extends Wrapper {
this.context_props = this.node.contexts.map(prop => b`child_ctx[${renderer.context_lookup.get(prop.key.name).index}] = ${prop.modifier(x`list[i]`)};`);
if (this.node.has_binding) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.vars.each_block_value.name).index}] = list;`);
if (this.node.has_binding || this.node.index) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.index_name.name).index}] = i;`);
if (this.node.has_binding || this.node.has_index_binding || this.node.index) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.index_name.name).index}] = i;`);
const snippet = this.node.expression.manipulate(block);

@ -7,8 +7,9 @@ import replace_object from '../../../utils/replace_object';
import Block from '../../Block';
import Renderer from '../../Renderer';
import flatten_reference from '../../../utils/flatten_reference';
import EachBlock from '../../../nodes/EachBlock';
import { Node, Identifier } from 'estree';
import add_to_set from '../../../utils/add_to_set';
import mark_each_block_bindings from '../shared/mark_each_block_bindings';
export default class BindingWrapper {
node: Binding;
@ -42,12 +43,7 @@ export default class BindingWrapper {
}
if (node.is_contextual) {
// we need to ensure that the each block creates a context including
// the list and the index, if they're not otherwise referenced
const { name } = get_object(this.node.expression.node);
const each_block = this.parent.node.scope.get_owner(name);
(each_block as EachBlock).has_binding = true;
mark_each_block_bindings(this.parent, this.node);
}
this.object = get_object(this.node.expression.node).name;
@ -123,17 +119,31 @@ export default class BindingWrapper {
switch (this.node.name) {
case 'group':
{
const binding_group = get_binding_group(parent.renderer, this.node.expression.node);
const { binding_group, is_context, contexts, index } = get_binding_group(parent.renderer, this.node, block);
block.renderer.add_to_context(`$$binding_groups`);
const reference = block.renderer.reference(`$$binding_groups`);
if (is_context) {
if (contexts.length > 1) {
let binding_group = x`${block.renderer.reference('$$binding_groups')}[${index}]`;
for (const name of contexts.slice(0, -1)) {
binding_group = x`${binding_group}[${block.renderer.reference(name)}]`;
block.chunks.init.push(
b`${binding_group} = ${binding_group} || [];`
);
}
}
block.chunks.init.push(
b`${binding_group(true)} = [];`
);
}
block.chunks.hydrate.push(
b`${reference}[${binding_group}].push(${parent.var});`
b`${binding_group(true)}.push(${parent.var});`
);
block.chunks.destroy.push(
b`${reference}[${binding_group}].splice(${reference}[${binding_group}].indexOf(${parent.var}), 1);`
b`${binding_group(true)}.splice(${binding_group(true)}.indexOf(${parent.var}), 1);`
);
break;
}
@ -245,19 +255,61 @@ function get_dom_updater(
return b`${element.var}.${binding.node.name} = ${binding.snippet};`;
}
function get_binding_group(renderer: Renderer, value: Node) {
const { parts } = flatten_reference(value); // TODO handle cases involving computed member expressions
const keypath = parts.join('.');
function get_binding_group(renderer: Renderer, value: Binding, block: Block) {
const { parts } = flatten_reference(value.raw_expression);
let keypath = parts.join('.');
const contexts = [];
for (const dep of value.expression.contextual_dependencies) {
const context = block.bindings.get(dep);
let key;
let name;
if (context) {
key = context.object.name;
name = context.property.name;
} else {
key = dep;
name = dep;
}
keypath = `${key}@${keypath}`;
contexts.push(name);
}
if (!renderer.binding_groups.has(keypath)) {
const index = renderer.binding_groups.size;
contexts.forEach(context => {
renderer.add_to_context(context, true);
});
// TODO handle contextual bindings — `keypath` should include unique ID of
// each block that provides context
let index = renderer.binding_groups.indexOf(keypath);
if (index === -1) {
index = renderer.binding_groups.length;
renderer.binding_groups.push(keypath);
renderer.binding_groups.set(keypath, {
binding_group: (to_reference: boolean = false) => {
let binding_group = '$$binding_groups';
let _secondary_indexes = contexts;
if (to_reference) {
binding_group = block.renderer.reference(binding_group);
_secondary_indexes = _secondary_indexes.map(name => block.renderer.reference(name));
}
if (_secondary_indexes.length > 0) {
let obj = x`${binding_group}[${index}]`;
_secondary_indexes.forEach(secondary_index => {
obj = x`${obj}[${secondary_index}]`;
});
return obj;
} else {
return x`${binding_group}[${index}]`;
}
},
is_context: contexts.length > 0,
contexts,
index,
});
}
return index;
return renderer.binding_groups.get(keypath);
}
function get_event_handler(
@ -295,7 +347,7 @@ function get_event_handler(
}
}
const value = get_value_from_dom(renderer, binding.parent, binding);
const value = get_value_from_dom(renderer, binding.parent, binding, block, contextual_dependencies);
const mutation = b`
${lhs} = ${value};
@ -313,7 +365,9 @@ function get_event_handler(
function get_value_from_dom(
renderer: Renderer,
element: ElementWrapper | InlineComponentWrapper,
binding: BindingWrapper
binding: BindingWrapper,
block: Block,
contextual_dependencies: Set<string>
) {
const { node } = element;
const { name } = binding.node;
@ -333,9 +387,10 @@ function get_value_from_dom(
// <input type='checkbox' bind:group='foo'>
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}], this.__value, this.checked)`;
const { binding_group, contexts } = get_binding_group(renderer, binding.node, block);
add_to_set(contextual_dependencies, contexts);
return x`@get_binding_group_value(${binding_group()}, this.__value, this.checked)`;
}
return x`this.__value`;

@ -8,16 +8,15 @@ import { sanitize } from '../../../../utils/names';
import add_to_set from '../../../utils/add_to_set';
import { b, x, p } from 'code-red';
import Attribute from '../../../nodes/Attribute';
import get_object from '../../../utils/get_object';
import create_debugging_comment from '../shared/create_debugging_comment';
import { get_slot_definition } from '../shared/get_slot_definition';
import EachBlock from '../../../nodes/EachBlock';
import TemplateScope from '../../../nodes/shared/TemplateScope';
import is_dynamic from '../shared/is_dynamic';
import bind_this from '../shared/bind_this';
import { Node, Identifier, ObjectExpression } from 'estree';
import EventHandler from '../Element/EventHandler';
import { extract_names } from 'periscopic';
import mark_each_block_bindings from '../shared/mark_each_block_bindings';
export default class InlineComponentWrapper extends Wrapper {
var: Identifier;
@ -48,12 +47,7 @@ export default class InlineComponentWrapper extends Wrapper {
this.node.bindings.forEach(binding => {
if (binding.is_contextual) {
// we need to ensure that the each block creates a context including
// the list and the index, if they're not otherwise referenced
const { name } = get_object(binding.expression.node);
const each_block = this.node.scope.get_owner(name);
(each_block as EachBlock).has_binding = true;
mark_each_block_bindings(this, binding);
}
block.add_dependencies(binding.expression.dependencies);

@ -0,0 +1,24 @@
import EachBlock from "../../../nodes/EachBlock";
import InlineComponentWrapper from "../InlineComponent";
import ElementWrapper from "../Element";
import Binding from "../../../nodes/Binding";
import get_object from "../../../utils/get_object";
export default function mark_each_block_bindings(
parent: ElementWrapper | InlineComponentWrapper,
binding: Binding
) {
// we need to ensure that the each block creates a context including
// the list and the index, if they're not otherwise referenced
const object = get_object(binding.expression.node).name;
const each_block = parent.node.scope.get_owner(object);
(each_block as EachBlock).has_binding = true;
if (binding.name === "group") {
// for `<input bind:group={} >`, we make sure that all the each blocks creates context with `index`
for (const name of binding.expression.contextual_dependencies) {
const each_block = parent.node.scope.get_owner(name);
(each_block as EachBlock).has_index_binding = true;
}
}
}

@ -3,14 +3,18 @@ import { Node, Identifier } from 'estree';
export default function flatten_reference(node: Node) {
const nodes = [];
const parts = [];
while (node.type === 'MemberExpression') {
nodes.unshift(node.property);
if (!node.computed) {
parts.unshift((node.property as Identifier).name);
} else {
const computed_property = to_string(node.property);
if (computed_property) {
parts.unshift(`[${computed_property}]`);
}
}
node = node.object;
}
@ -20,9 +24,16 @@ export default function flatten_reference(node: Node) {
nodes.unshift(node);
if (!(node as any).computed) {
parts.unshift(name);
}
parts.unshift(name);
return { name, nodes, parts };
}
function to_string(node: Node) {
switch (node.type) {
case 'Literal':
return String(node.value);
case 'Identifier':
return node.name;
}
}

@ -0,0 +1,275 @@
const values = [
{ name: 'Alpha' },
{ name: 'Beta' },
{ name: 'Gamma' }
];
const selected_array = [
[values[1]],
[],
[values[2]],
];
export default {
props: {
values,
selected_array,
},
html: `
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Beta</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p></p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Gamma</p>
</div>
`,
async test({ assert, component, target, window }) {
const inputs = target.querySelectorAll('input');
assert.equal(inputs[0].checked, false);
assert.equal(inputs[1].checked, true);
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);
assert.equal(inputs[8].checked, true);
const event = new window.Event('change');
inputs[0].checked = true;
await inputs[0].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Alpha, Beta</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p></p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Gamma</p>
</div>
`);
inputs[3].checked = true;
await inputs[3].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Alpha, Beta</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Alpha</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Gamma</p>
</div>
`);
inputs[8].checked = false;
await inputs[8].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Alpha, Beta</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Alpha</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p></p>
</div>
`);
component.selected_array = [[values[1], values[2]], [values[2]]];
assert.equal(inputs[0].checked, false);
assert.equal(inputs[1].checked, true);
assert.equal(inputs[2].checked, true);
assert.equal(inputs[3].checked, false);
assert.equal(inputs[4].checked, false);
assert.equal(inputs[5].checked, true);
assert.htmlEqual(target.innerHTML, `
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Beta, Gamma</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Gamma</p>
</div>
`);
}
};

@ -0,0 +1,15 @@
<script>
export let selected_array;
export let values;
</script>
{#each selected_array as selected}
<div>
{#each values as value}
<label>
<input type="checkbox" value="{value}" bind:group={selected} /> {value.name}
</label>
{/each}
<p>{selected.map(v => v.name).join(', ')}</p>
</div>
{/each}

@ -0,0 +1,59 @@
export default {
html: `
<label>
<input type="checkbox" value="1"> 1
</label>
<label>
<input type="checkbox" value="2"> 2
</label>
<label>
<input type="checkbox" value="3"> 3
</label>
<p>1, 2, 3</p>`,
async test({ assert, component, target, window }) {
const inputs = target.querySelectorAll('input');
assert.equal(inputs[0].checked, true);
assert.equal(inputs[1].checked, true);
assert.equal(inputs[2].checked, true);
const event = new window.Event('change');
inputs[0].checked = false;
await inputs[0].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<label>
<input type="checkbox" value="1"> 1
</label>
<label>
<input type="checkbox" value="2"> 2
</label>
<label>
<input type="checkbox" value="3"> 3
</label>
<p>2, 3</p>
`);
component.selected = [[1, 3]];
assert.equal(inputs[0].checked, true);
assert.equal(inputs[1].checked, false);
assert.equal(inputs[2].checked, true);
assert.htmlEqual(target.innerHTML, `
<label>
<input type="checkbox" value="1"> 1
</label>
<label>
<input type="checkbox" value="2"> 2
</label>
<label>
<input type="checkbox" value="3"> 3
</label>
<p>1, 3</p>
`);
}
};

@ -0,0 +1,17 @@
<script>
let options = [1,2,3]
export let selected = [[1,2,3]];
</script>
{#each options as value}
<label>
<input
type='checkbox'
bind:group={selected[0]}
value={value}
/>
{value}
</label>
{/each}
<p>{selected[0].join(', ')}</p>

@ -0,0 +1,275 @@
const values = [
{ name: 'Alpha' },
{ name: 'Beta' },
{ name: 'Gamma' }
];
const selected_array = [
[values[1]],
[],
[values[2]],
];
export default {
props: {
values,
selected_array,
},
html: `
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Beta</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p></p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Gamma</p>
</div>
`,
async test({ assert, component, target, window }) {
const inputs = target.querySelectorAll('input');
assert.equal(inputs[0].checked, false);
assert.equal(inputs[1].checked, true);
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);
assert.equal(inputs[8].checked, true);
const event = new window.Event('change');
inputs[0].checked = true;
await inputs[0].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Alpha, Beta</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p></p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Gamma</p>
</div>
`);
inputs[3].checked = true;
await inputs[3].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Alpha, Beta</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Alpha</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Gamma</p>
</div>
`);
inputs[8].checked = false;
await inputs[8].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Alpha, Beta</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Alpha</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p></p>
</div>
`);
component.selected_array = [[values[1], values[2]], [values[2]]];
assert.equal(inputs[0].checked, false);
assert.equal(inputs[1].checked, true);
assert.equal(inputs[2].checked, true);
assert.equal(inputs[3].checked, false);
assert.equal(inputs[4].checked, false);
assert.equal(inputs[5].checked, true);
assert.htmlEqual(target.innerHTML, `
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Beta, Gamma</p>
</div>
<div>
<label>
<input type="checkbox" value="[object Object]"> Alpha
</label>
<label>
<input type="checkbox" value="[object Object]"> Beta
</label>
<label>
<input type="checkbox" value="[object Object]"> Gamma
</label>
<p>Gamma</p>
</div>
`);
}
};

@ -0,0 +1,15 @@
<script>
export let selected_array;
export let values;
</script>
{#each selected_array as _, index}
<div>
{#each values as value}
<label>
<input type="checkbox" value="{value}" bind:group={selected_array[index]} /> {value.name}
</label>
{/each}
<p>{selected_array[index].map(v => v.name).join(', ')}</p>
</div>
{/each}

@ -0,0 +1,153 @@
export default {
html: `
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>2</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p></p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>3</p>
`,
async test({ assert, component, target, window }) {
const inputs = target.querySelectorAll('input');
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);
assert.equal(inputs[8].checked, false);
assert.equal(inputs[9].checked, false);
assert.equal(inputs[10].checked, false);
assert.equal(inputs[11].checked, true);
const event = new window.Event('change');
inputs[2].checked = true;
await inputs[2].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>2</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p></p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>3</p>
`);
inputs[9].checked = true;
await inputs[9].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>2</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p></p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 3</p>
`);
inputs[4].checked = false;
await inputs[4].dispatchEvent(event);
inputs[5].checked = true;
await inputs[5].dispatchEvent(event);
inputs[6].checked = true;
await inputs[6].dispatchEvent(event);
inputs[7].checked = true;
await inputs[7].dispatchEvent(event);
inputs[11].checked = false;
await inputs[11].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 2</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
`);
component.selected_array_1 = [[3], [1]];
component.selected_array_2 = [[], [2]];
assert.equal(inputs[0].checked, false);
assert.equal(inputs[1].checked, false);
assert.equal(inputs[2].checked, true);
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, false);
assert.equal(inputs[8].checked, false);
assert.equal(inputs[9].checked, false);
assert.equal(inputs[10].checked, true);
assert.equal(inputs[11].checked, false);
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p></p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>2</p>
`);
}
};

@ -0,0 +1,33 @@
<script>
const options = [1, 2, 3];
export let selected_array_1 = [[1], [2]];
export let selected_array_2 = [[], [3]];
</script>
{#each selected_array_1 as selected}
{#each options as value}
<label>
<input
type='checkbox'
bind:group={selected}
value={value}
/>
{value}
</label>
{/each}
<p>{selected.join(', ')}</p>
{/each}
{#each selected_array_2 as selected}
{#each options as value}
<label>
<input
type='checkbox'
bind:group={selected}
value={value}
/>
{value}
</label>
{/each}
<p>{selected.join(', ')}</p>
{/each}

@ -0,0 +1,160 @@
export default {
html: `
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 2, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>2</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
`,
async test({ assert, component, target, window }) {
const inputs = target.querySelectorAll('input');
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, true);
assert.equal(inputs[6].checked, false);
assert.equal(inputs[7].checked, true);
assert.equal(inputs[8].checked, false);
assert.equal(inputs[9].checked, true);
assert.equal(inputs[10].checked, false);
assert.equal(inputs[11].checked, false);
const event = new window.Event('change');
inputs[2].checked = true;
await inputs[2].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 2, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>2</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
`);
inputs[8].checked = true;
await inputs[8].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 2, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>2, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
`);
component.selected_index = [1, 1];
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 2, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 2, 3</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
`);
assert.equal(inputs[0].checked, true);
assert.equal(inputs[1].checked, true);
assert.equal(inputs[2].checked, true);
assert.equal(inputs[3].checked, true);
assert.equal(inputs[4].checked, true);
assert.equal(inputs[5].checked, true);
assert.equal(inputs[6].checked, true);
assert.equal(inputs[7].checked, false);
assert.equal(inputs[8].checked, false);
assert.equal(inputs[9].checked, true);
assert.equal(inputs[10].checked, false);
assert.equal(inputs[11].checked, false);
inputs[5].checked = false;
await inputs[5].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 2</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1, 2</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
<label><input type="checkbox" value="1"> 1</label>
<label><input type="checkbox" value="2"> 2</label>
<label><input type="checkbox" value="3"> 3</label>
<p>1</p>
`);
assert.equal(inputs[0].checked, true);
assert.equal(inputs[1].checked, true);
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, true);
assert.equal(inputs[7].checked, false);
assert.equal(inputs[8].checked, false);
assert.equal(inputs[9].checked, true);
assert.equal(inputs[10].checked, false);
assert.equal(inputs[11].checked, false);
}
};

@ -0,0 +1,24 @@
<script>
const options = [1, 2, 3];
export let selected_array = [
[[1], [1, 2, 3]],
[[2], [1]],
];
export let selected_index = [0, 1];
</script>
{#each selected_array as selected}
{#each selected_index as index}
{#each options as value}
<label>
<input
type='checkbox'
bind:group={selected[index]}
value={value}
/>
{value}
</label>
{/each}
<p>{selected[index].join(', ')}</p>
{/each}
{/each}
Loading…
Cancel
Save