mirror of https://github.com/sveltejs/svelte
commit
b45fe8020b
@ -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);
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/* generated by Svelte vX.Y.Z */
|
||||
import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
|
||||
|
||||
function instance($$self) {
|
||||
class A {
|
||||
p1;
|
||||
p2 = 1;
|
||||
#p3 = 2;
|
||||
|
||||
#getP3() {
|
||||
return this.#p3;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
class Component extends SvelteComponent {
|
||||
constructor(options) {
|
||||
super();
|
||||
init(this, options, instance, null, safe_not_equal, {});
|
||||
}
|
||||
}
|
||||
|
||||
export default Component;
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
class A {
|
||||
p1;
|
||||
p2 = 1;
|
||||
|
||||
#p3 = 2;
|
||||
|
||||
#getP3() {
|
||||
return this.#p3;
|
||||
}
|
||||
}
|
||||
</script>
|
@ -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,25 @@
|
||||
export default {
|
||||
skip_if_ssr: true, // TODO would be nice to fix this in SSR as well
|
||||
|
||||
html: `
|
||||
<p>selected: b</p>
|
||||
|
||||
<select>
|
||||
<option value='a'>a</option>
|
||||
<option value='b'>b</option>
|
||||
<option value='c'>c</option>
|
||||
</select>
|
||||
|
||||
<p>selected: b</p>
|
||||
`,
|
||||
|
||||
test({ assert, component, target }) {
|
||||
assert.equal(component.selected, 'b');
|
||||
const select = target.querySelector('select');
|
||||
const options = [...target.querySelectorAll('option')];
|
||||
|
||||
// option with selected attribute should be selected
|
||||
assert.equal(select.value, 'b');
|
||||
assert.ok(options[1].selected);
|
||||
}
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
export let selected;
|
||||
</script>
|
||||
|
||||
<p>selected: {selected}</p>
|
||||
|
||||
<select bind:value={selected}>
|
||||
<option>a</option>
|
||||
<option selected>b</option>
|
||||
<option>c</option>
|
||||
</select>
|
||||
|
||||
<p>selected: {selected}</p>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue