mirror of https://github.com/sveltejs/svelte
[feature] Dynamic elements implementation <svelte:element> (#6898)
Closes #2324 Co-authored-by: Alfred Ringstad <alfred.ringstad@hyperlab.se> Co-authored-by: Simon Holthausen <simon.holthausen@accso.de> Co-authored-by: tanhauhau <lhtan93@gmail.com>pull/7434/head
parent
54197c5a1f
commit
e0d93254fd
@ -0,0 +1,18 @@
|
||||
<script>
|
||||
const options = ['h1', 'h3', 'p'];
|
||||
let selected = options[0];
|
||||
</script>
|
||||
|
||||
<select bind:value={selected}>
|
||||
{#each options as option}
|
||||
<option value={option}>{option}</option>
|
||||
{/each}
|
||||
</select>
|
||||
|
||||
{#if selected === 'h1'}
|
||||
<h1>I'm a h1 tag</h1>
|
||||
{:else if selected === 'h3'}
|
||||
<h3>I'm a h3 tag</h3>
|
||||
{:else if selected === 'p'}
|
||||
<p>I'm a p tag</p>
|
||||
{/if}
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
const options = ['h1', 'h3', 'p'];
|
||||
let selected = options[0];
|
||||
</script>
|
||||
|
||||
<select bind:value={selected}>
|
||||
{#each options as option}
|
||||
<option value={option}>{option}</option>
|
||||
{/each}
|
||||
</select>
|
||||
|
||||
<svelte:element this={selected}>I'm a {selected} tag</svelte:element>
|
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: <svelte:element>
|
||||
---
|
||||
|
||||
Sometimes we don't know in advance what kind of DOM element to render. `<svelte:element>` comes in handy here. Instead of a sequence of `if` blocks...
|
||||
|
||||
```html
|
||||
{#if selected === 'h1'}
|
||||
<h1>I'm a h1 tag</h1>
|
||||
{:else if selected === 'h3'}
|
||||
<h3>I'm a h3 tag</h3>
|
||||
{:else if selected === 'p'}
|
||||
<p>I'm a p tag</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
...we can have a single dynamic component:
|
||||
|
||||
```html
|
||||
<svelte:element this={selected}>I'm a {selected} tag</svelte:element>
|
||||
```
|
||||
|
||||
The `this` value can be any string, or a falsy value — if it's falsy, no element is rendered.
|
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
warnings: [
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 86,
|
||||
column: 8,
|
||||
line: 7
|
||||
},
|
||||
frame:
|
||||
' 5: color: red;\n 6: }\n 7: .unused {\n ^\n 8: font-style: italic;\n 9: }',
|
||||
message: 'Unused CSS selector ".unused"',
|
||||
pos: 79,
|
||||
start: {
|
||||
character: 79,
|
||||
column: 1,
|
||||
line: 7
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
@ -0,0 +1 @@
|
||||
.used.svelte-xyz{color:red}
|
@ -0,0 +1 @@
|
||||
<div class="used svelte-xyz"></div>
|
@ -0,0 +1,10 @@
|
||||
<svelte:element this="div" class="used" />
|
||||
|
||||
<style>
|
||||
.used {
|
||||
color: red;
|
||||
}
|
||||
.unused {
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,2 @@
|
||||
<svelte:element this="div"></svelte:element>
|
||||
<svelte:element this="div" class="foo"></svelte:element>
|
@ -0,0 +1,50 @@
|
||||
{
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 101,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 44,
|
||||
"type": "Element",
|
||||
"name": "svelte:element",
|
||||
"attributes": [],
|
||||
"children": [],
|
||||
"tag": "div"
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
"start": 44,
|
||||
"end": 45,
|
||||
"data": "\n",
|
||||
"raw": "\n"
|
||||
},
|
||||
{
|
||||
"start": 45,
|
||||
"end": 101,
|
||||
"type": "Element",
|
||||
"name": "svelte:element",
|
||||
"attributes": [
|
||||
{
|
||||
"type": "Attribute",
|
||||
"start": 72,
|
||||
"end": 83,
|
||||
"name": "class",
|
||||
"value": [
|
||||
{
|
||||
"type": "Text",
|
||||
"start": 79,
|
||||
"end": 82,
|
||||
"data": "foo",
|
||||
"raw": "foo"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"children": [],
|
||||
"tag": "div"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
<svelte:element this={tag}></svelte:element>
|
||||
<svelte:element this={tag} class="foo"></svelte:element>
|
@ -0,0 +1,80 @@
|
||||
{
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 101,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 44,
|
||||
"type": "Element",
|
||||
"name": "svelte:element",
|
||||
"attributes": [],
|
||||
"children": [],
|
||||
"tag": {
|
||||
"start": 22,
|
||||
"end": 25,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 22
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 25
|
||||
}
|
||||
},
|
||||
"name": "tag",
|
||||
"type": "Identifier"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
"start": 44,
|
||||
"end": 45,
|
||||
"data": "\n",
|
||||
"raw": "\n"
|
||||
},
|
||||
{
|
||||
"start": 45,
|
||||
"end": 101,
|
||||
"type": "Element",
|
||||
"name": "svelte:element",
|
||||
"children": [],
|
||||
"attributes": [
|
||||
{
|
||||
"type": "Attribute",
|
||||
"start": 72,
|
||||
"end": 83,
|
||||
"name": "class",
|
||||
"value": [
|
||||
{
|
||||
"type": "Text",
|
||||
"start": 79,
|
||||
"end": 82,
|
||||
"data": "foo",
|
||||
"raw": "foo"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tag": {
|
||||
"start": 67,
|
||||
"end": 70,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 22
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 25
|
||||
}
|
||||
},
|
||||
"name": "tag",
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
let logs = [];
|
||||
|
||||
export default {
|
||||
html: `
|
||||
<h1>tag is h1.</h1>
|
||||
`,
|
||||
props: {
|
||||
logs
|
||||
},
|
||||
after_test() {
|
||||
logs = [];
|
||||
},
|
||||
|
||||
async test({ assert, component, target }) {
|
||||
assert.equal(component.tag, 'h1');
|
||||
|
||||
assert.deepEqual(logs, ['create: h1,opt1']);
|
||||
component.opt = 'opt2';
|
||||
|
||||
assert.equal(component.tag, 'h1');
|
||||
assert.deepEqual(logs, ['create: h1,opt1', 'update: h1,opt2']);
|
||||
|
||||
component.tag = 'h2';
|
||||
|
||||
assert.equal(component.tag, 'h2');
|
||||
assert.deepEqual(logs, [
|
||||
'create: h1,opt1',
|
||||
'update: h1,opt2',
|
||||
'destroy',
|
||||
'create: h2,opt2'
|
||||
]);
|
||||
assert.htmlEqual(target.innerHTML, '<h2>tag is h2.</h2>');
|
||||
|
||||
component.tag = false;
|
||||
assert.deepEqual(logs, [
|
||||
'create: h1,opt1',
|
||||
'update: h1,opt2',
|
||||
'destroy',
|
||||
'create: h2,opt2',
|
||||
'destroy'
|
||||
]);
|
||||
|
||||
assert.htmlEqual(target.innerHTML, '');
|
||||
}
|
||||
};
|
@ -0,0 +1,14 @@
|
||||
<script>
|
||||
export let logs = [];
|
||||
export let tag = "h1";
|
||||
export let opt = "opt1";
|
||||
function foo(node, {tag, opt}) {
|
||||
logs.push(`create: ${tag},${opt}`);
|
||||
return {
|
||||
update: ({tag, opt}) => logs.push(`update: ${tag},${opt}`),
|
||||
destroy: () => logs.push('destroy'),
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag} use:foo={{tag, opt}}>tag is {tag}.</svelte:element>
|
@ -0,0 +1,105 @@
|
||||
let originalDivGetBoundingClientRect;
|
||||
let originalSpanGetBoundingClientRect;
|
||||
let originalParagraphGetBoundingClientRect;
|
||||
|
||||
export default {
|
||||
skip_if_ssr: true,
|
||||
props: {
|
||||
things: [
|
||||
{ id: 1, name: 'a' },
|
||||
{ id: 2, name: 'b' },
|
||||
{ id: 3, name: 'c' },
|
||||
{ id: 4, name: 'd' },
|
||||
{ id: 5, name: 'e' }
|
||||
],
|
||||
tag: 'div'
|
||||
},
|
||||
|
||||
html: `
|
||||
<div>a</div>
|
||||
<div>b</div>
|
||||
<div>c</div>
|
||||
<div>d</div>
|
||||
<div>e</div>
|
||||
`,
|
||||
|
||||
before_test() {
|
||||
originalDivGetBoundingClientRect =
|
||||
window.HTMLDivElement.prototype.getBoundingClientRect;
|
||||
originalSpanGetBoundingClientRect =
|
||||
window.HTMLSpanElement.prototype.getBoundingClientRect;
|
||||
originalParagraphGetBoundingClientRect =
|
||||
window.HTMLParagraphElement.prototype.getBoundingClientRect;
|
||||
|
||||
window.HTMLDivElement.prototype.getBoundingClientRect =
|
||||
fakeGetBoundingClientRect;
|
||||
window.HTMLSpanElement.prototype.getBoundingClientRect =
|
||||
fakeGetBoundingClientRect;
|
||||
window.HTMLParagraphElement.prototype.getBoundingClientRect =
|
||||
fakeGetBoundingClientRect;
|
||||
|
||||
function fakeGetBoundingClientRect() {
|
||||
const index = [...this.parentNode.children].indexOf(this);
|
||||
const top = index * 30;
|
||||
|
||||
return {
|
||||
left: 0,
|
||||
right: 100,
|
||||
top,
|
||||
bottom: top + 20
|
||||
};
|
||||
}
|
||||
},
|
||||
after_test() {
|
||||
window.HTMLDivElement.prototype.getBoundingClientRect =
|
||||
originalDivGetBoundingClientRect;
|
||||
window.HTMLSpanElement.prototype.getBoundingClientRect =
|
||||
originalSpanGetBoundingClientRect;
|
||||
window.HTMLParagraphElement.prototype.getBoundingClientRect =
|
||||
originalParagraphGetBoundingClientRect;
|
||||
},
|
||||
|
||||
async test({ assert, component, target, raf }) {
|
||||
// switch tag and things at the same time
|
||||
await component.update('p', [
|
||||
{ id: 5, name: 'e' },
|
||||
{ id: 2, name: 'b' },
|
||||
{ id: 3, name: 'c' },
|
||||
{ id: 4, name: 'd' },
|
||||
{ id: 1, name: 'a' }
|
||||
]);
|
||||
|
||||
const ps = document.querySelectorAll('p');
|
||||
assert.equal(ps[0].dy, 120);
|
||||
assert.equal(ps[4].dy, -120);
|
||||
|
||||
raf.tick(50);
|
||||
assert.equal(ps[0].dy, 60);
|
||||
assert.equal(ps[4].dy, -60);
|
||||
|
||||
raf.tick(100);
|
||||
assert.equal(ps[0].dy, 0);
|
||||
assert.equal(ps[4].dy, 0);
|
||||
|
||||
await component.update('span', [
|
||||
{ id: 1, name: 'a' },
|
||||
{ id: 2, name: 'b' },
|
||||
{ id: 3, name: 'c' },
|
||||
{ id: 4, name: 'd' },
|
||||
{ id: 5, name: 'e' }
|
||||
]);
|
||||
|
||||
const spans = document.querySelectorAll('span');
|
||||
|
||||
assert.equal(spans[0].dy, 120);
|
||||
assert.equal(spans[4].dy, -120);
|
||||
|
||||
raf.tick(150);
|
||||
assert.equal(spans[0].dy, 60);
|
||||
assert.equal(spans[4].dy, -60);
|
||||
|
||||
raf.tick(200);
|
||||
assert.equal(spans[0].dy, 0);
|
||||
assert.equal(spans[4].dy, 0);
|
||||
}
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
<script>
|
||||
export let things;
|
||||
export let tag;
|
||||
|
||||
function flip(node, animation, params) {
|
||||
const dx = animation.from.left - animation.to.left;
|
||||
const dy = animation.from.top - animation.to.top;
|
||||
|
||||
return {
|
||||
duration: 100,
|
||||
tick: (t, u) => {
|
||||
node.dx = u * dx;
|
||||
node.dy = u * dy;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function update(new_tag, new_things) {
|
||||
things = new_things;
|
||||
tag = new_tag;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each things as thing (thing.id)}
|
||||
<svelte:element this={tag} animate:flip>{thing.name}</svelte:element>
|
||||
{/each}
|
@ -0,0 +1,62 @@
|
||||
export default {
|
||||
props: {
|
||||
things: [
|
||||
{ id: 1, name: 'a' },
|
||||
{ id: 2, name: 'b' },
|
||||
{ id: 3, name: 'c' },
|
||||
{ id: 4, name: 'd' },
|
||||
{ id: 5, name: 'e' }
|
||||
],
|
||||
tag: 'div'
|
||||
},
|
||||
|
||||
html: `
|
||||
<div>a</div>
|
||||
<div>b</div>
|
||||
<div>c</div>
|
||||
<div>d</div>
|
||||
<div>e</div>
|
||||
`,
|
||||
|
||||
test({ assert, component, target, raf }) {
|
||||
component.tag = 'p';
|
||||
assert.equal(target.querySelectorAll('p').length, 5);
|
||||
|
||||
component.tag = 'div';
|
||||
let divs = target.querySelectorAll('div');
|
||||
divs.forEach(div => {
|
||||
div.getBoundingClientRect = function() {
|
||||
const index = [...this.parentNode.children].indexOf(this);
|
||||
const top = index * 30;
|
||||
|
||||
return {
|
||||
left: 0,
|
||||
right: 100,
|
||||
top,
|
||||
bottom: top + 20
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
component.things = [
|
||||
{ id: 5, name: 'e' },
|
||||
{ id: 2, name: 'b' },
|
||||
{ id: 3, name: 'c' },
|
||||
{ id: 4, name: 'd' },
|
||||
{ id: 1, name: 'a' }
|
||||
];
|
||||
|
||||
divs = target.querySelectorAll('div');
|
||||
assert.ok(~divs[0].style.animation.indexOf('__svelte'));
|
||||
assert.equal(divs[1].style.animation, '');
|
||||
assert.equal(divs[2].style.animation, '');
|
||||
assert.equal(divs[3].style.animation, '');
|
||||
assert.ok(~divs[4].style.animation.indexOf('__svelte'));
|
||||
|
||||
raf.tick(100);
|
||||
assert.deepEqual([
|
||||
divs[0].style.animation,
|
||||
divs[4].style.animation
|
||||
], ['', '']);
|
||||
}
|
||||
};
|
@ -0,0 +1,18 @@
|
||||
<script>
|
||||
export let things;
|
||||
export let tag;
|
||||
|
||||
function flip(node, animation, params) {
|
||||
const dx = animation.from.left - animation.to.left;
|
||||
const dy = animation.from.top - animation.to.top;
|
||||
|
||||
return {
|
||||
duration: 100,
|
||||
css: (t, u) => `transform: translate(${u + dx}px, ${u * dy}px)`
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each things as thing (thing.id)}
|
||||
<svelte:element this={tag} animate:flip>{thing.name}</svelte:element>
|
||||
{/each}
|
@ -0,0 +1,17 @@
|
||||
export default {
|
||||
props: {
|
||||
tag: 'div'
|
||||
},
|
||||
html: '<div style="color: red;">Foo</div>',
|
||||
|
||||
test({ assert, component, target }) {
|
||||
component.tag = 'h1';
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<h1 style="color: red;">Foo</h1>
|
||||
`
|
||||
);
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let tag = 'div';
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag} style="color: red;">Foo</svelte:element>
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
error: "'value' is not a valid binding on <svelte:element> elements"
|
||||
};
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
const tag = "div";
|
||||
let value;
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag} bind:value></svelte:element>
|
@ -0,0 +1,8 @@
|
||||
export default {
|
||||
html: '<div></div>',
|
||||
|
||||
test({ assert, component, target }) {
|
||||
const div = target.querySelector('div');
|
||||
assert.equal(div, component.foo);
|
||||
}
|
||||
};
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
const tag = "div";
|
||||
export let foo;
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag} bind:this={foo}></svelte:element>
|
@ -0,0 +1,17 @@
|
||||
export default {
|
||||
props: {
|
||||
tag: 'div'
|
||||
},
|
||||
html: '<div>Foo</div>',
|
||||
|
||||
test({ assert, component, target }) {
|
||||
component.tag = 'h1';
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<h1>Foo</h1>
|
||||
`
|
||||
);
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let tag = 'div';
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag}>Foo</svelte:element>
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
html: ''
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
let tag = '';
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag}>Foo</svelte:element>
|
@ -0,0 +1,21 @@
|
||||
let clicked = false;
|
||||
function handler() {
|
||||
clicked = true;
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
handler
|
||||
},
|
||||
html: '<button>Foo</button>',
|
||||
|
||||
test({ assert, target }) {
|
||||
assert.equal(clicked, false);
|
||||
|
||||
const button = target.querySelector('button');
|
||||
const click = new window.MouseEvent('click');
|
||||
button.dispatchEvent(click);
|
||||
|
||||
assert.equal(clicked, true);
|
||||
}
|
||||
};
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
const tag = "button";
|
||||
export let handler;
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag} on:click={handler}>Foo</svelte:element>
|
@ -0,0 +1,23 @@
|
||||
let clicked = false;
|
||||
function handler() {
|
||||
clicked = true;
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
tag: 'div',
|
||||
handler
|
||||
},
|
||||
html: '<div>Foo</div>',
|
||||
|
||||
test({ assert, component, target }) {
|
||||
assert.equal(clicked, false);
|
||||
|
||||
component.tag = 'button';
|
||||
const button = target.querySelector('button');
|
||||
const click = new window.MouseEvent('click');
|
||||
button.dispatchEvent(click);
|
||||
|
||||
assert.equal(clicked, true);
|
||||
}
|
||||
};
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
export let tag;
|
||||
export let handler;
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag} on:click={handler}>Foo</svelte:element>
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
html: '<div>Foo</div>'
|
||||
};
|
@ -0,0 +1 @@
|
||||
<svelte:element this={"div"}>Foo</svelte:element>
|
@ -0,0 +1,9 @@
|
||||
export default {
|
||||
compileOptions: {
|
||||
dev: true
|
||||
},
|
||||
props: {
|
||||
tag: 123
|
||||
},
|
||||
error: '<svelte:element> expects "this" attribute to be a string.'
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let tag;
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag} />
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
html: ''
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
let tag = null;
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag}>Foo</svelte:element>
|
@ -0,0 +1,16 @@
|
||||
let clicked = false;
|
||||
|
||||
export default {
|
||||
props: {
|
||||
tag: 'div',
|
||||
onClick: () => clicked = true
|
||||
},
|
||||
html: '<div style="display: inline;">Foo</div>',
|
||||
|
||||
async test({ assert, target, window }) {
|
||||
const div = target.querySelector('div');
|
||||
await div.dispatchEvent(new window.MouseEvent('click'));
|
||||
|
||||
assert.equal(clicked, true);
|
||||
}
|
||||
};
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
const tag = "div";
|
||||
export let onClick;
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag} style="display: inline;" on:click={onClick}>Foo</svelte:element>
|
@ -0,0 +1,7 @@
|
||||
<h1>Foo</h1>
|
||||
<div id="default">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div id="other">
|
||||
<slot name='other'></slot>
|
||||
</div>
|
@ -0,0 +1,29 @@
|
||||
export default {
|
||||
props: {
|
||||
x: true
|
||||
},
|
||||
|
||||
html: `
|
||||
<h1>Foo</h1>
|
||||
<div id="default">
|
||||
<h1>This is default slot</h1>
|
||||
</div>
|
||||
<div id="other">
|
||||
<h1 slot='other'>This is other slot</h1>
|
||||
</div>
|
||||
`,
|
||||
|
||||
test({ assert, component, target }) {
|
||||
component.tag = 'h2';
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<h1>Foo</h1>
|
||||
<div id="default">
|
||||
<h2>This is default slot</h2>
|
||||
</div>
|
||||
<div id="other">
|
||||
<h2 slot='other'>This is other slot</h2>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,10 @@
|
||||
<script>
|
||||
import Foo from './Foo.svelte';
|
||||
export let tag = "h1";
|
||||
</script>
|
||||
|
||||
<Foo>
|
||||
<svelte:element this={tag}>This is default slot</svelte:element>
|
||||
<svelte:element this={tag} slot='other'>This is other slot</svelte:element>
|
||||
</Foo>
|
||||
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
html: '<div></div>'
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
html: '<div>Foo</div>'
|
||||
};
|
@ -0,0 +1 @@
|
||||
<svelte:element this="div">Foo</svelte:element>
|
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
props: {
|
||||
size: 1
|
||||
},
|
||||
html: '<h1>This is h1 tag</h1>',
|
||||
|
||||
test({ assert, component, target }) {
|
||||
const h1 = target.firstChild;
|
||||
component.size = 2;
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<h2>This is h2 tag</h2>
|
||||
`
|
||||
);
|
||||
|
||||
const h2 = target.firstChild;
|
||||
assert.notEqual(h1, h2);
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let size;
|
||||
</script>
|
||||
|
||||
<svelte:element this="{`h${size}`}">This is h{size} tag</svelte:element>
|
@ -0,0 +1,17 @@
|
||||
export default {
|
||||
test({ assert, component, target, raf }) {
|
||||
component.visible = true;
|
||||
const h1 = target.querySelector('h1');
|
||||
assert.equal(h1.style.animation, '__svelte_3809512021_0 100ms linear 0ms 1 both');
|
||||
|
||||
raf.tick(150);
|
||||
component.tag = 'h2';
|
||||
const h2 = target.querySelector('h2');
|
||||
assert.equal(h1.style.animation, '');
|
||||
assert.equal(h2.style.animation, '');
|
||||
|
||||
raf.tick(50);
|
||||
component.visible = false;
|
||||
assert.equal(h2.style.animation, '__svelte_3750847757_0 100ms linear 0ms 1 both');
|
||||
}
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
<script>
|
||||
export let tag = "h1";
|
||||
export let visible;
|
||||
|
||||
function foo() {
|
||||
return {
|
||||
duration: 100,
|
||||
css: t => {
|
||||
return `opacity: ${t}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if visible}
|
||||
<svelte:element this={tag} transition:foo></svelte:element>
|
||||
{/if}
|
@ -0,0 +1,19 @@
|
||||
export default {
|
||||
html: '',
|
||||
test({ component, target, assert }) {
|
||||
component.tag = 'h1';
|
||||
assert.htmlEqual(target.innerHTML, '<h1>Foo</h1>');
|
||||
|
||||
component.tag = null;
|
||||
assert.htmlEqual(target.innerHTML, '');
|
||||
|
||||
component.tag = 'div';
|
||||
assert.htmlEqual(target.innerHTML, '<div>Foo</div>');
|
||||
|
||||
component.tag = false;
|
||||
assert.htmlEqual(target.innerHTML, '');
|
||||
|
||||
component.tag = 'span';
|
||||
assert.htmlEqual(target.innerHTML, '<span>Foo</span>');
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let tag;
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag}>Foo</svelte:element>
|
@ -0,0 +1,20 @@
|
||||
export default {
|
||||
props: {
|
||||
tag: 'div',
|
||||
text: 'Foo'
|
||||
},
|
||||
html: '<div>Foo</div>',
|
||||
|
||||
test({ assert, component, target }) {
|
||||
const div = target.firstChild;
|
||||
component.tag = 'nav';
|
||||
component.text = 'Bar';
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<nav>Bar</nav>
|
||||
`);
|
||||
|
||||
const h1 = target.firstChild;
|
||||
assert.notEqual(div, h1);
|
||||
}
|
||||
};
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
export let tag = "div";
|
||||
export let text = "Foo";
|
||||
</script>
|
||||
|
||||
<svelte:element this={tag}>{text}</svelte:element>
|
@ -0,0 +1 @@
|
||||
<div>Foo</div>
|
@ -0,0 +1 @@
|
||||
<svelte:element this="div">Foo</svelte:element>
|
@ -0,0 +1,2 @@
|
||||
<h1>Foo</h1>
|
||||
<div>Bar</div>
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let heading = 'h1';
|
||||
let tag = 'div';
|
||||
</script>
|
||||
|
||||
<svelte:element this={heading}>Foo</svelte:element>
|
||||
<svelte:element this={tag}>Bar</svelte:element>
|
@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"message": "Invalid element definition",
|
||||
"code": "invalid-element-definition",
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 17,
|
||||
"character": 23
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 17,
|
||||
"character": 23
|
||||
},
|
||||
"pos": 23
|
||||
}
|
||||
]
|
@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<svelte:element this>foo</svelte:element>
|
||||
</div>
|
@ -0,0 +1,15 @@
|
||||
[{
|
||||
"code": "missing-element-definition",
|
||||
"message": "<svelte:element> must have a 'this' attribute",
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 1,
|
||||
"character": 7
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 1,
|
||||
"character": 7
|
||||
},
|
||||
"pos": 7
|
||||
}]
|
@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<svelte:element>foo</svelte:element>
|
||||
</div>
|
@ -0,0 +1,15 @@
|
||||
[{
|
||||
"code": "unexpected-reserved-word",
|
||||
"message": "'this' is a reserved word in JavaScript and cannot be used here",
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 18,
|
||||
"character": 24
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 18,
|
||||
"character": 24
|
||||
},
|
||||
"pos": 24
|
||||
}]
|
@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<svelte:element {this}>foo</svelte:element>
|
||||
</div>
|
Loading…
Reference in new issue