ripple 0.3.10 → 0.3.11
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/CHANGELOG.md +31 -0
- package/package.json +2 -2
- package/src/compiler/errors.js +1 -1
- package/src/compiler/index.d.ts +3 -1
- package/src/compiler/phases/1-parse/index.js +170 -8
- package/src/compiler/phases/2-analyze/index.js +231 -20
- package/src/compiler/phases/3-transform/client/index.js +169 -77
- package/src/compiler/phases/3-transform/server/index.js +46 -3
- package/src/compiler/types/index.d.ts +19 -2
- package/src/compiler/types/parse.d.ts +1 -1
- package/src/compiler/utils.js +174 -0
- package/src/runtime/index-client.js +14 -4
- package/src/runtime/internal/client/composite.js +2 -2
- package/src/runtime/internal/client/expression.js +64 -2
- package/src/runtime/internal/client/portal.js +1 -1
- package/src/utils/builders.js +30 -0
- package/tests/client/basic/__snapshots__/basic.rendering.test.ripple.snap +1 -0
- package/tests/client/basic/basic.rendering.test.ripple +4 -2
- package/tests/client/composite/composite.render.test.ripple +46 -0
- package/tests/client/return.test.ripple +101 -0
- package/tests/client/tsx.test.ripple +486 -0
- package/tests/hydration/compiled/client/basic.js +8 -24
- package/tests/hydration/compiled/client/composite.js +6 -24
- package/tests/hydration/compiled/client/events.js +9 -54
- package/tests/hydration/compiled/client/for.js +59 -152
- package/tests/hydration/compiled/client/head.js +5 -20
- package/tests/hydration/compiled/client/hmr.js +2 -8
- package/tests/hydration/compiled/client/html.js +59 -226
- package/tests/hydration/compiled/client/if-children.js +6 -22
- package/tests/hydration/compiled/client/mixed-control-flow.js +18 -66
- package/tests/hydration/compiled/client/nested-control-flow.js +92 -368
- package/tests/hydration/compiled/client/portal.js +4 -16
- package/tests/hydration/compiled/client/reactivity.js +7 -40
- package/tests/hydration/compiled/client/return.js +1 -4
- package/tests/hydration/compiled/client/try.js +2 -2
- package/tests/utils/compiler-compat-config.test.js +38 -0
- package/tests/utils/vite-plugin-config.test.js +113 -0
- package/tsconfig.typecheck.json +2 -1
- package/types/index.d.ts +2 -12
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import { flushSync, track } from 'ripple';
|
|
2
|
+
|
|
3
|
+
describe('tsx expression', () => {
|
|
4
|
+
it('renders a basic tsx element', () => {
|
|
5
|
+
component App() {
|
|
6
|
+
const el = <tsx><div>hello world</div></tsx>;
|
|
7
|
+
{el}
|
|
8
|
+
}
|
|
9
|
+
render(App);
|
|
10
|
+
expect(container.textContent).toBe('hello world');
|
|
11
|
+
expect(container.querySelector('div')).toBeTruthy();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('renders a tsx element assigned to a variable', () => {
|
|
15
|
+
component App() {
|
|
16
|
+
const el = <tsx><span class="test">content</span></tsx>;
|
|
17
|
+
{el}
|
|
18
|
+
}
|
|
19
|
+
render(App);
|
|
20
|
+
const span = container.querySelector('span.test');
|
|
21
|
+
expect(span).toBeTruthy();
|
|
22
|
+
expect(span.textContent).toBe('content');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('renders a tsx element with multiple children', () => {
|
|
26
|
+
component App() {
|
|
27
|
+
const el = <tsx><div>first</div><div>second</div></tsx>;
|
|
28
|
+
{el}
|
|
29
|
+
}
|
|
30
|
+
render(App);
|
|
31
|
+
const divs = container.querySelectorAll('div');
|
|
32
|
+
expect(divs.length).toBe(2);
|
|
33
|
+
expect(divs[0].textContent).toBe('first');
|
|
34
|
+
expect(divs[1].textContent).toBe('second');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('renders a tsx element with nested elements', () => {
|
|
38
|
+
component App() {
|
|
39
|
+
const el = <tsx>
|
|
40
|
+
<div class="outer">
|
|
41
|
+
<span class="inner">nested</span>
|
|
42
|
+
</div>
|
|
43
|
+
</tsx>;
|
|
44
|
+
{el}
|
|
45
|
+
}
|
|
46
|
+
render(App);
|
|
47
|
+
const outer = container.querySelector('div.outer');
|
|
48
|
+
expect(outer).toBeTruthy();
|
|
49
|
+
const inner = outer.querySelector('span.inner');
|
|
50
|
+
expect(inner).toBeTruthy();
|
|
51
|
+
expect(inner.textContent).toBe('nested');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('renders a tsx element inline in a parent element', () => {
|
|
55
|
+
component App() {
|
|
56
|
+
const el = <tsx><span>inline</span></tsx>;
|
|
57
|
+
<div class="parent">{el}</div>
|
|
58
|
+
}
|
|
59
|
+
render(App);
|
|
60
|
+
const parent = container.querySelector('div.parent');
|
|
61
|
+
expect(parent).toBeTruthy();
|
|
62
|
+
expect(parent.querySelector('span')).toBeTruthy();
|
|
63
|
+
expect(parent.textContent).toBe('inline');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('renders a tsx element with reactive expressions', () => {
|
|
67
|
+
component App() {
|
|
68
|
+
let &[count] = track(0);
|
|
69
|
+
const el = <tsx>
|
|
70
|
+
<div>
|
|
71
|
+
{'count: ' + count}
|
|
72
|
+
</div>
|
|
73
|
+
</tsx>;
|
|
74
|
+
{el}
|
|
75
|
+
<button onClick={() => count++}>{'increment'}</button>
|
|
76
|
+
}
|
|
77
|
+
render(App);
|
|
78
|
+
expect(container.querySelector('div').textContent).toBe('count: 0');
|
|
79
|
+
|
|
80
|
+
container.querySelector('button').click();
|
|
81
|
+
flushSync();
|
|
82
|
+
expect(container.querySelector('div').textContent).toBe('count: 1');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('conditionally renders tsx elements', () => {
|
|
86
|
+
component App() {
|
|
87
|
+
let &[show] = track(true);
|
|
88
|
+
const el = <tsx><div class="tsx-content">visible</div></tsx>;
|
|
89
|
+
|
|
90
|
+
if (show) {
|
|
91
|
+
{el}
|
|
92
|
+
}
|
|
93
|
+
<button onClick={() => (show = !show)}>{'toggle'}</button>
|
|
94
|
+
}
|
|
95
|
+
render(App);
|
|
96
|
+
expect(container.querySelector('.tsx-content')).toBeTruthy();
|
|
97
|
+
|
|
98
|
+
container.querySelector('button').click();
|
|
99
|
+
flushSync();
|
|
100
|
+
expect(container.querySelector('.tsx-content')).toBeFalsy();
|
|
101
|
+
|
|
102
|
+
container.querySelector('button').click();
|
|
103
|
+
flushSync();
|
|
104
|
+
expect(container.querySelector('.tsx-content')).toBeTruthy();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('renders tsx element passed as children prop', () => {
|
|
108
|
+
component Child(&{ children }: { children: any }) {
|
|
109
|
+
<div class="wrapper">{children}</div>
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
component App() {
|
|
113
|
+
const el = <tsx><span>from tsx</span></tsx>;
|
|
114
|
+
<Child children={el} />
|
|
115
|
+
}
|
|
116
|
+
render(App);
|
|
117
|
+
const wrapper = container.querySelector('.wrapper');
|
|
118
|
+
expect(wrapper).toBeTruthy();
|
|
119
|
+
expect(wrapper.querySelector('span')).toBeTruthy();
|
|
120
|
+
expect(wrapper.textContent).toBe('from tsx');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('renders tsx element with text content only', () => {
|
|
124
|
+
component App() {
|
|
125
|
+
const el = <tsx>just text</tsx>;
|
|
126
|
+
{el}
|
|
127
|
+
}
|
|
128
|
+
render(App);
|
|
129
|
+
expect(container.textContent).toBe('just text');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('renders tsx element with static attributes', () => {
|
|
133
|
+
component App() {
|
|
134
|
+
const el = <tsx>
|
|
135
|
+
<div id="my-id" class="my-class" data-testid="test" aria-label="label">content</div>
|
|
136
|
+
</tsx>;
|
|
137
|
+
{el}
|
|
138
|
+
}
|
|
139
|
+
render(App);
|
|
140
|
+
const div = container.querySelector('div');
|
|
141
|
+
expect(div.id).toBe('my-id');
|
|
142
|
+
expect(div.className).toBe('my-class');
|
|
143
|
+
expect(div.getAttribute('data-testid')).toBe('test');
|
|
144
|
+
expect(div.getAttribute('aria-label')).toBe('label');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('renders tsx element with dynamic attribute values', () => {
|
|
148
|
+
component App() {
|
|
149
|
+
let &[name] = track('initial');
|
|
150
|
+
const el = <tsx><div id={name} class={'cls-' + name}>content</div></tsx>;
|
|
151
|
+
{el}
|
|
152
|
+
<button onClick={() => (name = 'updated')}>{'update'}</button>
|
|
153
|
+
}
|
|
154
|
+
render(App);
|
|
155
|
+
const div = container.querySelector('div');
|
|
156
|
+
expect(div.id).toBe('initial');
|
|
157
|
+
expect(div.className).toBe('cls-initial');
|
|
158
|
+
|
|
159
|
+
container.querySelector('button').click();
|
|
160
|
+
flushSync();
|
|
161
|
+
expect(div.id).toBe('updated');
|
|
162
|
+
expect(div.className).toBe('cls-updated');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('renders tsx element with event handlers', () => {
|
|
166
|
+
component App() {
|
|
167
|
+
let &[clicked] = track(false);
|
|
168
|
+
const el = <tsx>
|
|
169
|
+
<button onClick={() => (clicked = true)}>
|
|
170
|
+
{'click me'}
|
|
171
|
+
</button>
|
|
172
|
+
</tsx>;
|
|
173
|
+
{el}
|
|
174
|
+
<div class="status">{clicked ? 'clicked' : 'not clicked'}</div>
|
|
175
|
+
}
|
|
176
|
+
render(App);
|
|
177
|
+
expect(container.querySelector('.status').textContent).toBe('not clicked');
|
|
178
|
+
|
|
179
|
+
container.querySelector('button').click();
|
|
180
|
+
flushSync();
|
|
181
|
+
expect(container.querySelector('.status').textContent).toBe('clicked');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('renders tsx element with boolean attributes', () => {
|
|
185
|
+
component App() {
|
|
186
|
+
let &[isDisabled] = track(true);
|
|
187
|
+
const el = <tsx>
|
|
188
|
+
<button disabled={isDisabled}>
|
|
189
|
+
{'btn'}
|
|
190
|
+
</button>
|
|
191
|
+
</tsx>;
|
|
192
|
+
{el}
|
|
193
|
+
<button class="toggle" onClick={() => (isDisabled = !isDisabled)}>{'toggle'}</button>
|
|
194
|
+
}
|
|
195
|
+
render(App);
|
|
196
|
+
expect(container.querySelector('button:not(.toggle)').disabled).toBe(true);
|
|
197
|
+
|
|
198
|
+
container.querySelector('.toggle').click();
|
|
199
|
+
flushSync();
|
|
200
|
+
expect(container.querySelector('button:not(.toggle)').disabled).toBe(false);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('renders tsx element with style attribute', () => {
|
|
204
|
+
component App() {
|
|
205
|
+
let &[color] = track('red');
|
|
206
|
+
const el = <tsx><div style={'color: ' + color}>styled</div></tsx>;
|
|
207
|
+
{el}
|
|
208
|
+
<button onClick={() => (color = 'blue')}>{'change color'}</button>
|
|
209
|
+
}
|
|
210
|
+
render(App);
|
|
211
|
+
expect(container.querySelector('div').style.color).toBe('red');
|
|
212
|
+
|
|
213
|
+
container.querySelector('button').click();
|
|
214
|
+
flushSync();
|
|
215
|
+
expect(container.querySelector('div').style.color).toBe('blue');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('renders tsx element with multiple dynamic attributes', () => {
|
|
219
|
+
component App() {
|
|
220
|
+
let &[index] = track(0);
|
|
221
|
+
const el = <tsx>
|
|
222
|
+
<div id={'item-' + index} class={'item pos-' + index} data-index={index} title={'Item ' +
|
|
223
|
+
index}>
|
|
224
|
+
{'Item ' + index}
|
|
225
|
+
</div>
|
|
226
|
+
</tsx>;
|
|
227
|
+
{el}
|
|
228
|
+
<button onClick={() => index++}>{'next'}</button>
|
|
229
|
+
}
|
|
230
|
+
render(App);
|
|
231
|
+
const div = container.querySelector('div');
|
|
232
|
+
expect(div.id).toBe('item-0');
|
|
233
|
+
expect(div.className).toBe('item pos-0');
|
|
234
|
+
expect(div.getAttribute('data-index')).toBe('0');
|
|
235
|
+
expect(div.title).toBe('Item 0');
|
|
236
|
+
|
|
237
|
+
container.querySelector('button').click();
|
|
238
|
+
flushSync();
|
|
239
|
+
expect(div.id).toBe('item-1');
|
|
240
|
+
expect(div.className).toBe('item pos-1');
|
|
241
|
+
expect(div.getAttribute('data-index')).toBe('1');
|
|
242
|
+
expect(div.title).toBe('Item 1');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('renders tsx passed directly as component prop', () => {
|
|
246
|
+
component Wrapper(&{ content }: { content: any }) {
|
|
247
|
+
<div class="wrapper">{content}</div>
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
component App() {
|
|
251
|
+
<Wrapper content={<tsx><span class="inner">direct prop</span></tsx>} />
|
|
252
|
+
}
|
|
253
|
+
render(App);
|
|
254
|
+
const wrapper = container.querySelector('.wrapper');
|
|
255
|
+
expect(wrapper).toBeTruthy();
|
|
256
|
+
expect(wrapper.querySelector('.inner')).toBeTruthy();
|
|
257
|
+
expect(wrapper.textContent).toBe('direct prop');
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('renders tsx passed directly as children prop', () => {
|
|
261
|
+
component Card(&{ title, children }: { title: any; children: any }) {
|
|
262
|
+
<div class="card">
|
|
263
|
+
<h2 class="card-title">{title}</h2>
|
|
264
|
+
<div class="card-body">{children}</div>
|
|
265
|
+
</div>
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
component App() {
|
|
269
|
+
<Card
|
|
270
|
+
title={<tsx><span class="bold">Title</span></tsx>}
|
|
271
|
+
children={<tsx><p>Card content here</p></tsx>}
|
|
272
|
+
/>
|
|
273
|
+
}
|
|
274
|
+
render(App);
|
|
275
|
+
const card = container.querySelector('.card');
|
|
276
|
+
expect(card.querySelector('.card-title .bold').textContent).toBe('Title');
|
|
277
|
+
expect(card.querySelector('.card-body p').textContent).toBe('Card content here');
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('renders tsx from function defined outside component', () => {
|
|
281
|
+
function createBadge(label: string) {
|
|
282
|
+
return <tsx>
|
|
283
|
+
<span class="badge">
|
|
284
|
+
{label}
|
|
285
|
+
</span>
|
|
286
|
+
</tsx>;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
component App() {
|
|
290
|
+
const badge = createBadge('New');
|
|
291
|
+
{badge}
|
|
292
|
+
}
|
|
293
|
+
render(App);
|
|
294
|
+
expect(container.querySelector('.badge')).toBeTruthy();
|
|
295
|
+
expect(container.querySelector('.badge').textContent).toBe('New');
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('renders tsx from function with multiple elements', () => {
|
|
299
|
+
function createListItem(item: string) {
|
|
300
|
+
return <tsx>
|
|
301
|
+
<li class="list-item">
|
|
302
|
+
{item}
|
|
303
|
+
</li>
|
|
304
|
+
</tsx>;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
component App() {
|
|
308
|
+
const items = ['Apple', 'Banana', 'Cherry'];
|
|
309
|
+
<ul>
|
|
310
|
+
for (const item of items) {
|
|
311
|
+
{createListItem(item)}
|
|
312
|
+
}
|
|
313
|
+
</ul>
|
|
314
|
+
}
|
|
315
|
+
render(App);
|
|
316
|
+
const listItems = container.querySelectorAll('.list-item');
|
|
317
|
+
expect(listItems.length).toBe(3);
|
|
318
|
+
expect(listItems[0].textContent).toBe('Apple');
|
|
319
|
+
expect(listItems[1].textContent).toBe('Banana');
|
|
320
|
+
expect(listItems[2].textContent).toBe('Cherry');
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('renders tsx from factory function passed to component', () => {
|
|
324
|
+
component List(&{ renderItem, items }: { renderItem: (item: string) => any; items: string[] }) {
|
|
325
|
+
<ul class="list">
|
|
326
|
+
for (const item of items) {
|
|
327
|
+
{renderItem(item)}
|
|
328
|
+
}
|
|
329
|
+
</ul>
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function itemRenderer(item: string) {
|
|
333
|
+
return <tsx>
|
|
334
|
+
<li>
|
|
335
|
+
<span class="item-content">
|
|
336
|
+
{item}
|
|
337
|
+
</span>
|
|
338
|
+
</li>
|
|
339
|
+
</tsx>;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
component App() {
|
|
343
|
+
<List items={['One', 'Two', 'Three']} renderItem={itemRenderer} />
|
|
344
|
+
}
|
|
345
|
+
render(App);
|
|
346
|
+
const items = container.querySelectorAll('.item-content');
|
|
347
|
+
expect(items.length).toBe(3);
|
|
348
|
+
expect(items[0].textContent).toBe('One');
|
|
349
|
+
expect(items[1].textContent).toBe('Two');
|
|
350
|
+
expect(items[2].textContent).toBe('Three');
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('renders tsx from function with reactive state', () => {
|
|
354
|
+
function createCounter(label: string, getCount: () => number) {
|
|
355
|
+
return <tsx>
|
|
356
|
+
<div class="counter-display">
|
|
357
|
+
<span class="label">
|
|
358
|
+
{label}
|
|
359
|
+
</span>
|
|
360
|
+
<span class="count">
|
|
361
|
+
{getCount()}
|
|
362
|
+
</span>
|
|
363
|
+
</div>
|
|
364
|
+
</tsx>;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
component App() {
|
|
368
|
+
let &[count] = track(0);
|
|
369
|
+
const counterElement = createCounter('Count:', () => count);
|
|
370
|
+
{counterElement}
|
|
371
|
+
<button onClick={() => count++}>{'increment'}</button>
|
|
372
|
+
}
|
|
373
|
+
render(App);
|
|
374
|
+
expect(container.querySelector('.label').textContent).toBe('Count:');
|
|
375
|
+
expect(container.querySelector('.count').textContent).toBe('0');
|
|
376
|
+
|
|
377
|
+
container.querySelector('button').click();
|
|
378
|
+
flushSync();
|
|
379
|
+
expect(container.querySelector('.count').textContent).toBe('1');
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('renders nested tsx from multiple functions', () => {
|
|
383
|
+
function createIcon(name: string) {
|
|
384
|
+
return <tsx><i class={'icon icon-' + name}></i></tsx>;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function createButton(icon: string, label: string) {
|
|
388
|
+
return <tsx>
|
|
389
|
+
<button class="icon-button">
|
|
390
|
+
{createIcon(icon)}
|
|
391
|
+
<span class="btn-label">
|
|
392
|
+
{label}
|
|
393
|
+
</span>
|
|
394
|
+
</button>
|
|
395
|
+
</tsx>;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
component App() {
|
|
399
|
+
const btn = createButton('save', 'Save');
|
|
400
|
+
{btn}
|
|
401
|
+
}
|
|
402
|
+
render(App);
|
|
403
|
+
const button = container.querySelector('.icon-button');
|
|
404
|
+
expect(button).toBeTruthy();
|
|
405
|
+
expect(button.querySelector('.icon-save')).toBeTruthy();
|
|
406
|
+
expect(button.querySelector('.btn-label').textContent).toBe('Save');
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('renders tsx as prop with fallback in component', () => {
|
|
410
|
+
component Alert(&{ icon, message }: { icon?: any; message: string }) {
|
|
411
|
+
<div class="alert">
|
|
412
|
+
{icon}
|
|
413
|
+
<span class="message">{message}</span>
|
|
414
|
+
</div>
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
component App() {
|
|
418
|
+
<Alert message="No icon" />
|
|
419
|
+
<Alert icon={<tsx><span class="custom-icon">✓</span></tsx>} message="Custom icon" />
|
|
420
|
+
}
|
|
421
|
+
render(App);
|
|
422
|
+
const alerts = container.querySelectorAll('.alert');
|
|
423
|
+
expect(alerts[0].querySelector('.message').textContent).toBe('No icon');
|
|
424
|
+
expect(alerts[1].querySelector('.custom-icon')).toBeTruthy();
|
|
425
|
+
expect(alerts[1].querySelector('.message').textContent).toBe('Custom icon');
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('renders tsx stored in array via function', () => {
|
|
429
|
+
function createItem(className: string, content: string) {
|
|
430
|
+
return <tsx>
|
|
431
|
+
<div class={className}>
|
|
432
|
+
{content}
|
|
433
|
+
</div>
|
|
434
|
+
</tsx>;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
component App() {
|
|
438
|
+
const items = [
|
|
439
|
+
{ className: 'item-a', content: 'A' },
|
|
440
|
+
{ className: 'item-b', content: 'B' },
|
|
441
|
+
{ className: 'item-c', content: 'C' },
|
|
442
|
+
];
|
|
443
|
+
|
|
444
|
+
<div class="container">
|
|
445
|
+
for (const item of items) {
|
|
446
|
+
{createItem(item.className, item.content)}
|
|
447
|
+
}
|
|
448
|
+
</div>
|
|
449
|
+
}
|
|
450
|
+
render(App);
|
|
451
|
+
const container_el = container.querySelector('.container');
|
|
452
|
+
expect(container_el.querySelector('.item-a').textContent).toBe('A');
|
|
453
|
+
expect(container_el.querySelector('.item-b').textContent).toBe('B');
|
|
454
|
+
expect(container_el.querySelector('.item-c').textContent).toBe('C');
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it('renders tsx conditionally from function', () => {
|
|
458
|
+
function createContent(type: string) {
|
|
459
|
+
if (type === 'success') {
|
|
460
|
+
return <tsx><div class="success">Success!</div></tsx>;
|
|
461
|
+
} else if (type === 'error') {
|
|
462
|
+
return <tsx><div class="error">Error!</div></tsx>;
|
|
463
|
+
}
|
|
464
|
+
return <tsx><div class="default">Default</div></tsx>;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
component App() {
|
|
468
|
+
let &[status] = track('default');
|
|
469
|
+
{createContent(status)}
|
|
470
|
+
<button class="set-success" onClick={() => (status = 'success')}>{'Success'}</button>
|
|
471
|
+
<button class="set-error" onClick={() => (status = 'error')}>{'Error'}</button>
|
|
472
|
+
}
|
|
473
|
+
render(App);
|
|
474
|
+
expect(container.querySelector('.default')).toBeTruthy();
|
|
475
|
+
|
|
476
|
+
container.querySelector('.set-success').click();
|
|
477
|
+
flushSync();
|
|
478
|
+
expect(container.querySelector('.success')).toBeTruthy();
|
|
479
|
+
expect(container.querySelector('.default')).toBeFalsy();
|
|
480
|
+
|
|
481
|
+
container.querySelector('.set-error').click();
|
|
482
|
+
flushSync();
|
|
483
|
+
expect(container.querySelector('.error')).toBeTruthy();
|
|
484
|
+
expect(container.querySelector('.success')).toBeFalsy();
|
|
485
|
+
});
|
|
486
|
+
});
|
|
@@ -135,13 +135,10 @@ export function Greeting(__anchor, props, __block) {
|
|
|
135
135
|
{
|
|
136
136
|
var expression = _$_.child(div_6, true);
|
|
137
137
|
|
|
138
|
+
_$_.expression(expression, () => 'Hello ' + _$_.with_scope(__block, () => String(props.name)));
|
|
138
139
|
_$_.pop(div_6);
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
_$_.render(() => {
|
|
142
|
-
_$_.set_text(expression, 'Hello ' + _$_.with_scope(__block, () => String(props.name)));
|
|
143
|
-
});
|
|
144
|
-
|
|
145
142
|
_$_.append(__anchor, div_6);
|
|
146
143
|
_$_.pop_component();
|
|
147
144
|
}
|
|
@@ -168,7 +165,7 @@ export function ExpressionContent(__anchor, _, __block) {
|
|
|
168
165
|
{
|
|
169
166
|
var expression_1 = _$_.child(div_7, true);
|
|
170
167
|
|
|
171
|
-
expression_1
|
|
168
|
+
_$_.expression(expression_1, () => value);
|
|
172
169
|
_$_.pop(div_7);
|
|
173
170
|
}
|
|
174
171
|
|
|
@@ -177,15 +174,11 @@ export function ExpressionContent(__anchor, _, __block) {
|
|
|
177
174
|
{
|
|
178
175
|
var expression_2 = _$_.child(span_2, true);
|
|
179
176
|
|
|
177
|
+
_$_.expression(expression_2, () => _$_.with_scope(__block, () => label.toUpperCase()));
|
|
180
178
|
_$_.pop(span_2);
|
|
181
179
|
}
|
|
182
180
|
|
|
183
181
|
_$_.next();
|
|
184
|
-
|
|
185
|
-
_$_.render(() => {
|
|
186
|
-
_$_.set_text(expression_2, _$_.with_scope(__block, () => label.toUpperCase()));
|
|
187
|
-
});
|
|
188
|
-
|
|
189
182
|
_$_.append(__anchor, fragment_4, true);
|
|
190
183
|
_$_.pop_component();
|
|
191
184
|
}
|
|
@@ -198,13 +191,10 @@ function TextProp(__anchor, __props, __block) {
|
|
|
198
191
|
{
|
|
199
192
|
var expression_3 = _$_.child(div_8);
|
|
200
193
|
|
|
194
|
+
_$_.expression(expression_3, () => __props.children);
|
|
201
195
|
_$_.pop(div_8);
|
|
202
196
|
}
|
|
203
197
|
|
|
204
|
-
_$_.render(() => {
|
|
205
|
-
_$_.expression(expression_3, () => __props.children);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
198
|
_$_.append(__anchor, div_8);
|
|
209
199
|
_$_.pop_component();
|
|
210
200
|
}
|
|
@@ -257,7 +247,7 @@ export function StaticChildWithSiblings(__anchor, _, __block) {
|
|
|
257
247
|
{
|
|
258
248
|
var expression_4 = _$_.child(span_3, true);
|
|
259
249
|
|
|
260
|
-
expression_4
|
|
250
|
+
_$_.expression(expression_4, () => foo);
|
|
261
251
|
_$_.pop(span_3);
|
|
262
252
|
}
|
|
263
253
|
|
|
@@ -266,7 +256,7 @@ export function StaticChildWithSiblings(__anchor, _, __block) {
|
|
|
266
256
|
{
|
|
267
257
|
var expression_5 = _$_.child(span_4, true);
|
|
268
258
|
|
|
269
|
-
expression_5
|
|
259
|
+
_$_.expression(expression_5, () => foo);
|
|
270
260
|
_$_.pop(span_4);
|
|
271
261
|
}
|
|
272
262
|
|
|
@@ -325,14 +315,11 @@ function Layout(__anchor, { children }, __block) {
|
|
|
325
315
|
{
|
|
326
316
|
var expression_6 = _$_.child(div_10);
|
|
327
317
|
|
|
318
|
+
_$_.expression(expression_6, () => children);
|
|
328
319
|
_$_.pop(div_10);
|
|
329
320
|
}
|
|
330
321
|
}
|
|
331
322
|
|
|
332
|
-
_$_.render(() => {
|
|
333
|
-
_$_.expression(expression_6, () => children);
|
|
334
|
-
});
|
|
335
|
-
|
|
336
323
|
_$_.append(__anchor, main_1);
|
|
337
324
|
_$_.pop_component();
|
|
338
325
|
}
|
|
@@ -355,9 +342,7 @@ export function WebsiteIndex(__anchor, _, __block) {
|
|
|
355
342
|
Layout(
|
|
356
343
|
node_7,
|
|
357
344
|
{
|
|
358
|
-
children: _$_.ripple_element(function render_children(__anchor,
|
|
359
|
-
_$_.push_component();
|
|
360
|
-
|
|
345
|
+
children: _$_.ripple_element(function render_children(__anchor, __block) {
|
|
361
346
|
var fragment_10 = root_22();
|
|
362
347
|
var node_8 = _$_.first_child_frag(fragment_10);
|
|
363
348
|
|
|
@@ -375,7 +360,6 @@ export function WebsiteIndex(__anchor, _, __block) {
|
|
|
375
360
|
|
|
376
361
|
Actions(node_11, { playgroundVisible: false }, _$_.active_block);
|
|
377
362
|
_$_.append(__anchor, fragment_10);
|
|
378
|
-
_$_.pop_component();
|
|
379
363
|
})
|
|
380
364
|
},
|
|
381
365
|
_$_.active_block
|
|
@@ -23,13 +23,10 @@ export function Layout(__anchor, __props, __block) {
|
|
|
23
23
|
{
|
|
24
24
|
var expression = _$_.child(div_1);
|
|
25
25
|
|
|
26
|
+
_$_.expression(expression, () => __props.children);
|
|
26
27
|
_$_.pop(div_1);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
_$_.render(() => {
|
|
30
|
-
_$_.expression(expression, () => __props.children);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
30
|
_$_.append(__anchor, div_1);
|
|
34
31
|
_$_.pop_component();
|
|
35
32
|
}
|
|
@@ -43,13 +40,10 @@ export function TextWrappedLayout(__anchor, __props, __block) {
|
|
|
43
40
|
var text = _$_.child(div_2);
|
|
44
41
|
var expression_1 = _$_.sibling(text);
|
|
45
42
|
|
|
43
|
+
_$_.expression(expression_1, () => __props.children);
|
|
46
44
|
_$_.pop(div_2);
|
|
47
45
|
}
|
|
48
46
|
|
|
49
|
-
_$_.render(() => {
|
|
50
|
-
_$_.expression(expression_1, () => __props.children);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
47
|
_$_.append(__anchor, div_2);
|
|
54
48
|
_$_.pop_component();
|
|
55
49
|
}
|
|
@@ -93,15 +87,12 @@ export function LayoutWithSingleChild(__anchor, _, __block) {
|
|
|
93
87
|
Layout(
|
|
94
88
|
node_1,
|
|
95
89
|
{
|
|
96
|
-
children: _$_.ripple_element(function render_children(__anchor,
|
|
97
|
-
_$_.push_component();
|
|
98
|
-
|
|
90
|
+
children: _$_.ripple_element(function render_children(__anchor, __block) {
|
|
99
91
|
var fragment_3 = root_6();
|
|
100
92
|
var node_2 = _$_.first_child_frag(fragment_3);
|
|
101
93
|
|
|
102
94
|
SingleChild(node_2, {}, _$_.active_block);
|
|
103
95
|
_$_.append(__anchor, fragment_3);
|
|
104
|
-
_$_.pop_component();
|
|
105
96
|
})
|
|
106
97
|
},
|
|
107
98
|
_$_.active_block
|
|
@@ -120,15 +111,12 @@ export function LayoutWithMultipleChildren(__anchor, _, __block) {
|
|
|
120
111
|
Layout(
|
|
121
112
|
node_3,
|
|
122
113
|
{
|
|
123
|
-
children: _$_.ripple_element(function render_children(__anchor,
|
|
124
|
-
_$_.push_component();
|
|
125
|
-
|
|
114
|
+
children: _$_.ripple_element(function render_children(__anchor, __block) {
|
|
126
115
|
var fragment_5 = root_8();
|
|
127
116
|
var node_4 = _$_.first_child_frag(fragment_5);
|
|
128
117
|
|
|
129
118
|
SingleChild(node_4, {}, _$_.active_block);
|
|
130
119
|
_$_.append(__anchor, fragment_5);
|
|
131
|
-
_$_.pop_component();
|
|
132
120
|
})
|
|
133
121
|
},
|
|
134
122
|
_$_.active_block
|
|
@@ -147,15 +135,12 @@ export function LayoutWithMultiRootChild(__anchor, _, __block) {
|
|
|
147
135
|
Layout(
|
|
148
136
|
node_5,
|
|
149
137
|
{
|
|
150
|
-
children: _$_.ripple_element(function render_children(__anchor,
|
|
151
|
-
_$_.push_component();
|
|
152
|
-
|
|
138
|
+
children: _$_.ripple_element(function render_children(__anchor, __block) {
|
|
153
139
|
var fragment_7 = root_10();
|
|
154
140
|
var node_6 = _$_.first_child_frag(fragment_7);
|
|
155
141
|
|
|
156
142
|
MultiRootChild(node_6, {}, _$_.active_block);
|
|
157
143
|
_$_.append(__anchor, fragment_7);
|
|
158
|
-
_$_.pop_component();
|
|
159
144
|
})
|
|
160
145
|
},
|
|
161
146
|
_$_.active_block
|
|
@@ -174,15 +159,12 @@ export function LayoutWithTextAroundChildren(__anchor, _, __block) {
|
|
|
174
159
|
TextWrappedLayout(
|
|
175
160
|
node_7,
|
|
176
161
|
{
|
|
177
|
-
children: _$_.ripple_element(function render_children(__anchor,
|
|
178
|
-
_$_.push_component();
|
|
179
|
-
|
|
162
|
+
children: _$_.ripple_element(function render_children(__anchor, __block) {
|
|
180
163
|
var fragment_9 = root_12();
|
|
181
164
|
var node_8 = _$_.first_child_frag(fragment_9);
|
|
182
165
|
|
|
183
166
|
SingleChild(node_8, {}, _$_.active_block);
|
|
184
167
|
_$_.append(__anchor, fragment_9);
|
|
185
|
-
_$_.pop_component();
|
|
186
168
|
})
|
|
187
169
|
},
|
|
188
170
|
_$_.active_block
|