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