mirror of https://github.com/sveltejs/svelte
commit
15fa8ad0cc
@ -0,0 +1,10 @@
|
|||||||
|
<script>
|
||||||
|
let selection = '';
|
||||||
|
|
||||||
|
const handleSelectionChange = (e) => selection = document.getSelection();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:document on:selectionchange={handleSelectionChange} />
|
||||||
|
|
||||||
|
<p>Select this text to fire events</p>
|
||||||
|
<p>Selection: {selection}</p>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"title": "<svelte:document>"
|
||||||
|
}
|
||||||
@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
title: <svelte:body>
|
|
||||||
---
|
|
||||||
|
|
||||||
Similar to `<svelte:window>`, the `<svelte:body>` element allows you to listen for events that fire on `document.body`. This is useful with the `mouseenter` and `mouseleave` events, which don't fire on `window`.
|
|
||||||
|
|
||||||
Add the `mouseenter` and `mouseleave` handlers to the `<svelte:body>` tag:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<svelte:body
|
|
||||||
on:mouseenter={handleMouseenter}
|
|
||||||
on:mouseleave={handleMouseleave}
|
|
||||||
/>
|
|
||||||
```
|
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
<script>
|
||||||
|
let selection = '';
|
||||||
|
|
||||||
|
const handleSelectionChange = (e) => selection = document.getSelection();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:body />
|
||||||
|
|
||||||
|
<p>Select this text to fire events</p>
|
||||||
|
<p>Selection: {selection}</p>
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
<script>
|
||||||
|
let selection = '';
|
||||||
|
|
||||||
|
const handleSelectionChange = (e) => selection = document.getSelection();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:document on:selectionchange={handleSelectionChange} />
|
||||||
|
|
||||||
|
<p>Select this text to fire events</p>
|
||||||
|
<p>Selection: {selection}</p>
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: <svelte:document>
|
||||||
|
---
|
||||||
|
|
||||||
|
Similar to `<svelte:window>`, the `<svelte:document>` element allows you to listen for events that fire on `document`. This is useful with events like `selectionchange`, which doesn't fire on `window`.
|
||||||
|
|
||||||
|
Add the `selectionchange` handler to the `<svelte:document>` tag:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svelte:document on:selectionchange={handleSelectionChange} />
|
||||||
|
```
|
||||||
|
|
||||||
|
> Avoid `mouseenter` and `mouseleave` handlers on this element, these events are not fired on `document` in all browsers. Use `<svelte:body>` for this instead.
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: <svelte:body>
|
||||||
|
---
|
||||||
|
|
||||||
|
Similar to `<svelte:window>` and `<svelte:document>`, the `<svelte:body>` element allows you to listen for events that fire on `document.body`. This is useful with the `mouseenter` and `mouseleave` events, which don't fire on `window`.
|
||||||
|
|
||||||
|
Add the `mouseenter` and `mouseleave` handlers to the `<svelte:body>` tag:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svelte:body
|
||||||
|
on:mouseenter={handleMouseenter}
|
||||||
|
on:mouseleave={handleMouseleave}
|
||||||
|
/>
|
||||||
|
```
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
import Node from './shared/Node';
|
||||||
|
import EventHandler from './EventHandler';
|
||||||
|
import Action from './Action';
|
||||||
|
import Component from '../Component';
|
||||||
|
import TemplateScope from './shared/TemplateScope';
|
||||||
|
import { Element } from '../../interfaces';
|
||||||
|
import compiler_warnings from '../compiler_warnings';
|
||||||
|
|
||||||
|
export default class Document extends Node {
|
||||||
|
type: 'Document';
|
||||||
|
handlers: EventHandler[] = [];
|
||||||
|
actions: Action[] = [];
|
||||||
|
|
||||||
|
constructor(component: Component, parent: Node, scope: TemplateScope, info: Element) {
|
||||||
|
super(component, parent, scope, info);
|
||||||
|
|
||||||
|
info.attributes.forEach((node) => {
|
||||||
|
if (node.type === 'EventHandler') {
|
||||||
|
this.handlers.push(new EventHandler(component, this, scope, node));
|
||||||
|
} else if (node.type === 'Action') {
|
||||||
|
this.actions.push(new Action(component, this, scope, node));
|
||||||
|
} else {
|
||||||
|
// TODO there shouldn't be anything else here...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private validate() {
|
||||||
|
const handlers_map = new Set();
|
||||||
|
|
||||||
|
this.handlers.forEach(handler => (
|
||||||
|
handlers_map.add(handler.name)
|
||||||
|
));
|
||||||
|
|
||||||
|
if (handlers_map.has('mouseenter') || handlers_map.has('mouseleave')) {
|
||||||
|
this.component.warn(this, compiler_warnings.avoid_mouse_events_on_document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import Block from '../Block';
|
||||||
|
import Wrapper from './shared/Wrapper';
|
||||||
|
import { x } from 'code-red';
|
||||||
|
import Document from '../../nodes/Document';
|
||||||
|
import { Identifier } from 'estree';
|
||||||
|
import EventHandler from './Element/EventHandler';
|
||||||
|
import add_event_handlers from './shared/add_event_handlers';
|
||||||
|
import { TemplateNode } from '../../../interfaces';
|
||||||
|
import Renderer from '../Renderer';
|
||||||
|
import add_actions from './shared/add_actions';
|
||||||
|
|
||||||
|
export default class DocumentWrapper extends Wrapper {
|
||||||
|
node: Document;
|
||||||
|
handlers: EventHandler[];
|
||||||
|
|
||||||
|
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) {
|
||||||
|
super(renderer, block, parent, node);
|
||||||
|
this.handlers = this.node.handlers.map(handler => new EventHandler(handler, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
|
||||||
|
add_event_handlers(block, x`@_document`, this.handlers);
|
||||||
|
add_actions(block, x`@_document`, this.node.actions);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
// Utilities for managing contenteditable nodes
|
||||||
|
import Attribute from '../nodes/Attribute';
|
||||||
|
import Element from '../nodes/Element';
|
||||||
|
|
||||||
|
export const CONTENTEDITABLE_BINDINGS = [
|
||||||
|
'textContent',
|
||||||
|
'innerHTML',
|
||||||
|
'innerText'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if node is an 'input' or 'textarea'.
|
||||||
|
* @param {Element} node The element to be checked
|
||||||
|
*/
|
||||||
|
function is_input_or_textarea(node: Element): boolean {
|
||||||
|
return node.name === 'textarea' || node.name === 'input';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given attribute is 'contenteditable'.
|
||||||
|
* @param {Attribute} attribute A node.attribute
|
||||||
|
*/
|
||||||
|
function is_attr_contenteditable(attribute: Attribute): boolean {
|
||||||
|
return attribute.name === 'contenteditable';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if any of a node's attributes are 'contentenditable'.
|
||||||
|
* @param {Element} node The element to be checked
|
||||||
|
*/
|
||||||
|
export function has_contenteditable_attr(node: Element): boolean {
|
||||||
|
return node.attributes.some(is_attr_contenteditable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if node is not textarea or input, but has 'contenteditable' attribute.
|
||||||
|
* @param {Element} node The element to be tested
|
||||||
|
*/
|
||||||
|
export function is_contenteditable(node: Element): boolean {
|
||||||
|
return !is_input_or_textarea(node) && has_contenteditable_attr(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a given binding/node is contenteditable.
|
||||||
|
* @param {string} name A binding or node name to be checked
|
||||||
|
*/
|
||||||
|
export function is_name_contenteditable(name: string): boolean {
|
||||||
|
return CONTENTEDITABLE_BINDINGS.includes(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the contenteditable attribute from the node (if it exists).
|
||||||
|
* @param {Element} node The element to get the attribute from
|
||||||
|
*/
|
||||||
|
export function get_contenteditable_attr(node: Element): Attribute | undefined {
|
||||||
|
return node.attributes.find(is_attr_contenteditable);
|
||||||
|
}
|
||||||
@ -1,60 +0,0 @@
|
|||||||
/* generated by Svelte vX.Y.Z */
|
|
||||||
import {
|
|
||||||
SvelteComponent,
|
|
||||||
detach,
|
|
||||||
element,
|
|
||||||
init,
|
|
||||||
insert,
|
|
||||||
noop,
|
|
||||||
safe_not_equal,
|
|
||||||
set_data,
|
|
||||||
space,
|
|
||||||
text
|
|
||||||
} from "svelte/internal";
|
|
||||||
|
|
||||||
function create_fragment(ctx) {
|
|
||||||
let h1;
|
|
||||||
let t3;
|
|
||||||
let t4;
|
|
||||||
|
|
||||||
return {
|
|
||||||
c() {
|
|
||||||
h1 = element("h1");
|
|
||||||
h1.textContent = `Hello ${name}!`;
|
|
||||||
t3 = space();
|
|
||||||
t4 = text(/*foo*/ ctx[0]);
|
|
||||||
},
|
|
||||||
m(target, anchor) {
|
|
||||||
insert(target, h1, anchor);
|
|
||||||
insert(target, t3, anchor);
|
|
||||||
insert(target, t4, anchor);
|
|
||||||
},
|
|
||||||
p(ctx, [dirty]) {
|
|
||||||
if (dirty & /*foo*/ 1) set_data(t4, /*foo*/ ctx[0]);
|
|
||||||
},
|
|
||||||
i: noop,
|
|
||||||
o: noop,
|
|
||||||
d(detaching) {
|
|
||||||
if (detaching) detach(h1);
|
|
||||||
if (detaching) detach(t3);
|
|
||||||
if (detaching) detach(t4);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = 'world';
|
|
||||||
|
|
||||||
function instance($$self) {
|
|
||||||
let foo;
|
|
||||||
$: foo = name + name;
|
|
||||||
return [foo];
|
|
||||||
}
|
|
||||||
|
|
||||||
class Component extends SvelteComponent {
|
|
||||||
constructor(options) {
|
|
||||||
super();
|
|
||||||
init(this, options, instance, create_fragment, safe_not_equal, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Component;
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
<script>
|
|
||||||
let name = 'world';
|
|
||||||
$: foo = name + name;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h1>Hello {name}!</h1>
|
|
||||||
{foo}
|
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
export default {
|
||||||
|
html: `
|
||||||
|
<p style="font-size: 32px; color: red; background-color: green; border-color: green;"></p>
|
||||||
|
`,
|
||||||
|
|
||||||
|
test({ assert, target, window, component }) {
|
||||||
|
const p = target.querySelector('p');
|
||||||
|
const styles = window.getComputedStyle(p);
|
||||||
|
assert.equal(styles.color, 'rgb(255, 0, 0)');
|
||||||
|
assert.equal(styles.fontSize, '32px');
|
||||||
|
assert.equal(styles.backgroundColor, 'rgb(0, 128, 0)');
|
||||||
|
assert.equal(styles.borderColor, 'rgb(0, 128, 0)');
|
||||||
|
|
||||||
|
component.foo = 'font-size: 50px; color: green;'; // Update style attribute
|
||||||
|
{
|
||||||
|
const p = target.querySelector('p');
|
||||||
|
const styles = window.getComputedStyle(p);
|
||||||
|
assert.equal(styles.color, 'rgb(255, 0, 0)');
|
||||||
|
assert.equal(styles.fontSize, '32px');
|
||||||
|
assert.equal(styles.backgroundColor, 'rgb(0, 128, 0)');
|
||||||
|
assert.equal(styles.borderColor, 'rgb(0, 128, 0)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<script>
|
||||||
|
export let foo = "font-size: 20px; color: blue;";
|
||||||
|
let baz = "red"; // static value
|
||||||
|
let bar = "32"; // static value interpolated
|
||||||
|
export let bg = "gre"; // dynamic value interpolated/cached
|
||||||
|
export let borderColor = "green"; // dynamic value non-cached
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p
|
||||||
|
style:font-size="{bar}px"
|
||||||
|
style:color={baz}
|
||||||
|
style="{foo}"
|
||||||
|
style:background-color="{bg}en"
|
||||||
|
style:border-color={borderColor}
|
||||||
|
/>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
export default {
|
||||||
|
html: '<div></div>',
|
||||||
|
|
||||||
|
async test({ assert, target, window }) {
|
||||||
|
const visibility = new window.Event('visibilitychange');
|
||||||
|
|
||||||
|
await window.document.dispatchEvent(visibility);
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<div>
|
||||||
|
<div class="tooltip">Perform an Action</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<script>
|
||||||
|
let container;
|
||||||
|
function tooltip(node, text) {
|
||||||
|
let tooltip = null;
|
||||||
|
|
||||||
|
function onVisibilityChange() {
|
||||||
|
tooltip = document.createElement('div');
|
||||||
|
tooltip.classList.add('tooltip');
|
||||||
|
tooltip.textContent = text;
|
||||||
|
container.appendChild(tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.addEventListener('visibilitychange', onVisibilityChange);
|
||||||
|
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
node.removeEventListener('visibilitychange', onVisibilityChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:document use:tooltip="{'Perform an Action'}" />
|
||||||
|
<div bind:this={container} />
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
export default {
|
||||||
|
async test({ assert, component, target }) {
|
||||||
|
await Promise.resolve();
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<p>propA: 3</p>
|
||||||
|
<p>propB: 7</p>
|
||||||
|
<p>num: 3</p>
|
||||||
|
<p>rest: {"prop3":{"prop9":9,"prop10":10}}</p>
|
||||||
|
<p>propZ: 5</p>
|
||||||
|
<p>propY: 6</p>
|
||||||
|
<p>rest: {"propX":7,"propW":8}</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
await (component.object = Promise.resolve({ prop1: 'one', prop2: 'two', prop3: { prop7: 'seven' }, prop4: { prop10: 'ten' }}));
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<p>propA: seven</p>
|
||||||
|
<p>propB: ten</p>
|
||||||
|
<p>num: 5</p>
|
||||||
|
<p>rest: {"prop1":"one","prop2":"two"}</p>
|
||||||
|
<p>propZ: 5</p>
|
||||||
|
<p>propY: 6</p>
|
||||||
|
<p>rest: {"propX":7,"propW":8}</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<script>
|
||||||
|
export let object = Promise.resolve({ prop1: { prop4: 2, prop5: 3 }, prop2: { prop6: 5, prop7: 6, prop8: 7 }, prop3: { prop9: 9, prop10: 10 } });
|
||||||
|
const objectReject = Promise.reject({ propZ: 5, propY: 6, propX: 7, propW: 8 });
|
||||||
|
|
||||||
|
let num = 1;
|
||||||
|
const prop = 'prop';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await object then { [`prop${num++}`]: { [`prop${num + 3}`]: propA }, [`prop${num++}`]: { [`prop${num + 5}`]: propB }, ...rest }}
|
||||||
|
<p>propA: {propA}</p>
|
||||||
|
<p>propB: {propB}</p>
|
||||||
|
<p>num: {num}</p>
|
||||||
|
<p>rest: {JSON.stringify(rest)}</p>
|
||||||
|
{/await}
|
||||||
|
|
||||||
|
{#await objectReject then value}
|
||||||
|
resolved
|
||||||
|
{:catch { [`${prop}Z`]: propZ, [`${prop}Y`]: propY, ...rest }}
|
||||||
|
<p>propZ: {propZ}</p>
|
||||||
|
<p>propY: {propY}</p>
|
||||||
|
<p>rest: {JSON.stringify(rest)}</p>
|
||||||
|
{/await}
|
||||||
|
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
thePromise: new Promise(_ => {})
|
||||||
|
},
|
||||||
|
|
||||||
|
html: `
|
||||||
|
loading...
|
||||||
|
`,
|
||||||
|
|
||||||
|
async test({ assert, component, target }) {
|
||||||
|
await (component.thePromise = Promise.resolve([10, 11, 12, 13, 14, 15]));
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<p>[1] 11</p>
|
||||||
|
<p>[3] 13</p>
|
||||||
|
<p>[4] 14</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
await (component.thePromise = Promise.resolve({ 1: 21, 3: 23, 4: 24 }));
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<p>[1] 21</p>
|
||||||
|
<p>[3] 23</p>
|
||||||
|
<p>[4] 24</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await (component.thePromise = Promise.reject([30, 31, 32, 33, 34, 35]));
|
||||||
|
} catch (e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<p>[0] 30</p>
|
||||||
|
<p>[2] 32</p>
|
||||||
|
<p>[5] 35</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await (component.thePromise = Promise.reject({ 0: 40, 2: 42, 5: 45 }));
|
||||||
|
} catch (e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<p>[0] 40</p>
|
||||||
|
<p>[2] 42</p>
|
||||||
|
<p>[5] 45</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<script>
|
||||||
|
export let thePromise;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await thePromise}
|
||||||
|
loading...
|
||||||
|
{:then { 1: a, 3: b, 4: c }}
|
||||||
|
<p>[1] {a}</p>
|
||||||
|
<p>[3] {b}</p>
|
||||||
|
<p>[4] {c}</p>
|
||||||
|
{:catch { 0: d, 2: e, 5: f }}
|
||||||
|
<p>[0] {d}</p>
|
||||||
|
<p>[2] {e}</p>
|
||||||
|
<p>[5] {f}</p>
|
||||||
|
{/await}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
export default {
|
||||||
|
async test({ assert, target }) {
|
||||||
|
await Promise.resolve();
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<p>prop-1: 1</p>
|
||||||
|
<p>prop4: 4</p>
|
||||||
|
<p>rest: {"prop2":2,"prop-3":3}</p>
|
||||||
|
<p>prop-7: 7</p>
|
||||||
|
<p>prop6: 6</p>
|
||||||
|
<p>rest: {"prop-5":5,"prop8":8}</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<script>
|
||||||
|
const object = Promise.resolve({ 'prop-1': 1, 'prop2': 2, 'prop-3': 3, 'prop4': 4 });
|
||||||
|
const objectReject = Promise.reject({ 'prop-5': 5, 'prop6': 6, 'prop-7': 7, 'prop8': 8 });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await object then { 'prop-1': prop1, 'prop4': fourthProp, ...rest }}
|
||||||
|
<p>prop-1: {prop1}</p>
|
||||||
|
<p>prop4: {fourthProp}</p>
|
||||||
|
<p>rest: {JSON.stringify(rest)}</p>
|
||||||
|
{/await}
|
||||||
|
|
||||||
|
{#await objectReject then value}
|
||||||
|
resolved
|
||||||
|
{:catch { 'prop-7': prop7, 'prop6': sixthProp, ...rest }}
|
||||||
|
<p>prop-7: {prop7}</p>
|
||||||
|
<p>prop6: {sixthProp}</p>
|
||||||
|
<p>rest: {JSON.stringify(rest)}</p>
|
||||||
|
{/await}
|
||||||
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
name: 'world'
|
||||||
|
},
|
||||||
|
|
||||||
|
ssrHtml: `
|
||||||
|
<editor contenteditable="true">world</editor>
|
||||||
|
<p>hello world</p>
|
||||||
|
`,
|
||||||
|
|
||||||
|
async test({ assert, component, target, window }) {
|
||||||
|
// JSDom doesn't support innerText yet, so the test is not ideal
|
||||||
|
// https://github.com/jsdom/jsdom/issues/1245
|
||||||
|
const el = target.querySelector('editor');
|
||||||
|
assert.equal(el.innerText, 'world');
|
||||||
|
|
||||||
|
const event = new window.Event('input');
|
||||||
|
el.innerText = 'everybody';
|
||||||
|
await el.dispatchEvent(event);
|
||||||
|
assert.equal(component.name, 'everybody');
|
||||||
|
|
||||||
|
component.name = 'goodbye';
|
||||||
|
assert.equal(el.innerText, 'goodbye');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<script>
|
||||||
|
export let name;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<editor contenteditable="true" bind:innerText={name} />
|
||||||
|
<p>hello {name}</p>
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
export default {
|
||||||
|
async test({ assert, target, component, window }) {
|
||||||
|
const button = target.querySelector('button');
|
||||||
|
const clickEvent = new window.Event('click');
|
||||||
|
const changeEvent = new window.Event('change');
|
||||||
|
|
||||||
|
const [input1, input2] = target.querySelectorAll('input[type="checkbox"]');
|
||||||
|
function validate_inputs(v1, v2) {
|
||||||
|
assert.equal(input1.checked, v1);
|
||||||
|
assert.equal(input2.checked, v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.deepEqual(component.test, []);
|
||||||
|
validate_inputs(false, false);
|
||||||
|
|
||||||
|
component.test = ['a', 'b'];
|
||||||
|
validate_inputs(true, true);
|
||||||
|
|
||||||
|
input1.checked = false;
|
||||||
|
await input1.dispatchEvent(changeEvent);
|
||||||
|
assert.deepEqual(component.test, ['b']);
|
||||||
|
|
||||||
|
input2.checked = false;
|
||||||
|
await input2.dispatchEvent(changeEvent);
|
||||||
|
assert.deepEqual(component.test, []);
|
||||||
|
|
||||||
|
input1.checked = true;
|
||||||
|
input2.checked = true;
|
||||||
|
await input1.dispatchEvent(changeEvent);
|
||||||
|
await input2.dispatchEvent(changeEvent);
|
||||||
|
assert.deepEqual(component.test, ['b', 'a']);
|
||||||
|
|
||||||
|
await button.dispatchEvent(clickEvent);
|
||||||
|
assert.deepEqual(component.test, ['b', 'a']); // should it be ['a'] only? valid arguments for both outcomes
|
||||||
|
|
||||||
|
input1.checked = false;
|
||||||
|
await input1.dispatchEvent(changeEvent);
|
||||||
|
assert.deepEqual(component.test, []);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<script>
|
||||||
|
export let test = [];
|
||||||
|
let hidden = false
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button on:click={() => hidden = !hidden}>
|
||||||
|
{hidden ? "show" : "hide"} b
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<label>a <input type="checkbox" bind:group={test} value="a" /></label>
|
||||||
|
{#if !hidden}
|
||||||
|
<label>b <input type="checkbox" bind:group={test} value="b" /></label>
|
||||||
|
{/if}
|
||||||
|
<label>c <input value="just here, so b is not the last input" /></label>
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
export default {
|
||||||
|
async test({ assert, target, component, window }) {
|
||||||
|
const button = target.querySelector('button');
|
||||||
|
const clickEvent = new window.Event('click');
|
||||||
|
const changeEvent = new window.Event('change');
|
||||||
|
|
||||||
|
const [input1, input2] = target.querySelectorAll('input[type="radio"]');
|
||||||
|
function validate_inputs(v1, v2) {
|
||||||
|
assert.equal(input1.checked, v1);
|
||||||
|
assert.equal(input2.checked, v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
component.test = 'a';
|
||||||
|
validate_inputs(true, false);
|
||||||
|
|
||||||
|
component.test = 'b';
|
||||||
|
validate_inputs(false, true);
|
||||||
|
|
||||||
|
input1.checked = true;
|
||||||
|
await input1.dispatchEvent(changeEvent);
|
||||||
|
assert.deepEqual(component.test, 'a');
|
||||||
|
|
||||||
|
input2.checked = true;
|
||||||
|
await input2.dispatchEvent(changeEvent);
|
||||||
|
assert.deepEqual(component.test, 'b');
|
||||||
|
|
||||||
|
await button.dispatchEvent(clickEvent);
|
||||||
|
assert.deepEqual(component.test, 'b'); // should it be undefined? valid arguments for both outcomes
|
||||||
|
|
||||||
|
input1.checked = true;
|
||||||
|
await input1.dispatchEvent(changeEvent);
|
||||||
|
assert.deepEqual(component.test, 'a');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<script>
|
||||||
|
export let test;
|
||||||
|
let hidden = false
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button on:click={() => hidden = !hidden}>
|
||||||
|
{hidden ? "show" : "hide"} b
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<label>a <input type="radio" bind:group={test} value="a" /></label>
|
||||||
|
{#if !hidden}
|
||||||
|
<label>b <input type="radio" bind:group={test} value="b" /></label>
|
||||||
|
{/if}
|
||||||
|
<label>c <input value="just here, so b is not the last input" /></label>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
export default {
|
||||||
|
html: `
|
||||||
|
<p>4, 12, 60</p>
|
||||||
|
`,
|
||||||
|
|
||||||
|
async test({ component, target, assert }) {
|
||||||
|
component.permutation = [2, 3, 1];
|
||||||
|
await (component.promise1 = Promise.resolve({length: 1, width: 2, height: 3}));
|
||||||
|
try {
|
||||||
|
await (component.promise2 = Promise.reject({length: 97, width: 98, height: 99}));
|
||||||
|
} catch (e) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<p>2, 11, 2</p>
|
||||||
|
<p>9506, 28811, 98</p>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<script>
|
||||||
|
export let promise1 = {length: 5, width: 3, height: 4};
|
||||||
|
export let promise2 = {length: 12, width: 5, height: 13};
|
||||||
|
export let permutation = [1, 2, 3];
|
||||||
|
|
||||||
|
function calculate(length, width, height) {
|
||||||
|
return {
|
||||||
|
'1-Dimensions': [length, width, height],
|
||||||
|
'2-Dimensions': [length * width, width * height, length * height],
|
||||||
|
'3-Dimensions': [length * width * height, length + width + height, length * width + width * height + length * height]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await promise1 then { length, width, height }}
|
||||||
|
{@const [a, b, c] = permutation}
|
||||||
|
{@const { [`${a}-Dimensions`]: { [c - 1]: first }, [`${b}-Dimensions`]: { [b - 1]: second }, [`${c}-Dimensions`]: { [a - 1]: third } } = calculate(length, width, height) }
|
||||||
|
<p>{first}, {second}, {third}</p>
|
||||||
|
{/await}
|
||||||
|
|
||||||
|
{#await promise2 catch { length, width, height }}
|
||||||
|
{@const [a, b, c] = permutation}
|
||||||
|
{@const { [`${a}-Dimensions`]: { [c - 1]: first }, [`${b}-Dimensions`]: { [b - 1]: second }, [`${c}-Dimensions`]: { [a - 1]: third } } = calculate(length, width, height) }
|
||||||
|
<p>{first}, {second}, {third}</p>
|
||||||
|
{/await}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
export default {
|
||||||
|
html: '<div>12 120 70, 30+4=34</div>',
|
||||||
|
async test({ component, target, assert }) {
|
||||||
|
component.promise1 = Promise.resolve({width: 5, height: 6});
|
||||||
|
component.promise2 = Promise.reject({width: 6, height: 7});
|
||||||
|
|
||||||
|
await Promise.resolve();
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<div>30 300 110, 50+6=56</div>
|
||||||
|
<div>42 420 130, 60+7=67</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
component.constant = 20;
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<div>30 600 220, 100+6=106</div>
|
||||||
|
<div>42 840 260, 120+7=127</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<script>
|
||||||
|
export let promise1 = {width: 3, height: 4};
|
||||||
|
export let promise2 = {width: 5, height: 7};
|
||||||
|
export let constant = 10;
|
||||||
|
|
||||||
|
function calculate(width, height, constant) {
|
||||||
|
return { 'the-area': width * height, 'the-volume': width * height * constant };
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await promise1 then { width, height }}
|
||||||
|
{@const {'the-area': area, 'the-volume': volume} = calculate(width, height, constant)}
|
||||||
|
{@const perimeter = (width + height) * constant}
|
||||||
|
{@const { 0: _width, 1: _height, 2: sum } = [width * constant, height, width * constant + height]}
|
||||||
|
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
|
||||||
|
{/await}
|
||||||
|
|
||||||
|
{#await promise2 catch { width, height }}
|
||||||
|
{@const {'the-area': area, 'the-volume': volume} = calculate(width, height, constant)}
|
||||||
|
{@const perimeter = (width + height) * constant}
|
||||||
|
{@const { 0: _width, 1: _height, 2: sum } = [width * constant, height, width * constant + height]}
|
||||||
|
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
|
||||||
|
{/await}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
export default {
|
||||||
|
html: `
|
||||||
|
<button>6, 12, 8, 24</button>
|
||||||
|
<button>45, 35, 63, 315</button>
|
||||||
|
<button>60, 48, 80, 480</button>
|
||||||
|
`,
|
||||||
|
|
||||||
|
async test({ component, target, assert }) {
|
||||||
|
component.boxes = [{ length: 10, width: 20, height: 30 }];
|
||||||
|
|
||||||
|
assert.htmlEqual(target.innerHTML,
|
||||||
|
'<button>200, 600, 300, 6000</button>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
<script>
|
||||||
|
export let boxes = [
|
||||||
|
{length: 2, width: 3, height: 4},
|
||||||
|
{length: 9, width: 5, height: 7},
|
||||||
|
{length: 10, width: 6, height: 8}
|
||||||
|
];
|
||||||
|
|
||||||
|
function calculate(length, width, height) {
|
||||||
|
return {
|
||||||
|
twoDimensions: {
|
||||||
|
bottomArea: length * width,
|
||||||
|
sideArea1: width * height,
|
||||||
|
sideArea2: length * height
|
||||||
|
},
|
||||||
|
threeDimensions: {
|
||||||
|
volume: length * width * height
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export let dimension = 'Dimensions';
|
||||||
|
function changeDimension() {
|
||||||
|
dimension = 'DIMENSIONS';
|
||||||
|
}
|
||||||
|
|
||||||
|
let area = 'Area';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each boxes as { length, width, height }}
|
||||||
|
{@const {
|
||||||
|
[`two${dimension}`]: {
|
||||||
|
i = 1,
|
||||||
|
[`bottom${area}`]: bottom,
|
||||||
|
[`side${area}${i++}`]: sideone,
|
||||||
|
[`side${area}${i++}`]: sidetwo
|
||||||
|
},
|
||||||
|
[`three${dimension}`]: {
|
||||||
|
volume
|
||||||
|
}
|
||||||
|
} = calculate(length, width, height)}
|
||||||
|
<button on:click={changeDimension}>{bottom}, {sideone}, {sidetwo}, {volume}</button>
|
||||||
|
{/each}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue