ripple 0.2.6 → 0.2.7
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/README.md +1 -1
- package/package.json +1 -1
- package/src/compiler/errors.js +20 -22
- package/src/compiler/phases/1-parse/index.js +14 -10
- package/src/compiler/phases/1-parse/style.js +27 -27
- package/src/compiler/phases/2-analyze/index.js +25 -25
- package/src/compiler/phases/2-analyze/prune.js +64 -27
- package/src/compiler/phases/3-transform/index.js +144 -109
- package/src/compiler/phases/3-transform/segments.js +25 -20
- package/src/compiler/phases/3-transform/stylesheet.js +28 -28
- package/src/compiler/scope.js +3 -3
- package/src/compiler/utils.js +7 -9
- package/src/constants.js +1 -2
- package/src/jsx-runtime.d.ts +59 -59
- package/src/jsx-runtime.js +13 -13
- package/src/runtime/array.js +15 -15
- package/src/runtime/internal/client/blocks.js +1 -1
- package/src/runtime/internal/client/constants.js +1 -1
- package/src/runtime/internal/client/events.js +2 -2
- package/src/runtime/internal/client/for.js +5 -5
- package/src/runtime/internal/client/operations.js +1 -1
- package/src/runtime/internal/client/runtime.js +28 -20
- package/src/runtime/internal/client/template.js +1 -1
- package/src/runtime/internal/client/try.js +2 -2
- package/src/utils/ast.js +9 -9
- package/src/utils/builders.js +66 -28
- package/tests/basic.test.ripple +292 -263
- package/tests/composite.test.ripple +125 -0
- package/tests/use.test.ripple +24 -22
- package/types/index.d.ts +1 -1
package/tests/basic.test.ripple
CHANGED
|
@@ -1,273 +1,302 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
|
|
2
3
|
import { mount, flushSync } from 'ripple';
|
|
3
4
|
|
|
4
5
|
describe('basic', () => {
|
|
5
|
-
|
|
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
|
+
it('render static text', () => {
|
|
25
|
+
component Basic() {
|
|
26
|
+
<div>{'Hello World'}</div>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
render(Basic);
|
|
30
|
+
expect(container).toMatchSnapshot();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('render static attributes', () => {
|
|
34
|
+
component Basic() {
|
|
35
|
+
<div class='foo' id='bar' style='color: red;'>{'Hello World'}</div>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
render(Basic);
|
|
39
|
+
|
|
40
|
+
expect(container).toMatchSnapshot();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('render semi-dynamic text', () => {
|
|
44
|
+
component Basic() {
|
|
45
|
+
let text = 'Hello World';
|
|
46
|
+
<div>{text}</div>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
render(Basic);
|
|
50
|
+
|
|
51
|
+
expect(container).toMatchSnapshot();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('render dynamic text', () => {
|
|
55
|
+
component Basic() {
|
|
56
|
+
let $text = 'Hello World';
|
|
57
|
+
<button onClick={() => $text = 'Hello Ripple'}>{'Change Text'}</button>
|
|
58
|
+
<div>{$text}</div>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
render(Basic);
|
|
62
|
+
|
|
63
|
+
const button = container.querySelector('button');
|
|
64
|
+
|
|
65
|
+
button.click();
|
|
66
|
+
flushSync();
|
|
67
|
+
|
|
68
|
+
expect(container.querySelector('div').textContent).toEqual('Hello Ripple');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('render dynamic class attribute', () => {
|
|
72
|
+
component Basic() {
|
|
73
|
+
let $active = false;
|
|
74
|
+
<button onClick={() => $active = !$active}>{'Toggle'}</button>
|
|
75
|
+
<div $class={$active ? 'active' : 'inactive'}>{'Dynamic Class'}</div>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
render(Basic);
|
|
79
|
+
|
|
80
|
+
const button = container.querySelector('button');
|
|
81
|
+
const div = container.querySelector('div');
|
|
82
|
+
|
|
83
|
+
expect(div.className).toBe('inactive');
|
|
84
|
+
|
|
85
|
+
button.click();
|
|
86
|
+
flushSync();
|
|
87
|
+
|
|
88
|
+
expect(div.className).toBe('active');
|
|
89
|
+
|
|
90
|
+
button.click();
|
|
91
|
+
flushSync();
|
|
92
|
+
|
|
93
|
+
expect(div.className).toBe('inactive');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('render dynamic id attribute', () => {
|
|
97
|
+
component Basic() {
|
|
98
|
+
let $count = 0;
|
|
99
|
+
<button onClick={() => $count++}>{'Increment'}</button>
|
|
100
|
+
<div $id={`item-${$count}`}>{'Dynamic ID'}</div>
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
render(Basic);
|
|
104
|
+
|
|
105
|
+
const button = container.querySelector('button');
|
|
106
|
+
const div = container.querySelector('div');
|
|
107
|
+
|
|
108
|
+
expect(div.id).toBe('item-0');
|
|
109
|
+
|
|
110
|
+
button.click();
|
|
111
|
+
flushSync();
|
|
112
|
+
|
|
113
|
+
expect(div.id).toBe('item-1');
|
|
114
|
+
|
|
115
|
+
button.click();
|
|
116
|
+
flushSync();
|
|
117
|
+
|
|
118
|
+
expect(div.id).toBe('item-2');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('render dynamic style attribute', () => {
|
|
122
|
+
component Basic() {
|
|
123
|
+
let $color = 'red';
|
|
124
|
+
<button onClick={() => $color = $color === 'red' ? 'blue' : 'red'}>{'Change Color'}</button>
|
|
125
|
+
<div $style={`color: ${$color}; font-weight: bold;`}>{'Dynamic Style'}</div>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
render(Basic);
|
|
129
|
+
|
|
130
|
+
const button = container.querySelector('button');
|
|
131
|
+
const div = container.querySelector('div');
|
|
132
|
+
|
|
133
|
+
expect(div.style.color).toBe('red');
|
|
134
|
+
expect(div.style.fontWeight).toBe('bold');
|
|
135
|
+
|
|
136
|
+
button.click();
|
|
137
|
+
flushSync();
|
|
6
138
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
139
|
+
expect(div.style.color).toBe('blue');
|
|
140
|
+
expect(div.style.fontWeight).toBe('bold');
|
|
141
|
+
});
|
|
10
142
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
143
|
+
it('render dynamic boolean attributes', () => {
|
|
144
|
+
component Basic() {
|
|
145
|
+
let $disabled = false;
|
|
146
|
+
let $checked = false;
|
|
147
|
+
<button onClick={() => {
|
|
148
|
+
$disabled = !$disabled;
|
|
149
|
+
$checked = !$checked;
|
|
150
|
+
}}>{'Toggle'}</button>
|
|
151
|
+
<input type='checkbox' $disabled={$disabled} $checked={$checked} />
|
|
152
|
+
}
|
|
15
153
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
it('render static text', () => {
|
|
22
|
-
component Basic() {
|
|
23
|
-
<div>{"Hello World"}</div>
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
render(Basic);
|
|
27
|
-
|
|
28
|
-
expect(container).toMatchSnapshot();
|
|
29
|
-
});
|
|
154
|
+
render(Basic);
|
|
155
|
+
|
|
156
|
+
const button = container.querySelector('button');
|
|
157
|
+
const input = container.querySelector('input');
|
|
30
158
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
<div class="foo" id="bar" style="color: red;">{"Hello World"}</div>
|
|
34
|
-
}
|
|
159
|
+
expect(input.disabled).toBe(false);
|
|
160
|
+
expect(input.checked).toBe(false);
|
|
35
161
|
|
|
36
|
-
|
|
162
|
+
button.click();
|
|
163
|
+
flushSync();
|
|
37
164
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
let $theme = 'light';
|
|
177
|
-
let $size = 'medium';
|
|
178
|
-
|
|
179
|
-
<button onClick={() => {
|
|
180
|
-
$theme = $theme === 'light' ? 'dark' : 'light';
|
|
181
|
-
$size = $size === 'medium' ? 'large' : 'medium';
|
|
182
|
-
}}>{"Toggle Theme & Size"}</button>
|
|
183
|
-
|
|
184
|
-
<div
|
|
185
|
-
$class={`theme-${$theme} size-${$size}`}
|
|
186
|
-
$data-theme={$theme}
|
|
187
|
-
$data-size={$size}
|
|
188
|
-
>
|
|
189
|
-
{"Multiple Dynamic Attributes"}
|
|
190
|
-
</div>
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
render(Basic);
|
|
194
|
-
|
|
195
|
-
const button = container.querySelector('button');
|
|
196
|
-
const div = container.querySelector('div');
|
|
197
|
-
|
|
198
|
-
expect(div.className).toBe('theme-light size-medium');
|
|
199
|
-
expect(div.getAttribute('data-theme')).toBe('light');
|
|
200
|
-
expect(div.getAttribute('data-size')).toBe('medium');
|
|
201
|
-
|
|
202
|
-
button.click();
|
|
203
|
-
flushSync();
|
|
204
|
-
|
|
205
|
-
expect(div.className).toBe('theme-dark size-large');
|
|
206
|
-
expect(div.getAttribute('data-theme')).toBe('dark');
|
|
207
|
-
expect(div.getAttribute('data-size')).toBe('large');
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('render conditional attributes', () => {
|
|
211
|
-
component Basic() {
|
|
212
|
-
let $showTitle = false;
|
|
213
|
-
let $showAria = false;
|
|
214
|
-
|
|
215
|
-
<button onClick={() => { $showTitle = !$showTitle; $showAria = !$showAria; }}>{"Toggle Attributes"}</button>
|
|
216
|
-
|
|
217
|
-
<div
|
|
218
|
-
$title={$showTitle ? 'This is a title' : null}
|
|
219
|
-
$aria-label={$showAria ? 'Accessible label' : null}
|
|
220
|
-
>
|
|
221
|
-
{"Conditional Attributes"}
|
|
222
|
-
</div>
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
render(Basic);
|
|
226
|
-
|
|
227
|
-
const button = container.querySelector('button');
|
|
228
|
-
const div = container.querySelector('div');
|
|
229
|
-
|
|
230
|
-
expect(div.hasAttribute('title')).toBe(false);
|
|
231
|
-
expect(div.hasAttribute('aria-label')).toBe(false);
|
|
232
|
-
|
|
233
|
-
button.click();
|
|
234
|
-
flushSync();
|
|
235
|
-
|
|
236
|
-
expect(div.getAttribute('title')).toBe('This is a title');
|
|
237
|
-
expect(div.getAttribute('aria-label')).toBe('Accessible label');
|
|
238
|
-
|
|
239
|
-
button.click();
|
|
240
|
-
flushSync();
|
|
241
|
-
|
|
242
|
-
expect(div.hasAttribute('title')).toBe(false);
|
|
243
|
-
expect(div.hasAttribute('aria-label')).toBe(false);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('render spread attributes', () => {
|
|
247
|
-
component Basic() {
|
|
248
|
-
let $attrs = { class: 'initial', id: 'test-1' };
|
|
249
|
-
|
|
250
|
-
<button onClick={() => {
|
|
251
|
-
$attrs = { class: 'updated', id: 'test-2', 'data-extra': 'value' };
|
|
252
|
-
}}>{"Update Attributes"}</button>
|
|
253
|
-
|
|
254
|
-
<div {...$attrs}>{"Spread Attributes"}</div>
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
render(Basic);
|
|
258
|
-
|
|
259
|
-
const button = container.querySelector('button');
|
|
260
|
-
const div = container.querySelector('div');
|
|
261
|
-
|
|
262
|
-
expect(div.className).toBe('initial');
|
|
263
|
-
expect(div.id).toBe('test-1');
|
|
264
|
-
expect(div.hasAttribute('data-extra')).toBe(false);
|
|
265
|
-
|
|
266
|
-
button.click();
|
|
267
|
-
flushSync();
|
|
268
|
-
|
|
269
|
-
expect(div.className).toBe('updated');
|
|
270
|
-
expect(div.id).toBe('test-2');
|
|
271
|
-
expect(div.getAttribute('data-extra')).toBe('value');
|
|
272
|
-
});
|
|
273
|
-
});
|
|
165
|
+
expect(input.disabled).toBe(true);
|
|
166
|
+
expect(input.checked).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('render multiple dynamic attributes', () => {
|
|
170
|
+
component Basic() {
|
|
171
|
+
let $theme = 'light';
|
|
172
|
+
let $size = 'medium';
|
|
173
|
+
<button
|
|
174
|
+
onClick={() => {
|
|
175
|
+
$theme = $theme === 'light' ? 'dark' : 'light';
|
|
176
|
+
$size = $size === 'medium' ? 'large' : 'medium';
|
|
177
|
+
}}
|
|
178
|
+
>{'Toggle Theme & Size'}</button>
|
|
179
|
+
<div $class={`theme-${$theme} size-${$size}`} $data-theme={$theme} $data-size={$size}>{'Multiple Dynamic Attributes'}</div>
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
render(Basic);
|
|
183
|
+
|
|
184
|
+
const button = container.querySelector('button');
|
|
185
|
+
const div = container.querySelector('div');
|
|
186
|
+
|
|
187
|
+
expect(div.className).toBe('theme-light size-medium');
|
|
188
|
+
expect(div.getAttribute('data-theme')).toBe('light');
|
|
189
|
+
expect(div.getAttribute('data-size')).toBe('medium');
|
|
190
|
+
|
|
191
|
+
button.click();
|
|
192
|
+
flushSync();
|
|
193
|
+
|
|
194
|
+
expect(div.className).toBe('theme-dark size-large');
|
|
195
|
+
expect(div.getAttribute('data-theme')).toBe('dark');
|
|
196
|
+
expect(div.getAttribute('data-size')).toBe('large');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('render conditional attributes', () => {
|
|
200
|
+
component Basic() {
|
|
201
|
+
let $showTitle = false;
|
|
202
|
+
let $showAria = false;
|
|
203
|
+
<button onClick={() => {
|
|
204
|
+
$showTitle = !$showTitle;
|
|
205
|
+
$showAria = !$showAria;
|
|
206
|
+
}}>{'Toggle Attributes'}</button>
|
|
207
|
+
<div
|
|
208
|
+
$title={$showTitle ? 'This is a title' : null}
|
|
209
|
+
$aria-label={$showAria ? 'Accessible label' : null}
|
|
210
|
+
>{'Conditional Attributes'}</div>
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
render(Basic);
|
|
214
|
+
|
|
215
|
+
const button = container.querySelector('button');
|
|
216
|
+
const div = container.querySelector('div');
|
|
217
|
+
|
|
218
|
+
expect(div.hasAttribute('title')).toBe(false);
|
|
219
|
+
expect(div.hasAttribute('aria-label')).toBe(false);
|
|
220
|
+
|
|
221
|
+
button.click();
|
|
222
|
+
flushSync();
|
|
223
|
+
|
|
224
|
+
expect(div.getAttribute('title')).toBe('This is a title');
|
|
225
|
+
expect(div.getAttribute('aria-label')).toBe('Accessible label');
|
|
226
|
+
|
|
227
|
+
button.click();
|
|
228
|
+
flushSync();
|
|
229
|
+
|
|
230
|
+
expect(div.hasAttribute('title')).toBe(false);
|
|
231
|
+
expect(div.hasAttribute('aria-label')).toBe(false);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('render spread attributes', () => {
|
|
235
|
+
component Basic() {
|
|
236
|
+
let $attrs = {
|
|
237
|
+
class: 'initial',
|
|
238
|
+
id: 'test-1'
|
|
239
|
+
};
|
|
240
|
+
<button
|
|
241
|
+
onClick={() => {
|
|
242
|
+
$attrs = {
|
|
243
|
+
class: 'updated',
|
|
244
|
+
id: 'test-2',
|
|
245
|
+
'data-extra': 'value'
|
|
246
|
+
};
|
|
247
|
+
}}
|
|
248
|
+
>{'Update Attributes'}</button>
|
|
249
|
+
<div {...$attrs}>{'Spread Attributes'}</div>
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
render(Basic);
|
|
253
|
+
|
|
254
|
+
const button = container.querySelector('button');
|
|
255
|
+
const div = container.querySelector('div');
|
|
256
|
+
|
|
257
|
+
expect(div.className).toBe('initial');
|
|
258
|
+
expect(div.id).toBe('test-1');
|
|
259
|
+
expect(div.hasAttribute('data-extra')).toBe(false);
|
|
260
|
+
|
|
261
|
+
button.click();
|
|
262
|
+
flushSync();
|
|
263
|
+
|
|
264
|
+
expect(div.className).toBe('updated');
|
|
265
|
+
expect(div.id).toBe('test-2');
|
|
266
|
+
expect(div.getAttribute('data-extra')).toBe('value');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('renders multiple reactive lexical blocks', () => {
|
|
270
|
+
component Basic() {
|
|
271
|
+
<div>
|
|
272
|
+
let obj = {
|
|
273
|
+
$count: 0
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
<span>{obj.$count}</span>
|
|
277
|
+
</div>
|
|
278
|
+
<div>
|
|
279
|
+
let b = {
|
|
280
|
+
$count: 0
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
<button onClick={() => b.$count--}>{'-'}</button>
|
|
284
|
+
<span class='count'>{b.$count}</span>
|
|
285
|
+
<button onClick={() => b.$count++}>{'+'}</button>
|
|
286
|
+
</div>
|
|
287
|
+
}
|
|
288
|
+
render(Basic);
|
|
289
|
+
|
|
290
|
+
const buttons = container.querySelectorAll('button');
|
|
291
|
+
|
|
292
|
+
buttons[0].click();
|
|
293
|
+
flushSync();
|
|
294
|
+
|
|
295
|
+
expect(container.querySelector('.count').textContent).toBe('-1');
|
|
296
|
+
|
|
297
|
+
buttons[1].click();
|
|
298
|
+
flushSync();
|
|
299
|
+
|
|
300
|
+
expect(container.querySelector('.count').textContent).toBe('0');
|
|
301
|
+
});
|
|
302
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { mount, flushSync } from 'ripple';
|
|
4
|
+
|
|
5
|
+
describe('composite components', () => {
|
|
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
|
+
it('renders composite components with object state', () => {
|
|
25
|
+
component Button({ obj }) {
|
|
26
|
+
<button class='count2' onClick={() => {
|
|
27
|
+
obj.$count++;
|
|
28
|
+
}}>{obj.$count}</button>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
component App() {
|
|
32
|
+
<div>
|
|
33
|
+
let obj = {
|
|
34
|
+
$count: 0
|
|
35
|
+
};
|
|
36
|
+
let $button = true;
|
|
37
|
+
|
|
38
|
+
<span class='count'>{obj.$count}</span>
|
|
39
|
+
<span>{' '}</span>
|
|
40
|
+
<Button obj={obj} />
|
|
41
|
+
</div>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
render(App);
|
|
45
|
+
|
|
46
|
+
const button = container.querySelector('button');
|
|
47
|
+
|
|
48
|
+
button.click();
|
|
49
|
+
flushSync();
|
|
50
|
+
|
|
51
|
+
expect(container.querySelector('.count').textContent).toBe('1');
|
|
52
|
+
expect(container.querySelector('.count2').textContent).toBe('1');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('renders composite components with object state', () => {
|
|
56
|
+
component Button({ obj }) {
|
|
57
|
+
<button class='count2' onClick={() => {
|
|
58
|
+
obj.$count++;
|
|
59
|
+
}}>{obj.$count}</button>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
component App() {
|
|
63
|
+
<div>
|
|
64
|
+
let obj = {
|
|
65
|
+
$count: 0
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
<span class='count'>{obj.$count}</span>
|
|
69
|
+
<Button obj={obj} />
|
|
70
|
+
</div>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
render(App);
|
|
74
|
+
|
|
75
|
+
const button = container.querySelector('button');
|
|
76
|
+
|
|
77
|
+
button.click();
|
|
78
|
+
flushSync();
|
|
79
|
+
|
|
80
|
+
expect(container.querySelector('.count').textContent).toBe('1');
|
|
81
|
+
expect(container.querySelector('.count2').textContent).toBe('1');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('renders composite components with object state wrapped in an if statement', () => {
|
|
85
|
+
component Button({ obj }) {
|
|
86
|
+
<button class='count2' onClick={() => {
|
|
87
|
+
obj.$count++;
|
|
88
|
+
}}>{obj.$count}</button>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
component OtherComponent({ obj }) {
|
|
92
|
+
<div class='count3'>{obj.$count}</div>
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
component App() {
|
|
96
|
+
<div>
|
|
97
|
+
let obj = {
|
|
98
|
+
$count: 0
|
|
99
|
+
};
|
|
100
|
+
let $button = true;
|
|
101
|
+
|
|
102
|
+
<span class='count'>{obj.$count}</span>
|
|
103
|
+
<span>{' '}</span>
|
|
104
|
+
if (obj) {
|
|
105
|
+
<Button obj={obj} />
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (obj) {
|
|
109
|
+
<OtherComponent obj={obj} />
|
|
110
|
+
}
|
|
111
|
+
</div>
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
render(App);
|
|
115
|
+
|
|
116
|
+
const button = container.querySelector('button');
|
|
117
|
+
|
|
118
|
+
button.click();
|
|
119
|
+
flushSync();
|
|
120
|
+
|
|
121
|
+
expect(container.querySelector('.count').textContent).toBe('1');
|
|
122
|
+
expect(container.querySelector('.count2').textContent).toBe('1');
|
|
123
|
+
expect(container.querySelector('.count3').textContent).toBe('1');
|
|
124
|
+
});
|
|
125
|
+
});
|