mirror of https://github.com/sveltejs/svelte
implement <svelte:fragment> (#4556)
add validation and test replace svelte:slot -> svelte:fragment slot as a sugar syntax fix eslintpull/6041/head
parent
c4479d976b
commit
1d6e20f2a9
@ -0,0 +1,28 @@
|
||||
import Component from '../Component';
|
||||
import TemplateScope from './shared/TemplateScope';
|
||||
import Node from './shared/Node';
|
||||
import Let from './Let';
|
||||
import { INode } from './interfaces';
|
||||
|
||||
export default class DefaultSlotTemplate extends Node {
|
||||
type: 'SlotTemplate';
|
||||
scope: TemplateScope;
|
||||
children: INode[];
|
||||
lets: Let[] = [];
|
||||
slot_template_name = 'default';
|
||||
|
||||
constructor(
|
||||
component: Component,
|
||||
parent: INode,
|
||||
scope: TemplateScope,
|
||||
info: any,
|
||||
lets: Let[],
|
||||
children: INode[]
|
||||
) {
|
||||
super(component, parent, scope, info);
|
||||
this.type = 'SlotTemplate';
|
||||
this.children = children;
|
||||
this.scope = scope;
|
||||
this.lets = lets;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
import map_children from './shared/map_children';
|
||||
import Component from '../Component';
|
||||
import TemplateScope from './shared/TemplateScope';
|
||||
import Node from './shared/Node';
|
||||
import Let from './Let';
|
||||
import Attribute from './Attribute';
|
||||
import { INode } from './interfaces';
|
||||
|
||||
export default class SlotTemplate extends Node {
|
||||
type: 'SlotTemplate';
|
||||
scope: TemplateScope;
|
||||
children: INode[];
|
||||
lets: Let[] = [];
|
||||
slot_attribute: Attribute;
|
||||
slot_template_name: string = 'default';
|
||||
|
||||
constructor(
|
||||
component: Component,
|
||||
parent: INode,
|
||||
scope: TemplateScope,
|
||||
info: any
|
||||
) {
|
||||
super(component, parent, scope, info);
|
||||
|
||||
this.validate_slot_template_placement();
|
||||
|
||||
const has_let = info.attributes.some((node) => node.type === 'Let');
|
||||
if (has_let) {
|
||||
scope = scope.child();
|
||||
}
|
||||
|
||||
info.attributes.forEach((node) => {
|
||||
switch (node.type) {
|
||||
case 'Let': {
|
||||
const l = new Let(component, this, scope, node);
|
||||
this.lets.push(l);
|
||||
const dependencies = new Set([l.name.name]);
|
||||
|
||||
l.names.forEach((name) => {
|
||||
scope.add(name, dependencies, this);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'Attribute': {
|
||||
if (node.name === 'slot') {
|
||||
this.slot_attribute = new Attribute(component, this, scope, node);
|
||||
if (!this.slot_attribute.is_static) {
|
||||
component.error(node, {
|
||||
code: 'invalid-slot-attribute',
|
||||
message: 'slot attribute cannot have a dynamic value'
|
||||
});
|
||||
}
|
||||
const value = this.slot_attribute.get_static_value();
|
||||
if (typeof value === 'boolean') {
|
||||
component.error(node, {
|
||||
code: 'invalid-slot-attribute',
|
||||
message: 'slot attribute value is missing'
|
||||
});
|
||||
}
|
||||
this.slot_template_name = value as string;
|
||||
break;
|
||||
}
|
||||
throw new Error(`Invalid attribute '${node.name}' in <svelte:fragment>`);
|
||||
}
|
||||
default:
|
||||
throw new Error(`Not implemented: ${node.type}`);
|
||||
}
|
||||
});
|
||||
|
||||
this.scope = scope;
|
||||
this.children = map_children(component, this, this.scope, info.children);
|
||||
}
|
||||
|
||||
validate_slot_template_placement() {
|
||||
if (this.parent.type !== 'InlineComponent') {
|
||||
this.component.error(this, {
|
||||
code: 'invalid-slotted-content',
|
||||
message: '<svelte:fragment> must be a child of a component'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
import ElementWrapper from './index';
|
||||
import SlotWrapper from '../Slot';
|
||||
import Block from '../../Block';
|
||||
import { sanitize } from '../../../../utils/names';
|
||||
import InlineComponentWrapper from '../InlineComponent';
|
||||
import create_debugging_comment from '../shared/create_debugging_comment';
|
||||
import { get_slot_definition } from '../shared/get_slot_definition';
|
||||
|
||||
export default function create_slot_block(attribute, element: ElementWrapper | SlotWrapper, block: Block) {
|
||||
const owner = find_slot_owner(element.parent);
|
||||
|
||||
if (owner && owner.node.type === 'InlineComponent') {
|
||||
const name = attribute.get_static_value() as string;
|
||||
|
||||
if (!((owner as unknown) as InlineComponentWrapper).slots.has(name)) {
|
||||
const child_block = block.child({
|
||||
comment: create_debugging_comment(element.node, element.renderer.component),
|
||||
name: element.renderer.component.get_unique_name(
|
||||
`create_${sanitize(name)}_slot`
|
||||
),
|
||||
type: 'slot'
|
||||
});
|
||||
|
||||
const { scope, lets } = element.node;
|
||||
const seen = new Set(lets.map(l => l.name.name));
|
||||
|
||||
((owner as unknown) as InlineComponentWrapper).node.lets.forEach(l => {
|
||||
if (!seen.has(l.name.name)) lets.push(l);
|
||||
});
|
||||
|
||||
((owner as unknown) as InlineComponentWrapper).slots.set(
|
||||
name,
|
||||
get_slot_definition(child_block, scope, lets)
|
||||
);
|
||||
element.renderer.blocks.push(child_block);
|
||||
}
|
||||
|
||||
element.slot_block = ((owner as unknown) as InlineComponentWrapper).slots.get(
|
||||
name
|
||||
).block;
|
||||
|
||||
return element.slot_block;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
function find_slot_owner(owner) {
|
||||
while (owner) {
|
||||
if (owner.node.type === 'InlineComponent') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (owner.node.type === 'Element' && /-/.test(owner.node.name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
owner = owner.parent;
|
||||
}
|
||||
return owner;
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
import Wrapper from './shared/Wrapper';
|
||||
import Renderer from '../Renderer';
|
||||
import Block from '../Block';
|
||||
import FragmentWrapper from './Fragment';
|
||||
import create_debugging_comment from './shared/create_debugging_comment';
|
||||
import { get_slot_definition } from './shared/get_slot_definition';
|
||||
import { x } from 'code-red';
|
||||
import { sanitize } from '../../../utils/names';
|
||||
import { Identifier } from 'estree';
|
||||
import InlineComponentWrapper from './InlineComponent';
|
||||
import { extract_names } from 'periscopic';
|
||||
import { INode } from '../../nodes/interfaces';
|
||||
import Let from '../../nodes/Let';
|
||||
import TemplateScope from '../../nodes/shared/TemplateScope';
|
||||
|
||||
type NodeWithLets = INode & {
|
||||
scope: TemplateScope;
|
||||
lets: Let[];
|
||||
slot_template_name: string;
|
||||
};
|
||||
|
||||
export default class SlotTemplateWrapper extends Wrapper {
|
||||
node: NodeWithLets;
|
||||
fragment: FragmentWrapper;
|
||||
block: Block;
|
||||
parent: InlineComponentWrapper;
|
||||
|
||||
constructor(
|
||||
renderer: Renderer,
|
||||
block: Block,
|
||||
parent: Wrapper,
|
||||
node: NodeWithLets,
|
||||
strip_whitespace: boolean,
|
||||
next_sibling: Wrapper
|
||||
) {
|
||||
super(renderer, block, parent, node);
|
||||
|
||||
const { scope, lets, slot_template_name } = this.node;
|
||||
|
||||
lets.forEach(l => {
|
||||
extract_names(l.value || l.name).forEach(name => {
|
||||
renderer.add_to_context(name, true);
|
||||
});
|
||||
});
|
||||
|
||||
this.block = block.child({
|
||||
comment: create_debugging_comment(this.node, this.renderer.component),
|
||||
name: this.renderer.component.get_unique_name(
|
||||
`create_${sanitize(slot_template_name)}_slot`
|
||||
),
|
||||
type: 'slot'
|
||||
});
|
||||
this.renderer.blocks.push(this.block);
|
||||
|
||||
const seen = new Set(lets.map(l => l.name.name));
|
||||
this.parent.node.lets.forEach(l => {
|
||||
if (!seen.has(l.name.name)) lets.push(l);
|
||||
});
|
||||
|
||||
this.parent.set_slot(
|
||||
slot_template_name,
|
||||
get_slot_definition(this.block, scope, lets)
|
||||
);
|
||||
|
||||
this.fragment = new FragmentWrapper(
|
||||
renderer,
|
||||
this.block,
|
||||
node.type === 'SlotTemplate' ? node.children : [node],
|
||||
this,
|
||||
strip_whitespace,
|
||||
next_sibling
|
||||
);
|
||||
|
||||
this.block.parent.add_dependencies(this.block.dependencies);
|
||||
}
|
||||
|
||||
render() {
|
||||
this.fragment.render(this.block, null, x`#nodes` as Identifier);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import Renderer, { RenderOptions } from '../Renderer';
|
||||
import SlotTemplate from '../../nodes/SlotTemplate';
|
||||
import remove_whitespace_children from './utils/remove_whitespace_children';
|
||||
import { get_slot_scope } from './shared/get_slot_scope';
|
||||
import InlineComponent from '../../nodes/InlineComponent';
|
||||
import Element from '../../nodes/Element';
|
||||
|
||||
export default function(node: SlotTemplate | Element | InlineComponent, renderer: Renderer, options: RenderOptions & {
|
||||
slot_scopes: Map<any, any>;
|
||||
}) {
|
||||
const parent_inline_component = node.parent as InlineComponent;
|
||||
const children = remove_whitespace_children(node instanceof SlotTemplate ? node.children : [node], node.next);
|
||||
|
||||
renderer.push();
|
||||
renderer.render(children, options);
|
||||
|
||||
const lets = node.lets;
|
||||
const seen = new Set(lets.map(l => l.name.name));
|
||||
parent_inline_component.lets.forEach(l => {
|
||||
if (!seen.has(l.name.name)) lets.push(l);
|
||||
});
|
||||
|
||||
const slot_fragment_content = renderer.pop();
|
||||
if (!is_empty_template_literal(slot_fragment_content)) {
|
||||
if (options.slot_scopes.has(node.slot_template_name)) {
|
||||
if (node.slot_template_name === 'default') {
|
||||
throw new Error('Found elements without slot attribute when using slot="default"');
|
||||
}
|
||||
throw new Error(`Duplicate slot name "${node.slot_template_name}" in <${parent_inline_component.name}>`);
|
||||
}
|
||||
|
||||
options.slot_scopes.set(node.slot_template_name, {
|
||||
input: get_slot_scope(node.lets),
|
||||
output: slot_fragment_content
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function is_empty_template_literal(template_literal) {
|
||||
return (
|
||||
template_literal.expressions.length === 0 &&
|
||||
template_literal.quasis.length === 1 &&
|
||||
template_literal.quasis[0].value.raw === ''
|
||||
);
|
||||
}
|
@ -0,0 +1 @@
|
||||
<slot name="footer" />
|
@ -0,0 +1,15 @@
|
||||
export default {
|
||||
html: `
|
||||
<button>Disable</button>
|
||||
<button slot="footer">Button</button>
|
||||
<button slot="footer">Button</button>
|
||||
`,
|
||||
async test({ assert, component, target, window }) {
|
||||
const [btn, btn1, btn2] = target.querySelectorAll('button');
|
||||
|
||||
await btn.dispatchEvent(new window.MouseEvent('click'));
|
||||
|
||||
assert.equal(btn1.disabled, true);
|
||||
assert.equal(btn2.disabled, true);
|
||||
}
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
import Component from './Component.svelte';
|
||||
|
||||
let disabled = false;
|
||||
</script>
|
||||
|
||||
<button on:click={() => disabled = !disabled}>Disable</button>
|
||||
|
||||
<Component>
|
||||
<button slot="footer" disabled={disabled}>Button</button>
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<button disabled={disabled} slot="footer">Button</button>
|
||||
</Component>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let name;
|
||||
</script>
|
||||
|
||||
<span>Hello {name}</span>
|
@ -0,0 +1 @@
|
||||
<slot name="name"></slot>
|
@ -0,0 +1,7 @@
|
||||
export default {
|
||||
preserveIdentifiers: true,
|
||||
|
||||
html: `
|
||||
<span>Hello world</span>
|
||||
`
|
||||
};
|
@ -0,0 +1,10 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
import Hello from './Hello.svelte';
|
||||
|
||||
let name = 'world';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
<Hello slot="name" {name} />
|
||||
</Nested>
|
@ -0,0 +1 @@
|
||||
<span>Hello</span>
|
@ -0,0 +1 @@
|
||||
<slot name="name"></slot>
|
@ -0,0 +1 @@
|
||||
<span>world</span>
|
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
html: `
|
||||
<span>Hello</span>
|
||||
<span>world</span>
|
||||
`
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
import Hello from './Hello.svelte';
|
||||
import World from './World.svelte';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
<Hello slot="name" />
|
||||
</Nested>
|
||||
|
||||
<Nested>
|
||||
<World slot="name" />
|
||||
</Nested>
|
@ -0,0 +1 @@
|
||||
<p>bar</p>
|
@ -0,0 +1 @@
|
||||
<p>foo</p>
|
@ -0,0 +1,5 @@
|
||||
<div>
|
||||
<slot/>
|
||||
<slot name='bar'/>
|
||||
<slot name='foo'/>
|
||||
</div>
|
@ -0,0 +1,9 @@
|
||||
export default {
|
||||
html: `
|
||||
<div>
|
||||
Hello
|
||||
<p>bar</p>
|
||||
<p>foo</p>
|
||||
</div>
|
||||
`
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
import Foo from './Foo.svelte';
|
||||
import Bar from './Bar.svelte';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
Hello
|
||||
|
||||
<Foo slot='foo' />
|
||||
<Bar slot='bar' />
|
||||
</Nested>
|
@ -0,0 +1 @@
|
||||
<slot value="Hi" />
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
error: 'Duplicate slot name "foo" in <Nested>'
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
<svelte:fragment slot="foo">{value}</svelte:fragment>
|
||||
<svelte:fragment slot="foo">{value}</svelte:fragment>
|
||||
</Nested>
|
@ -0,0 +1 @@
|
||||
<slot value="Hi" />
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
error: 'Duplicate slot name "foo" in <Nested>'
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
<svelte:fragment slot="foo">{value}</svelte:fragment>
|
||||
<p slot="foo">{value}</p>
|
||||
</Nested>
|
@ -0,0 +1 @@
|
||||
<slot />
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
error: 'Found elements without slot attribute when using slot="default"'
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
<svelte:fragment slot="default">value</svelte:fragment>
|
||||
<p>value</p>
|
||||
</Nested>
|
@ -0,0 +1 @@
|
||||
<slot value="Hi" />
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
error: 'Duplicate slot name "foo" in <Nested>'
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
<p slot="foo">{value}</p>
|
||||
<p slot="foo">{value}</p>
|
||||
</Nested>
|
@ -0,0 +1 @@
|
||||
<slot name="name"></slot>
|
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
html: `
|
||||
<span>Hello</span>
|
||||
<span>world</span>
|
||||
`
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
<svelte:fragment slot="name">
|
||||
<span>Hello</span>
|
||||
</svelte:fragment>
|
||||
</Nested>
|
||||
|
||||
<Nested>
|
||||
<svelte:fragment slot="name">
|
||||
<span>world</span>
|
||||
</svelte:fragment>
|
||||
</Nested>
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
export let things;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#each things as thing}
|
||||
<slot name="main" {thing}/>
|
||||
{/each}
|
||||
</div>
|
@ -0,0 +1,24 @@
|
||||
export default {
|
||||
props: {
|
||||
things: [1, 2, 3]
|
||||
},
|
||||
|
||||
html: `
|
||||
<div>
|
||||
<span>1</span>
|
||||
<span>2</span>
|
||||
<span>3</span>
|
||||
</div>`,
|
||||
|
||||
test({ assert, component, target }) {
|
||||
component.things = [1, 2, 3, 4];
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>
|
||||
<span>1</span>
|
||||
<span>2</span>
|
||||
<span>3</span>
|
||||
<span>4</span>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
|
||||
export let things;
|
||||
</script>
|
||||
|
||||
<Nested {things} let:thing={x}>
|
||||
<svelte:fragment slot="main">
|
||||
<span>{x}</span>
|
||||
</svelte:fragment>
|
||||
</Nested>
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
let count = 0;
|
||||
</script>
|
||||
|
||||
<button on:click="{() => count += 1}">+1</button>
|
||||
<slot name="main" count={count}/>
|
@ -0,0 +1,18 @@
|
||||
export default {
|
||||
html: `
|
||||
<button>+1</button>
|
||||
<span>0</span>
|
||||
`,
|
||||
|
||||
async test({ assert, target, window }) {
|
||||
const button = target.querySelector('button');
|
||||
const click = new window.MouseEvent('click');
|
||||
|
||||
await button.dispatchEvent(click);
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<button>+1</button>
|
||||
<span>1</span>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested let:count>
|
||||
<svelte:fragment slot="main">
|
||||
<span>{count}</span>
|
||||
</svelte:fragment>
|
||||
</Nested>
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
let count = 0;
|
||||
</script>
|
||||
|
||||
<button on:click="{() => count += 1}">+1</button>
|
||||
<slot name='main' c={count}/>
|
@ -0,0 +1,18 @@
|
||||
export default {
|
||||
html: `
|
||||
<button>+1</button>
|
||||
<span>0 (undefined)</span>
|
||||
`,
|
||||
|
||||
async test({ assert, target, window }) {
|
||||
const button = target.querySelector('button');
|
||||
const click = new window.MouseEvent('click');
|
||||
|
||||
await button.dispatchEvent(click);
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<button>+1</button>
|
||||
<span>1 (undefined)</span>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
<svelte:fragment slot="main" let:c let:count>
|
||||
<span>{c} ({count})</span>
|
||||
</svelte:fragment>
|
||||
</Nested>
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let foo = 'a';
|
||||
</script>
|
||||
|
||||
<div on:click="{() => foo = 'b'}">
|
||||
<slot name="main" {foo}></slot>
|
||||
</div>
|
@ -0,0 +1,20 @@
|
||||
export default {
|
||||
html: `
|
||||
<div>
|
||||
<p>a</p>
|
||||
</div>
|
||||
`,
|
||||
|
||||
async test({ assert, target, window }) {
|
||||
const div = target.querySelector('div');
|
||||
const click = new window.MouseEvent('click');
|
||||
|
||||
await div.dispatchEvent(click);
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>
|
||||
<p>b</p>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
<svelte:fragment slot="main" let:foo={bar}>
|
||||
<p>{bar}</p>
|
||||
</svelte:fragment>
|
||||
</Nested>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let props;
|
||||
</script>
|
||||
|
||||
<slot name="main" value={props} data={Array.isArray(props) ? props[0] : props.a} />
|
@ -0,0 +1,68 @@
|
||||
export default {
|
||||
html: `
|
||||
<div>
|
||||
hello world 0 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
<div>
|
||||
hello world 0 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
<div>
|
||||
hello world 0 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
`,
|
||||
async test({ assert, component, target, window }) {
|
||||
const [button1, button2, button3] = target.querySelectorAll('button');
|
||||
const event = new window.MouseEvent('click');
|
||||
|
||||
await button1.dispatchEvent(event);
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>
|
||||
hello world 1 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
<div>
|
||||
hello world 0 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
<div>
|
||||
hello world 0 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
`);
|
||||
|
||||
await button2.dispatchEvent(event);
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>
|
||||
hello world 1 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
<div>
|
||||
hello world 1 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
<div>
|
||||
hello world 0 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
`);
|
||||
|
||||
await button3.dispatchEvent(event);
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>
|
||||
hello world 1 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
<div>
|
||||
hello world 1 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
<div>
|
||||
hello world 1 hello
|
||||
<button>Increment</button>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
<script>
|
||||
import Nested from "./Nested.svelte";
|
||||
let c = 0, d = 0, e = 0;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<Nested props={['hello', 'world']}>
|
||||
<svelte:fragment slot="main" let:value={pair} let:data={foo}>
|
||||
{pair[0]} {pair[1]} {c} {foo}
|
||||
</svelte:fragment>
|
||||
</Nested>
|
||||
|
||||
<button on:click={() => { c += 1; }}>Increment</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Nested props={['hello', 'world']}>
|
||||
<svelte:fragment slot="main" let:value={[a, b]} let:data={foo}>
|
||||
{a} {b} {d} {foo}
|
||||
</svelte:fragment>
|
||||
</Nested>
|
||||
|
||||
<button on:click={() => { d += 1; }}>Increment</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Nested props={{ a: 'hello', b: 'world' }}>
|
||||
<svelte:fragment slot="main" let:value={{ a, b }} let:data={foo}>
|
||||
{a} {b} {e} {foo}
|
||||
</svelte:fragment>
|
||||
</Nested>
|
||||
|
||||
<button on:click={() => { e += 1; }}>Increment</button>
|
||||
</div>
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
export let things;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#each things as thing}
|
||||
<slot name="item" {thing}/>
|
||||
{/each}
|
||||
</div>
|
@ -0,0 +1,34 @@
|
||||
export default {
|
||||
props: {
|
||||
things: [
|
||||
{ num: 1 },
|
||||
{ num: 2 },
|
||||
{ num: 3 }
|
||||
]
|
||||
},
|
||||
|
||||
html: `
|
||||
<div>
|
||||
<span>1</span>
|
||||
<span>2</span>
|
||||
<span>3</span>
|
||||
</div>`,
|
||||
|
||||
test({ assert, component, target }) {
|
||||
component.things = [
|
||||
{ num: 1 },
|
||||
{ num: 2 },
|
||||
{ num: 3 },
|
||||
{ num: 4 }
|
||||
];
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>
|
||||
<span>1</span>
|
||||
<span>2</span>
|
||||
<span>3</span>
|
||||
<span>4</span>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
|
||||
export let things;
|
||||
</script>
|
||||
|
||||
<Nested {things}>
|
||||
<svelte:fragment slot="item" let:thing="{{ num }}">
|
||||
<span>{num}</span>
|
||||
</svelte:fragment>
|
||||
</Nested>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import B from './B.svelte';
|
||||
export let x;
|
||||
</script>
|
||||
|
||||
<B {x} let:reflected>
|
||||
<svelte:fragment slot="main">
|
||||
<span>{reflected}</span>
|
||||
<slot name="main" {reflected} />
|
||||
</svelte:fragment>
|
||||
</B>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let x;
|
||||
</script>
|
||||
|
||||
<slot name="main" reflected={x}/>
|
@ -0,0 +1,19 @@
|
||||
export default {
|
||||
html: `
|
||||
<span>1</span>
|
||||
<span>1</span>
|
||||
<span>1</span>
|
||||
<span>1</span>
|
||||
`,
|
||||
|
||||
async test({ assert, target, component }) {
|
||||
component.x = 2;
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<span>2</span>
|
||||
<span>2</span>
|
||||
<span>2</span>
|
||||
<span>2</span>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
<script>
|
||||
import A from './A.svelte';
|
||||
export let x = 1;
|
||||
</script>
|
||||
|
||||
<A {x}>
|
||||
<svelte:fragment slot="main" let:reflected>
|
||||
<span>{reflected}</span>
|
||||
</svelte:fragment>
|
||||
</A>
|
||||
|
||||
<A {x} let:reflected>
|
||||
<svelte:fragment slot="main">
|
||||
<span>{reflected}</span>
|
||||
</svelte:fragment>
|
||||
</A>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let x;
|
||||
</script>
|
||||
|
||||
<slot name="foo" reflected={x}/>
|
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
html: `
|
||||
<span class="1">1</span>
|
||||
0
|
||||
`,
|
||||
async test({ assert, target, component, window }) {
|
||||
component.x = 2;
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<span class="2">2</span>
|
||||
0
|
||||
`);
|
||||
|
||||
const span = target.querySelector('span');
|
||||
await span.dispatchEvent(new window.MouseEvent('click'));
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<span class="2">2</span>
|
||||
2
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
<script>
|
||||
import A from './A.svelte';
|
||||
export let x = 1;
|
||||
let y = 0;
|
||||
</script>
|
||||
|
||||
<A {x}>
|
||||
<svelte:fragment slot="foo" let:reflected>
|
||||
<span
|
||||
on:click={() => y = reflected}
|
||||
class={reflected}
|
||||
>
|
||||
{reflected}
|
||||
</span>
|
||||
</svelte:fragment>
|
||||
</A>
|
||||
{ y }
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
export let items;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#each items as item, index}
|
||||
<slot name="main" {index}/>
|
||||
{/each}
|
||||
</div>
|
@ -0,0 +1,26 @@
|
||||
export default {
|
||||
html: `
|
||||
<div>
|
||||
<label>1: <input></label>
|
||||
<label>2: <input></label>
|
||||
<label>3: <input></label>
|
||||
</div>
|
||||
`,
|
||||
|
||||
ssrHtml: `
|
||||
<div>
|
||||
<label>1: <input value="a"></label>
|
||||
<label>2: <input value="b"></label>
|
||||
<label>3: <input value="c"></label>
|
||||
</div>
|
||||
`,
|
||||
|
||||
async test({ assert, component, target, window }) {
|
||||
const inputs = target.querySelectorAll('input');
|
||||
|
||||
inputs[2].value = 'd';
|
||||
await inputs[2].dispatchEvent(new window.Event('input'));
|
||||
|
||||
assert.deepEqual(component.letters, ['a', 'b', 'd']);
|
||||
}
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
|
||||
export let letters = ['a', 'b', 'c'];
|
||||
</script>
|
||||
|
||||
<Nested items={letters}>
|
||||
<svelte:fragment slot="main" let:index>
|
||||
<label>
|
||||
{index + 1}: <input bind:value={letters[index]}>
|
||||
</label>
|
||||
</svelte:fragment>
|
||||
</Nested>
|
@ -0,0 +1 @@
|
||||
<slot name="main"/>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let prop
|
||||
</script>
|
||||
|
||||
<slot name="main" value={prop} />
|
@ -0,0 +1,12 @@
|
||||
export default {
|
||||
props: {
|
||||
prop: 'a'
|
||||
},
|
||||
|
||||
html: 'a',
|
||||
|
||||
test({ assert, component, target }) {
|
||||
component.prop = 'b';
|
||||
assert.htmlEqual( target.innerHTML, 'b' );
|
||||
}
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
<script>
|
||||
import Outer from './Outer.svelte'
|
||||
import Inner from './Inner.svelte'
|
||||
|
||||
export let prop
|
||||
</script>
|
||||
|
||||
<Outer {prop}>
|
||||
<svelte:fragment slot="main" let:value>
|
||||
<Inner>
|
||||
<svelte:fragment slot="main">
|
||||
{value}
|
||||
</svelte:fragment>
|
||||
</Inner>
|
||||
</svelte:fragment>
|
||||
</Outer>
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
export let things;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#each things as thing}
|
||||
<slot name="foo" {thing}/>
|
||||
{/each}
|
||||
</div>
|
@ -0,0 +1,24 @@
|
||||
export default {
|
||||
props: {
|
||||
things: [1, 2, 3]
|
||||
},
|
||||
|
||||
html: `
|
||||
<div>
|
||||
<div slot="foo"><span>1</span></div>
|
||||
<div slot="foo"><span>2</span></div>
|
||||
<div slot="foo"><span>3</span></div>
|
||||
</div>`,
|
||||
|
||||
test({ assert, component, target }) {
|
||||
component.things = [1, 2, 3, 4];
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>
|
||||
<div slot="foo"><span>1</span></div>
|
||||
<div slot="foo"><span>2</span></div>
|
||||
<div slot="foo"><span>3</span></div>
|
||||
<div slot="foo"><span>4</span></div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
|
||||
export let things;
|
||||
</script>
|
||||
|
||||
<Nested {things}>
|
||||
<div slot="foo" let:thing>
|
||||
<span>{thing}</span>
|
||||
</div>
|
||||
</Nested>
|
@ -0,0 +1 @@
|
||||
<slot value="Hi" />
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
html: '<p>Hi</p>'
|
||||
};
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested let:value>
|
||||
<p>{value}</p>
|
||||
</Nested>
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
export let things;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#each things as thing}
|
||||
<slot {thing}/>
|
||||
{/each}
|
||||
</div>
|
@ -0,0 +1,24 @@
|
||||
export default {
|
||||
props: {
|
||||
things: [1, 2, 3]
|
||||
},
|
||||
|
||||
html: `
|
||||
<div>
|
||||
<span>1</span>
|
||||
<span>2</span>
|
||||
<span>3</span>
|
||||
</div>`,
|
||||
|
||||
test({ assert, component, target }) {
|
||||
component.things = [1, 2, 3, 4];
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>
|
||||
<span>1</span>
|
||||
<span>2</span>
|
||||
<span>3</span>
|
||||
<span>4</span>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
|
||||
export let things;
|
||||
</script>
|
||||
|
||||
<Nested {things} let:thing>
|
||||
<span>{thing}</span>
|
||||
</Nested>
|
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested>
|
||||
<svelte:fragment slot="name">
|
||||
<slot />
|
||||
</svelte:fragment>
|
||||
</Nested>
|
||||
|
||||
<Nested>
|
||||
<svelte:fragment slot="name">
|
||||
<slot name="b" />
|
||||
</svelte:fragment>
|
||||
</Nested>
|
@ -0,0 +1 @@
|
||||
<slot name="name"></slot>
|
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
html: `
|
||||
Default
|
||||
<p>B slot</p>
|
||||
`
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
import Child from './Child.svelte';
|
||||
</script>
|
||||
|
||||
<Child>
|
||||
<svelte:fragment>
|
||||
Default
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="b">
|
||||
<p>B slot</p>
|
||||
</svelte:fragment>
|
||||
</Child>
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
export let name;
|
||||
</script>
|
||||
|
||||
<div>Hello</div>
|
||||
<div>{name}</div>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue