From b32bcccee49086b39c97af9f7d6516ead08e1ce0 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 8 Jun 2020 21:14:12 -0400 Subject: [PATCH 1/5] site: remove BLM filter, but leave banner (#4994) --- site/src/routes/_layout.svelte | 7 ------- 1 file changed, 7 deletions(-) diff --git a/site/src/routes/_layout.svelte b/site/src/routes/_layout.svelte index 9c1601094a..3f5862e3e0 100644 --- a/site/src/routes/_layout.svelte +++ b/site/src/routes/_layout.svelte @@ -101,9 +101,6 @@ .BLM a { white-space: nowrap; } - :global(header){ - filter: grayscale(100%) /* BLM */ - } main { position: relative; margin: 0 auto; @@ -127,8 +124,4 @@ padding-bottom: 75px; /* BLM */ } } - - main > :global(*) { - filter: grayscale(100%) /* BLM */ - } From 4135ca5415dcf0003c2f14df0c1c94ee99eb7e51 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 8 Jun 2020 21:44:22 -0400 Subject: [PATCH 2/5] -> v3.23.1 --- CHANGELOG.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 512303ef50..5672da69b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Svelte changelog -## Unreleased +## 3.23.1 * 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)) diff --git a/package-lock.json b/package-lock.json index fab51f61ff..780f63f0b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.23.0", + "version": "3.23.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 4bbc90f505..705cee7441 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.23.0", + "version": "3.23.1", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 34e87ce57990d62666f04f15da2947784b6689ba Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 9 Jun 2020 16:21:03 -0700 Subject: [PATCH 3/5] site: faq: svelte-preprocess is now an official project (#4997) --- site/content/faq/500-what-about-typescript-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/faq/500-what-about-typescript-support.md b/site/content/faq/500-what-about-typescript-support.md index 7c41c353b1..7cd0ccbe54 100644 --- a/site/content/faq/500-what-about-typescript-support.md +++ b/site/content/faq/500-what-about-typescript-support.md @@ -2,7 +2,7 @@ question: What about Typescript support? --- -You need to install a [community supported preprocessor](https://github.com/sveltejs/integrations#preprocessors) such as [svelte-preprocess](https://github.com/kaisermann/svelte-preprocess). Work is ongoing to improve [IDE support](https://github.com/sveltejs/language-tools/issues/83). You can also run type checking from the command line with [svelte-check](https://www.npmjs.com/package/svelte-check). +You need to install a preprocessor such as [svelte-preprocess](https://github.com/sveltejs/svelte-preprocess). Work is ongoing to improve [IDE support](https://github.com/sveltejs/language-tools/issues/83). You can also run type checking from the command line with [svelte-check](https://www.npmjs.com/package/svelte-check). To declare the type of a reactive variable in a Svelte template, you can use the following syntax: ``` From 90794167fe455b46c99483331cc2f937a49c33d8 Mon Sep 17 00:00:00 2001 From: Antony Jones Date: Wed, 10 Jun 2020 13:47:31 +0100 Subject: [PATCH 4/5] Remove duplicate site build instructions (#4824) --- README.md | 8 +------- site/README.md | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e2e4fcd532..33faec0588 100644 --- a/README.md +++ b/README.md @@ -64,13 +64,7 @@ npm run test -- -g transition ## svelte.dev -The source code for https://svelte.dev, including all the documentation, lives in the [site](site) directory. The site is built with [Sapper](https://sapper.svelte.dev). To develop locally: - -```bash -cd site -npm install && npm run update -npm run dev -``` +The source code for https://svelte.dev, including all the documentation, lives in the [site](site) directory. The site is built with [Sapper](https://sapper.svelte.dev). ### Is svelte.dev down? diff --git a/site/README.md b/site/README.md index 259e956003..64ae22f9a8 100644 --- a/site/README.md +++ b/site/README.md @@ -1,24 +1,33 @@ ## Running locally -Set up the project: +Set up the site sub-project: ```bash git clone https://github.com/sveltejs/svelte.git -cd svelte -npm ci -PUBLISH=1 npm run build cd site npm ci npm run update +npm run dev ``` -Start the server with `npm run dev`, and navigate to [localhost:3000](http://localhost:3000). +and navigate to [localhost:3000](http://localhost:3000). -## Using a local copy of Svelte +## Running using the local copy of Svelte By default, the REPL will fetch the most recent version of Svelte from https://unpkg.com/svelte. When running the site locally, you can also use your local copy of Svelte. -To produce the proper browser-compatible UMD build of the compiler, you will need to run `npm run build` (or `npm run dev`) in the root of this repository with the `PUBLISH` environment variable set to any non-empty string. +To produce the proper browser-compatible UMD build of the compiler, you will need to run `npm run build` (or `npm run dev`) in the root of this repository with the `PUBLISH` environment variable set to any non-empty string: + +```bash +git clone https://github.com/sveltejs/svelte.git +cd svelte +npm ci +PUBLISH=1 npm run build +cd site +npm ci +npm run update +npm run dev +``` Then visit the REPL at [localhost:3000/repl?version=local](http://localhost:3000/repl?version=local). Please note that the local REPL only works with `npm run dev` and not when building the site for production usage. @@ -35,6 +44,7 @@ In order for the REPL's GitHub integration to work properly when running locally GITHUB_CLIENT_SECRET=[your app's Client Secret] BASEURL=http://localhost:3000 ``` + ## Building the site To build the website, run `npm run sapper`. The output can be found in `__sapper__/build`. From 38de3b2e9f46925be12ce43f55c424b49ddfb0af Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 11 Jun 2020 01:09:19 +0800 Subject: [PATCH 5/5] fix bind:group in each (#4868) --- CHANGELOG.md | 4 + src/compiler/compile/nodes/EachBlock.ts | 1 + src/compiler/compile/render_dom/Renderer.ts | 4 +- src/compiler/compile/render_dom/index.ts | 2 +- .../compile/render_dom/wrappers/EachBlock.ts | 2 +- .../render_dom/wrappers/Element/Binding.ts | 105 +++++-- .../wrappers/InlineComponent/index.ts | 10 +- .../shared/mark_each_block_bindings.ts | 24 ++ .../compile/utils/flatten_reference.ts | 21 +- .../binding-input-group-each-1/_config.js | 275 ++++++++++++++++++ .../binding-input-group-each-1/main.svelte | 15 + .../binding-input-group-each-2/_config.js | 59 ++++ .../binding-input-group-each-2/main.svelte | 17 ++ .../binding-input-group-each-3/_config.js | 275 ++++++++++++++++++ .../binding-input-group-each-3/main.svelte | 15 + .../binding-input-group-each-4/_config.js | 153 ++++++++++ .../binding-input-group-each-4/main.svelte | 33 +++ .../binding-input-group-each-5/_config.js | 160 ++++++++++ .../binding-input-group-each-5/main.svelte | 24 ++ 19 files changed, 1157 insertions(+), 42 deletions(-) create mode 100644 src/compiler/compile/render_dom/wrappers/shared/mark_each_block_bindings.ts create mode 100644 test/runtime/samples/binding-input-group-each-1/_config.js create mode 100644 test/runtime/samples/binding-input-group-each-1/main.svelte create mode 100644 test/runtime/samples/binding-input-group-each-2/_config.js create mode 100644 test/runtime/samples/binding-input-group-each-2/main.svelte create mode 100644 test/runtime/samples/binding-input-group-each-3/_config.js create mode 100644 test/runtime/samples/binding-input-group-each-3/main.svelte create mode 100644 test/runtime/samples/binding-input-group-each-4/_config.js create mode 100644 test/runtime/samples/binding-input-group-each-4/main.svelte create mode 100644 test/runtime/samples/binding-input-group-each-5/_config.js create mode 100644 test/runtime/samples/binding-input-group-each-5/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 5672da69b3..e6532eff73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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)) diff --git a/src/compiler/compile/nodes/EachBlock.ts b/src/compiler/compile/nodes/EachBlock.ts index 6458ea0020..30306745bd 100644 --- a/src/compiler/compile/nodes/EachBlock.ts +++ b/src/compiler/compile/nodes/EachBlock.ts @@ -21,6 +21,7 @@ export default class EachBlock extends AbstractBlock { contexts: Context[]; has_animation: boolean; has_binding = false; + has_index_binding = false; else?: ElseBlock; diff --git a/src/compiler/compile/render_dom/Renderer.ts b/src/compiler/compile/render_dom/Renderer.ts index 53f6c8554f..c02d646ebf 100644 --- a/src/compiler/compile/render_dom/Renderer.ts +++ b/src/compiler/compile/render_dom/Renderer.ts @@ -32,7 +32,7 @@ export default class Renderer { blocks: Array = []; readonly: Set = new Set(); meta_bindings: Array = []; // initial values for e.g. window.innerWidth, if there's a meta tag - binding_groups: string[] = []; + binding_groups: Map 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'); } diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index 13b9b56bad..3b5001d483 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -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} diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.ts b/src/compiler/compile/render_dom/wrappers/EachBlock.ts index 1efadfb90c..bd981a0603 100644 --- a/src/compiler/compile/render_dom/wrappers/EachBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/EachBlock.ts @@ -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); diff --git a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts index c45ede16e8..33214ffad3 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts @@ -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 ) { const { node } = element; const { name } = binding.node; @@ -333,9 +387,10 @@ 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}], 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`; diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index 8c342b0516..1847f1b758 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -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); diff --git a/src/compiler/compile/render_dom/wrappers/shared/mark_each_block_bindings.ts b/src/compiler/compile/render_dom/wrappers/shared/mark_each_block_bindings.ts new file mode 100644 index 0000000000..490d52bd9d --- /dev/null +++ b/src/compiler/compile/render_dom/wrappers/shared/mark_each_block_bindings.ts @@ -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 ``, 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; + } + } +} diff --git a/src/compiler/compile/utils/flatten_reference.ts b/src/compiler/compile/utils/flatten_reference.ts index e0d05ee7c4..6715e68482 100644 --- a/src/compiler/compile/utils/flatten_reference.ts +++ b/src/compiler/compile/utils/flatten_reference.ts @@ -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; + } +} \ No newline at end of file diff --git a/test/runtime/samples/binding-input-group-each-1/_config.js b/test/runtime/samples/binding-input-group-each-1/_config.js new file mode 100644 index 0000000000..b0477ec705 --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-1/_config.js @@ -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: ` +
+ + + + + + +

Beta

+
+
+ + + + + + +

+
+
+ + + + + + +

Gamma

+
+ `, + + 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, ` +
+ + + + + + +

Alpha, Beta

+
+
+ + + + + + +

+
+
+ + + + + + +

Gamma

+
+ `); + inputs[3].checked = true; + await inputs[3].dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` +
+ + + + + + +

Alpha, Beta

+
+
+ + + + + + +

Alpha

+
+
+ + + + + + +

Gamma

+
+ `); + + inputs[8].checked = false; + await inputs[8].dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` +
+ + + + + + +

Alpha, Beta

+
+
+ + + + + + +

Alpha

+
+
+ + + + + + +

+
+ `); + + 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, ` +
+ + + + + + +

Beta, Gamma

+
+
+ + + + + + +

Gamma

+
+ `); + } +}; diff --git a/test/runtime/samples/binding-input-group-each-1/main.svelte b/test/runtime/samples/binding-input-group-each-1/main.svelte new file mode 100644 index 0000000000..5ed255c83a --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-1/main.svelte @@ -0,0 +1,15 @@ + + +{#each selected_array as selected} +
+ {#each values as value} + + {/each} +

{selected.map(v => v.name).join(', ')}

+
+{/each} diff --git a/test/runtime/samples/binding-input-group-each-2/_config.js b/test/runtime/samples/binding-input-group-each-2/_config.js new file mode 100644 index 0000000000..78d692d979 --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-2/_config.js @@ -0,0 +1,59 @@ +export default { + html: ` + + + + +

1, 2, 3

`, + + 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, ` + + + + +

2, 3

+ `); + + 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, ` + + + + +

1, 3

+ `); + } +}; diff --git a/test/runtime/samples/binding-input-group-each-2/main.svelte b/test/runtime/samples/binding-input-group-each-2/main.svelte new file mode 100644 index 0000000000..46f7e9e698 --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-2/main.svelte @@ -0,0 +1,17 @@ + + +{#each options as value} + +{/each} + +

{selected[0].join(', ')}

\ No newline at end of file diff --git a/test/runtime/samples/binding-input-group-each-3/_config.js b/test/runtime/samples/binding-input-group-each-3/_config.js new file mode 100644 index 0000000000..b0477ec705 --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-3/_config.js @@ -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: ` +
+ + + + + + +

Beta

+
+
+ + + + + + +

+
+
+ + + + + + +

Gamma

+
+ `, + + 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, ` +
+ + + + + + +

Alpha, Beta

+
+
+ + + + + + +

+
+
+ + + + + + +

Gamma

+
+ `); + inputs[3].checked = true; + await inputs[3].dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` +
+ + + + + + +

Alpha, Beta

+
+
+ + + + + + +

Alpha

+
+
+ + + + + + +

Gamma

+
+ `); + + inputs[8].checked = false; + await inputs[8].dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` +
+ + + + + + +

Alpha, Beta

+
+
+ + + + + + +

Alpha

+
+
+ + + + + + +

+
+ `); + + 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, ` +
+ + + + + + +

Beta, Gamma

+
+
+ + + + + + +

Gamma

+
+ `); + } +}; diff --git a/test/runtime/samples/binding-input-group-each-3/main.svelte b/test/runtime/samples/binding-input-group-each-3/main.svelte new file mode 100644 index 0000000000..42a7a1c4e9 --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-3/main.svelte @@ -0,0 +1,15 @@ + + +{#each selected_array as _, index} +
+ {#each values as value} + + {/each} +

{selected_array[index].map(v => v.name).join(', ')}

+
+{/each} diff --git a/test/runtime/samples/binding-input-group-each-4/_config.js b/test/runtime/samples/binding-input-group-each-4/_config.js new file mode 100644 index 0000000000..f1168858b0 --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-4/_config.js @@ -0,0 +1,153 @@ +export default { + html: ` + + + +

1

+ + + +

2

+ + + +

+ + + +

3

+ `, + + 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, ` + + + +

1, 3

+ + + +

2

+ + + +

+ + + +

3

+ `); + + inputs[9].checked = true; + await inputs[9].dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` + + + +

1, 3

+ + + +

2

+ + + +

+ + + +

1, 3

+ `); + + 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, ` + + + +

1, 3

+ + + +

3

+ + + +

1, 2

+ + + +

1

+ `); + + 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, ` + + + +

3

+ + + +

1

+ + + +

+ + + +

2

+ `); + } +}; diff --git a/test/runtime/samples/binding-input-group-each-4/main.svelte b/test/runtime/samples/binding-input-group-each-4/main.svelte new file mode 100644 index 0000000000..0bbf5ea763 --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-4/main.svelte @@ -0,0 +1,33 @@ + + +{#each selected_array_1 as selected} + {#each options as value} + + {/each} +

{selected.join(', ')}

+{/each} + +{#each selected_array_2 as selected} + {#each options as value} + + {/each} +

{selected.join(', ')}

+{/each} \ No newline at end of file diff --git a/test/runtime/samples/binding-input-group-each-5/_config.js b/test/runtime/samples/binding-input-group-each-5/_config.js new file mode 100644 index 0000000000..579225c627 --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-5/_config.js @@ -0,0 +1,160 @@ +export default { + html: ` + + + +

1

+ + + +

1, 2, 3

+ + + +

2

+ + + +

1

+ `, + + 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, ` + + + +

1, 3

+ + + +

1, 2, 3

+ + + +

2

+ + + +

1

+ `); + + inputs[8].checked = true; + await inputs[8].dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` + + + +

1, 3

+ + + +

1, 2, 3

+ + + +

2, 3

+ + + +

1

+ `); + + component.selected_index = [1, 1]; + + assert.htmlEqual(target.innerHTML, ` + + + +

1, 2, 3

+ + + +

1, 2, 3

+ + + +

1

+ + + +

1

+ `); + + 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, ` + + + +

1, 2

+ + + +

1, 2

+ + + +

1

+ + + +

1

+ `); + + 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); + } +}; diff --git a/test/runtime/samples/binding-input-group-each-5/main.svelte b/test/runtime/samples/binding-input-group-each-5/main.svelte new file mode 100644 index 0000000000..93bdce6510 --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-5/main.svelte @@ -0,0 +1,24 @@ + + +{#each selected_array as selected} + {#each selected_index as index} + {#each options as value} + + {/each} +

{selected[index].join(', ')}

+ {/each} +{/each}