ripple 0.2.115 → 0.2.118
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +16 -16
- package/src/compiler/index.js +20 -1
- package/src/compiler/phases/1-parse/index.js +79 -0
- package/src/compiler/phases/3-transform/client/index.js +54 -8
- package/src/compiler/phases/3-transform/segments.js +107 -60
- package/src/compiler/phases/3-transform/server/index.js +21 -11
- package/src/compiler/types/index.d.ts +16 -0
- package/src/runtime/index-client.js +19 -185
- package/src/runtime/index-server.js +24 -0
- package/src/runtime/internal/client/bindings.js +443 -0
- package/src/runtime/internal/client/index.js +4 -0
- package/src/runtime/internal/client/runtime.js +10 -0
- package/src/runtime/internal/client/utils.js +0 -8
- package/src/runtime/map.js +11 -1
- package/src/runtime/set.js +11 -1
- package/tests/client/__snapshots__/for.test.ripple.snap +80 -0
- package/tests/client/_etc.test.ripple +5 -0
- package/tests/client/array/array.copy-within.test.ripple +120 -0
- package/tests/client/array/array.derived.test.ripple +495 -0
- package/tests/client/array/array.iteration.test.ripple +115 -0
- package/tests/client/array/array.mutations.test.ripple +385 -0
- package/tests/client/array/array.static.test.ripple +237 -0
- package/tests/client/array/array.to-methods.test.ripple +93 -0
- package/tests/client/basic/__snapshots__/basic.attributes.test.ripple.snap +60 -0
- package/tests/client/basic/__snapshots__/basic.rendering.test.ripple.snap +106 -0
- package/tests/client/basic/__snapshots__/basic.text.test.ripple.snap +49 -0
- package/tests/client/basic/basic.attributes.test.ripple +474 -0
- package/tests/client/basic/basic.collections.test.ripple +94 -0
- package/tests/client/basic/basic.components.test.ripple +225 -0
- package/tests/client/basic/basic.errors.test.ripple +126 -0
- package/tests/client/basic/basic.events.test.ripple +222 -0
- package/tests/client/basic/basic.reactivity.test.ripple +476 -0
- package/tests/client/basic/basic.rendering.test.ripple +204 -0
- package/tests/client/basic/basic.styling.test.ripple +63 -0
- package/tests/client/basic/basic.utilities.test.ripple +25 -0
- package/tests/client/boundaries.test.ripple +2 -21
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.ripple.snap +12 -0
- package/tests/client/compiler/__snapshots__/compiler.typescript.test.ripple.snap +22 -0
- package/tests/client/compiler/compiler.assignments.test.ripple +112 -0
- package/tests/client/compiler/compiler.attributes.test.ripple +95 -0
- package/tests/client/compiler/compiler.basic.test.ripple +203 -0
- package/tests/client/compiler/compiler.regex.test.ripple +87 -0
- package/tests/client/compiler/compiler.typescript.test.ripple +29 -0
- package/tests/client/{__snapshots__/composite.test.ripple.snap → composite/__snapshots__/composite.render.test.ripple.snap} +2 -2
- package/tests/client/composite/composite.dynamic-components.test.ripple +100 -0
- package/tests/client/composite/composite.generics.test.ripple +211 -0
- package/tests/client/composite/composite.props.test.ripple +106 -0
- package/tests/client/composite/composite.reactivity.test.ripple +184 -0
- package/tests/client/composite/composite.render.test.ripple +84 -0
- package/tests/client/computed-properties.test.ripple +2 -21
- package/tests/client/context.test.ripple +5 -22
- package/tests/client/date.test.ripple +1 -20
- package/tests/client/dynamic-elements.test.ripple +16 -24
- package/tests/client/for.test.ripple +4 -23
- package/tests/client/head.test.ripple +11 -23
- package/tests/client/html.test.ripple +1 -20
- package/tests/client/input-value.test.ripple +11 -31
- package/tests/client/map.test.ripple +82 -20
- package/tests/client/media-query.test.ripple +10 -23
- package/tests/client/object.test.ripple +5 -24
- package/tests/client/portal.test.ripple +2 -19
- package/tests/client/ref.test.ripple +8 -26
- package/tests/client/set.test.ripple +84 -22
- package/tests/client/svg.test.ripple +1 -22
- package/tests/client/switch.test.ripple +6 -25
- package/tests/client/tracked-expression.test.ripple +2 -21
- package/tests/client/typescript-generics.test.ripple +0 -21
- package/tests/client/url/url.derived.test.ripple +83 -0
- package/tests/client/url/url.parsing.test.ripple +165 -0
- package/tests/client/url/url.partial-removal.test.ripple +198 -0
- package/tests/client/url/url.reactivity.test.ripple +449 -0
- package/tests/client/url/url.serialization.test.ripple +50 -0
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +84 -0
- package/tests/client/url-search-params/url-search-params.initialization.test.ripple +61 -0
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +153 -0
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +343 -0
- package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +160 -0
- package/tests/client/url-search-params/url-search-params.serialization.test.ripple +53 -0
- package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +55 -0
- package/tests/client.d.ts +12 -0
- package/tests/server/if.test.ripple +66 -0
- package/tests/setup-client.js +28 -0
- package/tsconfig.json +4 -2
- package/types/index.d.ts +92 -46
- package/LICENSE +0 -21
- package/tests/client/__snapshots__/basic.test.ripple.snap +0 -117
- package/tests/client/__snapshots__/compiler.test.ripple.snap +0 -33
- package/tests/client/array.test.ripple +0 -1455
- package/tests/client/basic.test.ripple +0 -1892
- package/tests/client/compiler.test.ripple +0 -541
- package/tests/client/composite.test.ripple +0 -692
- package/tests/client/url-search-params.test.ripple +0 -912
- package/tests/client/url.test.ripple +0 -954
|
@@ -1,25 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mount, flushSync, track, TrackedArray, TrackedMap, effect } from 'ripple';
|
|
1
|
+
import { flushSync, track } from 'ripple';
|
|
3
2
|
|
|
4
3
|
describe('computed tracked properties', () => {
|
|
5
|
-
let container;
|
|
6
|
-
|
|
7
|
-
function render(component) {
|
|
8
|
-
mount(component, {
|
|
9
|
-
target: container
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
container = document.createElement('div');
|
|
15
|
-
document.body.appendChild(container);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
document.body.removeChild(container);
|
|
20
|
-
container = null;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
4
|
it('should update a property using assignment', () => {
|
|
24
5
|
component App() {
|
|
25
6
|
let obj = {
|
|
@@ -61,4 +42,4 @@ describe('computed tracked properties', () => {
|
|
|
61
42
|
|
|
62
43
|
expect(container).toMatchSnapshot();
|
|
63
44
|
});
|
|
64
|
-
});
|
|
45
|
+
});
|
|
@@ -1,26 +1,9 @@
|
|
|
1
|
-
import { describe, it, expect
|
|
2
|
-
import {
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { Context, flushSync } from 'ripple';
|
|
3
3
|
|
|
4
4
|
describe('context', () => {
|
|
5
|
-
let container;
|
|
6
|
-
|
|
7
|
-
function render(component) {
|
|
8
|
-
mount(component, {
|
|
9
|
-
target: container
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
container = document.createElement('div');
|
|
15
|
-
document.body.appendChild(container);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
document.body.removeChild(container);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
5
|
it('creates a reactive ref with initial value', () => {
|
|
23
|
-
const MyContext = new Context(null);
|
|
6
|
+
const MyContext = new Context<string | null>(null);
|
|
24
7
|
|
|
25
8
|
component Child() {
|
|
26
9
|
const value = MyContext.get();
|
|
@@ -43,10 +26,10 @@ describe('context', () => {
|
|
|
43
26
|
|
|
44
27
|
it('handles context captured inside a computed tracked', () => {
|
|
45
28
|
|
|
46
|
-
const MyContext = new Context(null)
|
|
29
|
+
const MyContext = new Context<number | null>(null)
|
|
47
30
|
|
|
48
31
|
const doubleContext = () => {
|
|
49
|
-
const value = MyContext.get()
|
|
32
|
+
const value = MyContext.get() as number
|
|
50
33
|
return value * 2
|
|
51
34
|
}
|
|
52
35
|
|
|
@@ -1,25 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mount, flushSync, TrackedDate, track } from 'ripple';
|
|
1
|
+
import { flushSync, TrackedDate, track } from 'ripple';
|
|
3
2
|
|
|
4
3
|
describe('TrackedDate', () => {
|
|
5
|
-
let container;
|
|
6
|
-
|
|
7
|
-
function render(component) {
|
|
8
|
-
mount(component, {
|
|
9
|
-
target: container,
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
container = document.createElement('div');
|
|
15
|
-
document.body.appendChild(container);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
document.body.removeChild(container);
|
|
20
|
-
container = null;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
4
|
it('handles getTime() with reactive updates', () => {
|
|
24
5
|
component DateTest() {
|
|
25
6
|
let date = new TrackedDate(2025, 0, 1);
|
|
@@ -1,23 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mount, flushSync, track, createRefKey } from 'ripple';
|
|
1
|
+
import { flushSync, track, createRefKey } from 'ripple';
|
|
3
2
|
|
|
4
3
|
describe('dynamic DOM elements', () => {
|
|
5
|
-
let container;
|
|
6
|
-
|
|
7
|
-
function render(component) {
|
|
8
|
-
mount(component, {
|
|
9
|
-
target: container,
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
container = document.createElement('div');
|
|
15
|
-
document.body.appendChild(container);
|
|
16
|
-
});
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
document.body.removeChild(container);
|
|
19
|
-
container = null;
|
|
20
|
-
});
|
|
21
4
|
it('renders static dynamic element', () => {
|
|
22
5
|
component App() {
|
|
23
6
|
let tag = track('div');
|
|
@@ -30,6 +13,7 @@ describe('dynamic DOM elements', () => {
|
|
|
30
13
|
expect(element).toBeTruthy();
|
|
31
14
|
expect(element.textContent).toBe('Hello World');
|
|
32
15
|
});
|
|
16
|
+
|
|
33
17
|
it('renders reactive dynamic element', () => {
|
|
34
18
|
component App() {
|
|
35
19
|
let tag = track('div');
|
|
@@ -53,6 +37,7 @@ describe('dynamic DOM elements', () => {
|
|
|
53
37
|
expect(dynamicElement.tagName).toBe('SPAN');
|
|
54
38
|
expect(dynamicElement.textContent).toBe('Hello World');
|
|
55
39
|
});
|
|
40
|
+
|
|
56
41
|
it('renders self-closing dynamic element', () => {
|
|
57
42
|
component App() {
|
|
58
43
|
let tag = track('input');
|
|
@@ -61,11 +46,12 @@ describe('dynamic DOM elements', () => {
|
|
|
61
46
|
}
|
|
62
47
|
render(App);
|
|
63
48
|
|
|
64
|
-
const element = container.querySelector('input');
|
|
49
|
+
const element = container.querySelector('input') as HTMLInputElement;
|
|
65
50
|
expect(element).toBeTruthy();
|
|
66
51
|
expect(element.type).toBe('text');
|
|
67
52
|
expect(element.value).toBe('test');
|
|
68
53
|
});
|
|
54
|
+
|
|
69
55
|
it('handles dynamic element with attributes', () => {
|
|
70
56
|
component App() {
|
|
71
57
|
let tag = track('div');
|
|
@@ -81,6 +67,7 @@ describe('dynamic DOM elements', () => {
|
|
|
81
67
|
expect(element.getAttribute('data-testid')).toBe('dynamic-element');
|
|
82
68
|
expect(element.textContent).toBe('Content');
|
|
83
69
|
});
|
|
70
|
+
|
|
84
71
|
it('handles nested dynamic elements', () => {
|
|
85
72
|
component App() {
|
|
86
73
|
let outerTag = track('div');
|
|
@@ -100,6 +87,7 @@ describe('dynamic DOM elements', () => {
|
|
|
100
87
|
expect(inner.textContent).toBe('Nested content');
|
|
101
88
|
expect(outer.contains(inner)).toBe(true);
|
|
102
89
|
});
|
|
90
|
+
|
|
103
91
|
it('handles dynamic element with class object', () => {
|
|
104
92
|
component App() {
|
|
105
93
|
let tag = track('div');
|
|
@@ -116,12 +104,13 @@ describe('dynamic DOM elements', () => {
|
|
|
116
104
|
expect(element.classList.contains('active')).toBe(true);
|
|
117
105
|
expect(element.classList.contains('dynamic-element')).toBe(true);
|
|
118
106
|
});
|
|
107
|
+
|
|
119
108
|
it('handles dynamic element with style object', () => {
|
|
120
109
|
component App() {
|
|
121
110
|
let tag = track('span');
|
|
122
111
|
|
|
123
|
-
<@tag style={{
|
|
124
|
-
color: 'red',
|
|
112
|
+
<@tag style={{
|
|
113
|
+
color: 'red',
|
|
125
114
|
fontSize: '16px',
|
|
126
115
|
fontWeight: 'bold'
|
|
127
116
|
}}>
|
|
@@ -136,6 +125,7 @@ describe('dynamic DOM elements', () => {
|
|
|
136
125
|
expect(element.style.fontSize).toBe('16px');
|
|
137
126
|
expect(element.style.fontWeight).toBe('bold');
|
|
138
127
|
});
|
|
128
|
+
|
|
139
129
|
it('handles dynamic element with spread attributes', () => {
|
|
140
130
|
component App() {
|
|
141
131
|
let tag = track('section');
|
|
@@ -158,13 +148,14 @@ describe('dynamic DOM elements', () => {
|
|
|
158
148
|
expect(element.className).toBe('spread-class');
|
|
159
149
|
expect(element.getAttribute('data-extra')).toBe('additional');
|
|
160
150
|
});
|
|
151
|
+
|
|
161
152
|
it('handles dynamic element with ref', () => {
|
|
162
|
-
let capturedElement
|
|
153
|
+
let capturedElement: HTMLArticleElement | undefined;
|
|
163
154
|
|
|
164
155
|
component App() {
|
|
165
156
|
let tag = track('article');
|
|
166
157
|
|
|
167
|
-
<@tag {ref (node) => { capturedElement = node; }} id="ref-test">
|
|
158
|
+
<@tag {ref (node: HTMLArticleElement) => { capturedElement = node; }} id="ref-test">
|
|
168
159
|
{'Element with ref'}
|
|
169
160
|
</@tag>
|
|
170
161
|
}
|
|
@@ -175,11 +166,12 @@ describe('dynamic DOM elements', () => {
|
|
|
175
166
|
expect(capturedElement.id).toBe('ref-test');
|
|
176
167
|
expect(capturedElement.textContent).toBe('Element with ref');
|
|
177
168
|
});
|
|
169
|
+
|
|
178
170
|
it('handles dynamic element with createRefKey in spread', () => {
|
|
179
171
|
component App() {
|
|
180
172
|
let tag = track('header');
|
|
181
173
|
|
|
182
|
-
function elementRef(node) {
|
|
174
|
+
function elementRef(node: HTMLHeaderElement) {
|
|
183
175
|
// Set an attribute on the element to prove ref was called
|
|
184
176
|
node.setAttribute('data-spread-ref-called', 'true');
|
|
185
177
|
node.setAttribute('data-spread-ref-tag', node.tagName.toLowerCase());
|
|
@@ -1,26 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mount, flushSync, TrackedArray, track } from 'ripple';
|
|
1
|
+
import { flushSync, TrackedArray, track } from 'ripple';
|
|
3
2
|
|
|
4
3
|
describe('for statements', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function render(component) {
|
|
8
|
-
mount(component, {
|
|
9
|
-
target: container
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
container = document.createElement('div');
|
|
15
|
-
document.body.appendChild(container);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
document.body.removeChild(container);
|
|
20
|
-
container = null;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('render a simple static array', () => {
|
|
4
|
+
it('renders a simple static array', () => {
|
|
24
5
|
component App() {
|
|
25
6
|
const items = ['Item 1', 'Item 2', 'Item 3'];
|
|
26
7
|
|
|
@@ -34,7 +15,7 @@ describe('for statements', () => {
|
|
|
34
15
|
expect(container).toMatchSnapshot();
|
|
35
16
|
});
|
|
36
17
|
|
|
37
|
-
it('
|
|
18
|
+
it('renders a simple dynamic array', () => {
|
|
38
19
|
component App() {
|
|
39
20
|
const items = new TrackedArray('Item 1', 'Item 2', 'Item 3');
|
|
40
21
|
|
|
@@ -76,7 +57,7 @@ describe('for statements', () => {
|
|
|
76
57
|
render(App);
|
|
77
58
|
|
|
78
59
|
expect(container).toMatchSnapshot();
|
|
79
|
-
|
|
60
|
+
|
|
80
61
|
const button = container.querySelector('button');
|
|
81
62
|
|
|
82
63
|
button.click();
|
|
@@ -1,26 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mount, flushSync, track } from 'ripple';
|
|
1
|
+
import { flushSync, track } from 'ripple';
|
|
3
2
|
|
|
4
3
|
describe('head elements', () => {
|
|
5
|
-
let
|
|
6
|
-
let originalTitle;
|
|
7
|
-
|
|
8
|
-
function render(component) {
|
|
9
|
-
mount(component, {
|
|
10
|
-
target: container
|
|
11
|
-
});
|
|
12
|
-
}
|
|
4
|
+
let originalTitle: string;
|
|
13
5
|
|
|
14
6
|
beforeEach(() => {
|
|
15
|
-
container = document.createElement('div');
|
|
16
|
-
document.body.appendChild(container);
|
|
17
7
|
// Store original title to restore later
|
|
18
8
|
originalTitle = document.title;
|
|
19
9
|
});
|
|
20
10
|
|
|
21
11
|
afterEach(() => {
|
|
22
|
-
document.body.removeChild(container);
|
|
23
|
-
container = null;
|
|
24
12
|
// Restore original title
|
|
25
13
|
document.title = originalTitle;
|
|
26
14
|
});
|
|
@@ -34,7 +22,7 @@ describe('head elements', () => {
|
|
|
34
22
|
}
|
|
35
23
|
|
|
36
24
|
render(App);
|
|
37
|
-
|
|
25
|
+
|
|
38
26
|
expect(document.title).toBe('Static Test Title');
|
|
39
27
|
expect(container.querySelector('div').textContent).toBe('Content');
|
|
40
28
|
});
|
|
@@ -53,7 +41,7 @@ describe('head elements', () => {
|
|
|
53
41
|
}
|
|
54
42
|
|
|
55
43
|
render(App);
|
|
56
|
-
|
|
44
|
+
|
|
57
45
|
expect(document.title).toBe('Initial Title');
|
|
58
46
|
expect(container.querySelector('span').textContent).toBe('Initial Title');
|
|
59
47
|
|
|
@@ -78,7 +66,7 @@ describe('head elements', () => {
|
|
|
78
66
|
}
|
|
79
67
|
|
|
80
68
|
render(App);
|
|
81
|
-
|
|
69
|
+
|
|
82
70
|
expect(document.title).toBe('Hello World!');
|
|
83
71
|
|
|
84
72
|
const button = container.querySelector('button');
|
|
@@ -103,7 +91,7 @@ describe('head elements', () => {
|
|
|
103
91
|
}
|
|
104
92
|
|
|
105
93
|
render(App);
|
|
106
|
-
|
|
94
|
+
|
|
107
95
|
expect(document.title).toBe('Count: 0');
|
|
108
96
|
|
|
109
97
|
const button = container.querySelector('button');
|
|
@@ -127,11 +115,11 @@ describe('head elements', () => {
|
|
|
127
115
|
}
|
|
128
116
|
|
|
129
117
|
render(App);
|
|
130
|
-
|
|
118
|
+
|
|
131
119
|
expect(document.title).toBe('Step 1 of 3');
|
|
132
120
|
|
|
133
121
|
const button = container.querySelector('button');
|
|
134
|
-
|
|
122
|
+
|
|
135
123
|
button.click();
|
|
136
124
|
flushSync();
|
|
137
125
|
expect(document.title).toBe('Step 2 of 3');
|
|
@@ -154,7 +142,7 @@ describe('head elements', () => {
|
|
|
154
142
|
}
|
|
155
143
|
|
|
156
144
|
render(App);
|
|
157
|
-
|
|
145
|
+
|
|
158
146
|
expect(document.title).toBe('');
|
|
159
147
|
});
|
|
160
148
|
|
|
@@ -173,11 +161,11 @@ describe('head elements', () => {
|
|
|
173
161
|
}
|
|
174
162
|
|
|
175
163
|
render(App);
|
|
176
|
-
|
|
164
|
+
|
|
177
165
|
expect(document.title).toBe('App - Main Page');
|
|
178
166
|
|
|
179
167
|
const buttons = container.querySelectorAll('button');
|
|
180
|
-
|
|
168
|
+
|
|
181
169
|
// Toggle prefix off
|
|
182
170
|
buttons[0].click();
|
|
183
171
|
flushSync();
|
|
@@ -1,25 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mount, flushSync, track } from 'ripple';
|
|
1
|
+
import { flushSync, track } from 'ripple';
|
|
3
2
|
|
|
4
3
|
describe('html directive', () => {
|
|
5
|
-
let container;
|
|
6
|
-
|
|
7
|
-
function render(component) {
|
|
8
|
-
mount(component, {
|
|
9
|
-
target: container
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
container = document.createElement('div');
|
|
15
|
-
document.body.appendChild(container);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
document.body.removeChild(container);
|
|
20
|
-
container = null;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
4
|
it('renders static html', () => {
|
|
24
5
|
component App() {
|
|
25
6
|
let str = '<div>Test</div>';
|
|
@@ -1,28 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mount, flushSync, track, effect } from 'ripple';
|
|
3
|
-
import { bindValue, bindChecked } from 'ripple';
|
|
1
|
+
import { flushSync, track, effect, bindValue, bindChecked } from 'ripple';
|
|
4
2
|
|
|
5
3
|
describe('use value()', () => {
|
|
6
|
-
let container;
|
|
7
|
-
|
|
8
|
-
function render(component) {
|
|
9
|
-
mount(component, {
|
|
10
|
-
target: container,
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
container = document.createElement('div');
|
|
16
|
-
document.body.appendChild(container);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
document.body.removeChild(container);
|
|
21
|
-
container = null;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
4
|
it('should update value on input', () => {
|
|
25
|
-
const logs = [];
|
|
5
|
+
const logs: string[] = [];
|
|
26
6
|
|
|
27
7
|
component App() {
|
|
28
8
|
const text = track('');
|
|
@@ -36,7 +16,7 @@ describe('use value()', () => {
|
|
|
36
16
|
render(App);
|
|
37
17
|
flushSync();
|
|
38
18
|
|
|
39
|
-
const input = container.querySelector('input');
|
|
19
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
40
20
|
input.value = 'Hello';
|
|
41
21
|
input.dispatchEvent(new Event('input'));
|
|
42
22
|
flushSync();
|
|
@@ -45,7 +25,7 @@ describe('use value()', () => {
|
|
|
45
25
|
});
|
|
46
26
|
|
|
47
27
|
it('should update value on input with a predefined value', () => {
|
|
48
|
-
const logs = [];
|
|
28
|
+
const logs: string[] = [];
|
|
49
29
|
|
|
50
30
|
component App() {
|
|
51
31
|
const text = track('foo');
|
|
@@ -59,8 +39,8 @@ describe('use value()', () => {
|
|
|
59
39
|
render(App);
|
|
60
40
|
flushSync();
|
|
61
41
|
|
|
62
|
-
|
|
63
|
-
|
|
42
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
43
|
+
expect(input.value).toBe('foo');
|
|
64
44
|
input.value = 'Hello';
|
|
65
45
|
input.dispatchEvent(new Event('input'));
|
|
66
46
|
flushSync();
|
|
@@ -69,7 +49,7 @@ describe('use value()', () => {
|
|
|
69
49
|
});
|
|
70
50
|
|
|
71
51
|
it('should update checked on input', () => {
|
|
72
|
-
const logs = [];
|
|
52
|
+
const logs: string[] = [];
|
|
73
53
|
|
|
74
54
|
component App() {
|
|
75
55
|
const value = track(false);
|
|
@@ -83,17 +63,17 @@ describe('use value()', () => {
|
|
|
83
63
|
render(App);
|
|
84
64
|
flushSync();
|
|
85
65
|
|
|
86
|
-
const input = container.querySelector('input');
|
|
66
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
87
67
|
input.checked = true;
|
|
88
68
|
input.dispatchEvent(new Event('change'));
|
|
89
69
|
flushSync();
|
|
90
|
-
|
|
70
|
+
|
|
91
71
|
expect(input.checked).toBe(true);
|
|
92
72
|
expect(logs).toEqual(['checked changed', false, 'checked changed', true]);
|
|
93
73
|
});
|
|
94
74
|
|
|
95
75
|
it('should update select value on change', () => {
|
|
96
|
-
const logs = [];
|
|
76
|
+
const logs: string[] = [];
|
|
97
77
|
|
|
98
78
|
component App() {
|
|
99
79
|
const select = track('2');
|
|
@@ -112,7 +92,7 @@ describe('use value()', () => {
|
|
|
112
92
|
render(App);
|
|
113
93
|
flushSync();
|
|
114
94
|
|
|
115
|
-
const select = container.querySelector('select');
|
|
95
|
+
const select = container.querySelector('select') as HTMLSelectElement;
|
|
116
96
|
select.value = '3';
|
|
117
97
|
select.dispatchEvent(new Event('change'));
|
|
118
98
|
flushSync();
|
|
@@ -1,25 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mount, flushSync, TrackedMap, track } from 'ripple';
|
|
1
|
+
import { flushSync, TrackedMap, track } from 'ripple';
|
|
3
2
|
|
|
4
3
|
describe('TrackedMap', () => {
|
|
5
|
-
let container;
|
|
6
|
-
|
|
7
|
-
function render(component) {
|
|
8
|
-
mount(component, {
|
|
9
|
-
target: container
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
container = document.createElement('div');
|
|
15
|
-
document.body.appendChild(container);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
document.body.removeChild(container);
|
|
20
|
-
container = null;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
4
|
it('handles set with update and delete operations with a reactive size var', () => {
|
|
24
5
|
component MapTest() {
|
|
25
6
|
let map = new TrackedMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
@@ -138,4 +119,85 @@ describe('TrackedMap', () => {
|
|
|
138
119
|
|
|
139
120
|
expect(container.querySelectorAll('pre')[0].textContent).toBe('[["foo",1],["bar",2]]');
|
|
140
121
|
});
|
|
122
|
+
|
|
123
|
+
it('creates empty TrackedMap using #Map() shorthand syntax', () => {
|
|
124
|
+
component MapTest() {
|
|
125
|
+
let map = #Map();
|
|
126
|
+
|
|
127
|
+
<button onClick={() => map.set('a', 1)}>{'add'}</button>
|
|
128
|
+
<pre>{map.size}</pre>
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
render(MapTest);
|
|
132
|
+
|
|
133
|
+
expect(container.querySelector('pre').textContent).toBe('0');
|
|
134
|
+
|
|
135
|
+
const addButton = container.querySelector('button');
|
|
136
|
+
addButton.click();
|
|
137
|
+
flushSync();
|
|
138
|
+
|
|
139
|
+
expect(container.querySelector('pre').textContent).toBe('1');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
it('creates TrackedMap with initial entries using #Map() shorthand syntax', () => {
|
|
144
|
+
component MapTest() {
|
|
145
|
+
let map = #Map([['a', 1], ['b', 2], ['c', 3]]);
|
|
146
|
+
let value = track(() => map.get('b'));
|
|
147
|
+
|
|
148
|
+
<button onClick={() => map.set('b', 10)}>{'update'}</button>
|
|
149
|
+
<pre>{map.size}</pre>
|
|
150
|
+
<pre>{@value}</pre>
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
render(MapTest);
|
|
154
|
+
|
|
155
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('3');
|
|
156
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('2');
|
|
157
|
+
|
|
158
|
+
const updateButton = container.querySelector('button');
|
|
159
|
+
updateButton.click();
|
|
160
|
+
flushSync();
|
|
161
|
+
|
|
162
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('10');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('handles all operations with #Map() shorthand syntax', () => {
|
|
166
|
+
component MapTest() {
|
|
167
|
+
let map = #Map([['x', 100], ['y', 200]]);
|
|
168
|
+
let keys = track(() => Array.from(map.keys()));
|
|
169
|
+
|
|
170
|
+
<button onClick={() => map.set('z', 300)}>{'add'}</button>
|
|
171
|
+
<button onClick={() => map.delete('x')}>{'delete'}</button>
|
|
172
|
+
<button onClick={() => map.clear()}>{'clear'}</button>
|
|
173
|
+
|
|
174
|
+
<pre>{JSON.stringify(@keys)}</pre>
|
|
175
|
+
<pre>{map.size}</pre>
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
render(MapTest);
|
|
179
|
+
|
|
180
|
+
const [addButton, deleteButton, clearButton] = container.querySelectorAll('button');
|
|
181
|
+
|
|
182
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["x","y"]');
|
|
183
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('2');
|
|
184
|
+
|
|
185
|
+
addButton.click();
|
|
186
|
+
flushSync();
|
|
187
|
+
|
|
188
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["x","y","z"]');
|
|
189
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
|
|
190
|
+
|
|
191
|
+
deleteButton.click();
|
|
192
|
+
flushSync();
|
|
193
|
+
|
|
194
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["y","z"]');
|
|
195
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('2');
|
|
196
|
+
|
|
197
|
+
clearButton.click();
|
|
198
|
+
flushSync();
|
|
199
|
+
|
|
200
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[]');
|
|
201
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('0');
|
|
202
|
+
});
|
|
141
203
|
});
|
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { flushSync, MediaQuery, track } from 'ripple';
|
|
2
|
+
|
|
3
|
+
type Callback = (event: MediaQueryListEvent) => void;
|
|
3
4
|
|
|
4
5
|
function setupMatchMedia() {
|
|
5
|
-
let listeners = new Set();
|
|
6
|
+
let listeners = new Set<Callback>();
|
|
6
7
|
|
|
7
8
|
// A mock implementation of matchMedia
|
|
8
9
|
const mockMatchMedia = vi.fn().mockImplementation(query => {
|
|
9
10
|
return {
|
|
10
11
|
media: query,
|
|
11
12
|
matches: false, // default value
|
|
12
|
-
addEventListener: (type, cb) => {
|
|
13
|
+
addEventListener: (type: string, cb: Callback) => {
|
|
13
14
|
if (type === 'change') listeners.add(cb);
|
|
14
15
|
},
|
|
15
|
-
removeEventListener: (type, cb) => {
|
|
16
|
+
removeEventListener: (type: string, cb: Callback) => {
|
|
16
17
|
if (type === 'change') listeners.delete(cb);
|
|
17
18
|
},
|
|
18
19
|
/** @param {function(MediaQueryListEvent): void} cb */
|
|
19
|
-
addListener: (cb) => listeners.add(cb),
|
|
20
|
+
addListener: (cb: Callback) => listeners.add(cb),
|
|
20
21
|
/** @param {function(MediaQueryListEvent): void} cb */
|
|
21
|
-
removeListener: (cb) => listeners.delete(cb),
|
|
22
|
-
dispatch: (event) => {
|
|
22
|
+
removeListener: (cb: Callback) => listeners.delete(cb),
|
|
23
|
+
dispatch: (event: MediaQueryListEvent) => {
|
|
23
24
|
listeners.forEach((cb) => cb(event));
|
|
24
25
|
},
|
|
25
26
|
listenersCount: () => listeners.size,
|
|
@@ -35,24 +36,10 @@ function setupMatchMedia() {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
describe('MediaQuery', () => {
|
|
38
|
-
let
|
|
39
|
-
let mm;
|
|
40
|
-
|
|
41
|
-
function render(component) {
|
|
42
|
-
mount(component, {
|
|
43
|
-
target: container
|
|
44
|
-
});
|
|
45
|
-
}
|
|
39
|
+
let mm: ReturnType<typeof setupMatchMedia>;
|
|
46
40
|
|
|
47
41
|
beforeEach(() => {
|
|
48
42
|
mm = setupMatchMedia();
|
|
49
|
-
container = document.createElement('div');
|
|
50
|
-
document.body.appendChild(container);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
afterEach(() => {
|
|
54
|
-
document.body.removeChild(container);
|
|
55
|
-
container = null;
|
|
56
43
|
});
|
|
57
44
|
|
|
58
45
|
it('should be reactive if matchMedia changes', () => {
|