ripple 0.2.133 → 0.2.135
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 +2 -2
- package/src/compiler/phases/1-parse/index.js +286 -55
- package/src/compiler/phases/2-analyze/index.js +35 -1
- package/src/compiler/phases/3-transform/client/index.js +224 -60
- package/src/runtime/index-client.js +3 -3
- package/src/runtime/internal/client/blocks.js +26 -4
- package/src/runtime/internal/client/compat.js +37 -5
- package/src/runtime/internal/client/types.d.ts +10 -0
- package/src/utils/builders.js +17 -0
- package/tests/client/basic/basic.components.test.ripple +30 -20
- package/tests/client/compiler/compiler.basic.test.ripple +93 -64
- package/tests/client/compiler/compiler.tracked-access.test.ripple +108 -0
- package/tests/client/dynamic-elements.test.ripple +120 -1
|
@@ -3,7 +3,7 @@ import { track, flushSync } from 'ripple';
|
|
|
3
3
|
describe('basic client > components & composition', () => {
|
|
4
4
|
it('renders with component composition and children', () => {
|
|
5
5
|
component Card(props) {
|
|
6
|
-
<div class=
|
|
6
|
+
<div class="card">
|
|
7
7
|
<props.children />
|
|
8
8
|
</div>
|
|
9
9
|
}
|
|
@@ -27,16 +27,14 @@ describe('basic client > components & composition', () => {
|
|
|
27
27
|
|
|
28
28
|
it('renders with nested components and prop passing', () => {
|
|
29
29
|
component Button(props) {
|
|
30
|
-
<button class={props.variant} onClick={props.onClick}>
|
|
31
|
-
{props.label}
|
|
32
|
-
</button>
|
|
30
|
+
<button class={props.variant} onClick={props.onClick}>{props.label}</button>
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
component Card(props) {
|
|
36
|
-
<div class=
|
|
34
|
+
<div class="card">
|
|
37
35
|
<h3>{props.title}</h3>
|
|
38
36
|
<p>{props.content}</p>
|
|
39
|
-
<Button variant=
|
|
37
|
+
<Button variant="primary" label={props.buttonText} onClick={props.onAction} />
|
|
40
38
|
</div>
|
|
41
39
|
}
|
|
42
40
|
|
|
@@ -44,12 +42,12 @@ describe('basic client > components & composition', () => {
|
|
|
44
42
|
let clicked = track(false);
|
|
45
43
|
|
|
46
44
|
<Card
|
|
47
|
-
title=
|
|
48
|
-
content=
|
|
49
|
-
buttonText=
|
|
45
|
+
title="Test Card"
|
|
46
|
+
content="This is a test card"
|
|
47
|
+
buttonText="Click me"
|
|
50
48
|
onAction={() => @clicked = true}
|
|
51
49
|
/>
|
|
52
|
-
<div class=
|
|
50
|
+
<div class="status">{@clicked ? 'Clicked' : 'Not clicked'}</div>
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
render(Basic);
|
|
@@ -74,8 +72,8 @@ describe('basic client > components & composition', () => {
|
|
|
74
72
|
|
|
75
73
|
it('renders with reactive component props', () => {
|
|
76
74
|
component ChildComponent(props) {
|
|
77
|
-
<div class=
|
|
78
|
-
<div class=
|
|
75
|
+
<div class="child-content">{props.@text}</div>
|
|
76
|
+
<div class="child-count">{props.@count}</div>
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
component Basic() {
|
|
@@ -83,10 +81,14 @@ describe('basic client > components & composition', () => {
|
|
|
83
81
|
let number = track(1);
|
|
84
82
|
|
|
85
83
|
<ChildComponent text={message} count={number} />
|
|
86
|
-
<button
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
<button
|
|
85
|
+
onClick={() => {
|
|
86
|
+
@message = @message === 'Hello' ? 'Goodbye' : 'Hello';
|
|
87
|
+
@number++;
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
{'Update Props'}
|
|
91
|
+
</button>
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
render(Basic);
|
|
@@ -139,7 +141,9 @@ describe('basic client > components & composition', () => {
|
|
|
139
141
|
@hasError = true;
|
|
140
142
|
}
|
|
141
143
|
}}
|
|
142
|
-
>
|
|
144
|
+
>
|
|
145
|
+
{'Nonexistent'}
|
|
146
|
+
</button>
|
|
143
147
|
<button
|
|
144
148
|
onClick={() => {
|
|
145
149
|
@hasError = false;
|
|
@@ -149,7 +153,9 @@ describe('basic client > components & composition', () => {
|
|
|
149
153
|
@hasError = true;
|
|
150
154
|
}
|
|
151
155
|
}}
|
|
152
|
-
>
|
|
156
|
+
>
|
|
157
|
+
{'Nonexistent chaining'}
|
|
158
|
+
</button>
|
|
153
159
|
<button
|
|
154
160
|
onClick={() => {
|
|
155
161
|
@hasError = false;
|
|
@@ -159,7 +165,9 @@ describe('basic client > components & composition', () => {
|
|
|
159
165
|
@hasError = true;
|
|
160
166
|
}
|
|
161
167
|
}}
|
|
162
|
-
>
|
|
168
|
+
>
|
|
169
|
+
{'Object null'}
|
|
170
|
+
</button>
|
|
163
171
|
<button
|
|
164
172
|
onClick={() => {
|
|
165
173
|
@hasError = false;
|
|
@@ -169,7 +177,9 @@ describe('basic client > components & composition', () => {
|
|
|
169
177
|
@hasError = true;
|
|
170
178
|
}
|
|
171
179
|
}}
|
|
172
|
-
>
|
|
180
|
+
>
|
|
181
|
+
{'Object null chained'}
|
|
182
|
+
</button>
|
|
173
183
|
<button onClick={() => obj.arr[obj.arr.length - 1]()}>{'BinaryExpression prop'}</button>
|
|
174
184
|
|
|
175
185
|
<span>{obj.@count}</span>
|
|
@@ -36,19 +36,19 @@ describe('compiler > basics', () => {
|
|
|
36
36
|
expect(ast.body[0].declaration.css.source).toEqual(style3);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
it('renders without crashing', () => {
|
|
40
40
|
component App() {
|
|
41
41
|
let foo;
|
|
42
42
|
let bar;
|
|
43
43
|
let baz;
|
|
44
44
|
|
|
45
45
|
foo = {};
|
|
46
|
-
foo = {
|
|
46
|
+
foo = { test: 0 };
|
|
47
47
|
foo['abc'] = 123;
|
|
48
48
|
|
|
49
|
-
bar = {
|
|
49
|
+
bar = { def: 456 };
|
|
50
50
|
|
|
51
|
-
baz = {
|
|
51
|
+
baz = { ghi: 789 };
|
|
52
52
|
baz['jkl'] = 987;
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -56,18 +56,18 @@ describe('compiler > basics', () => {
|
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
it('renders without crashing using < character', () => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
component App() {
|
|
60
|
+
function bar() {
|
|
61
|
+
for (let i = 0; i < 10; i++) {
|
|
62
|
+
// do nothing
|
|
63
|
+
}
|
|
64
|
+
const x = 1 < 1;
|
|
63
65
|
}
|
|
64
|
-
const x = 1 < 1;
|
|
65
|
-
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
let x = 5 < 10;
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
<div>{x}</div>
|
|
70
|
+
}
|
|
71
71
|
|
|
72
72
|
render(App);
|
|
73
73
|
});
|
|
@@ -97,28 +97,28 @@ describe('compiler > basics', () => {
|
|
|
97
97
|
it('renders without crashing using mapped types', () => {
|
|
98
98
|
component App() {
|
|
99
99
|
type RecordKey = 'test';
|
|
100
|
-
type RecordValue = { a: string
|
|
100
|
+
type RecordValue = { a: string; b: number };
|
|
101
101
|
|
|
102
102
|
const config: Record<RecordKey, RecordValue> = {
|
|
103
103
|
test: {
|
|
104
104
|
a: 'test',
|
|
105
|
-
b: 1
|
|
105
|
+
b: 1,
|
|
106
106
|
},
|
|
107
107
|
};
|
|
108
108
|
|
|
109
109
|
const config2: { [key in RecordKey]: RecordValue } = {
|
|
110
110
|
test: {
|
|
111
111
|
a: 'test2',
|
|
112
|
-
b: 2
|
|
113
|
-
}
|
|
114
|
-
}
|
|
112
|
+
b: 2,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
115
|
|
|
116
116
|
const config3: { [key: RecordKey]: RecordValue } = {
|
|
117
117
|
test: {
|
|
118
118
|
a: 'test3',
|
|
119
|
-
b: 3
|
|
120
|
-
}
|
|
121
|
-
}
|
|
119
|
+
b: 3,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
render(App);
|
|
@@ -130,11 +130,14 @@ describe('compiler > basics', () => {
|
|
|
130
130
|
const { a, b, ...rest } = obj;
|
|
131
131
|
|
|
132
132
|
<div>
|
|
133
|
-
{'a '}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
{'a '}
|
|
134
|
+
{a}
|
|
135
|
+
{'b '}
|
|
136
|
+
{b}
|
|
137
|
+
{'rest '}
|
|
138
|
+
{JSON.stringify(rest)}
|
|
139
|
+
|
|
140
|
+
<div />
|
|
138
141
|
</div>
|
|
139
142
|
}
|
|
140
143
|
|
|
@@ -146,11 +149,14 @@ describe('compiler > basics', () => {
|
|
|
146
149
|
const obj = { a: 1, b: 2, c: 3 };
|
|
147
150
|
const { a, b, ...rest } = obj;
|
|
148
151
|
|
|
149
|
-
{'a '}
|
|
152
|
+
{'a '}
|
|
153
|
+
{a}
|
|
154
|
+
{'b '}
|
|
155
|
+
{b}
|
|
156
|
+
{'rest '}
|
|
157
|
+
{JSON.stringify(rest)}
|
|
150
158
|
|
|
151
|
-
<div
|
|
152
|
-
|
|
153
|
-
</div>
|
|
159
|
+
<div />
|
|
154
160
|
}
|
|
155
161
|
|
|
156
162
|
render(App);
|
|
@@ -163,10 +169,10 @@ describe('compiler > basics', () => {
|
|
|
163
169
|
|
|
164
170
|
function Wrapper() {
|
|
165
171
|
return {
|
|
166
|
-
unwrap: function
|
|
172
|
+
unwrap: function() {
|
|
167
173
|
return null as unknown as T;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
174
|
+
},
|
|
175
|
+
};
|
|
170
176
|
}
|
|
171
177
|
|
|
172
178
|
component App() {
|
|
@@ -180,11 +186,11 @@ describe('compiler > basics', () => {
|
|
|
180
186
|
value: T;
|
|
181
187
|
}
|
|
182
188
|
|
|
183
|
-
class Box
|
|
189
|
+
class Box {
|
|
184
190
|
value: T;
|
|
185
191
|
|
|
186
|
-
method
|
|
187
|
-
|
|
192
|
+
method(): T {
|
|
193
|
+
return this.value;
|
|
188
194
|
}
|
|
189
195
|
}
|
|
190
196
|
|
|
@@ -213,7 +219,7 @@ describe('compiler > basics', () => {
|
|
|
213
219
|
const result = compile(source, 'test.ripple', { mode: 'client' });
|
|
214
220
|
});
|
|
215
221
|
|
|
216
|
-
it(
|
|
222
|
+
it('doesn\'t add duplicate imports when encountering shorthand syntax', () => {
|
|
217
223
|
const source = `
|
|
218
224
|
import {
|
|
219
225
|
TrackedArray,
|
|
@@ -234,14 +240,14 @@ component App() {
|
|
|
234
240
|
`;
|
|
235
241
|
const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
236
242
|
|
|
237
|
-
expect(count_occurrences(result,
|
|
238
|
-
expect(count_occurrences(result,
|
|
239
|
-
expect(count_occurrences(result,
|
|
240
|
-
expect(count_occurrences(result,
|
|
241
|
-
expect(count_occurrences(result,
|
|
243
|
+
expect(count_occurrences(result, 'TrackedArray')).toBe(2);
|
|
244
|
+
expect(count_occurrences(result, 'TrackedObject')).toBe(2);
|
|
245
|
+
expect(count_occurrences(result, 'TrackedSet')).toBe(2);
|
|
246
|
+
expect(count_occurrences(result, 'TrackedMap')).toBe(2);
|
|
247
|
+
expect(count_occurrences(result, 'createRefKey')).toBe(2);
|
|
242
248
|
});
|
|
243
249
|
|
|
244
|
-
it(
|
|
250
|
+
it('doesn\'t add duplicate imports for renamed imports when encountering shorthand syntax', () => {
|
|
245
251
|
const source = `
|
|
246
252
|
import {
|
|
247
253
|
TrackedArray as TA,
|
|
@@ -262,16 +268,16 @@ component App() {
|
|
|
262
268
|
`;
|
|
263
269
|
const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
264
270
|
|
|
265
|
-
expect(count_occurrences(result,
|
|
266
|
-
expect(count_occurrences(result,
|
|
267
|
-
expect(count_occurrences(result,
|
|
268
|
-
expect(count_occurrences(result,
|
|
269
|
-
expect(count_occurrences(result,
|
|
270
|
-
expect(count_occurrences(result,
|
|
271
|
-
expect(count_occurrences(result,
|
|
272
|
-
expect(count_occurrences(result,
|
|
273
|
-
expect(count_occurrences(result,
|
|
274
|
-
expect(count_occurrences(result,
|
|
271
|
+
expect(count_occurrences(result, 'TrackedArray')).toBe(1);
|
|
272
|
+
expect(count_occurrences(result, 'TA')).toBe(2);
|
|
273
|
+
expect(count_occurrences(result, 'TrackedObject')).toBe(1);
|
|
274
|
+
expect(count_occurrences(result, 'TO')).toBe(2);
|
|
275
|
+
expect(count_occurrences(result, 'TrackedSet')).toBe(1);
|
|
276
|
+
expect(count_occurrences(result, 'TS')).toBe(2);
|
|
277
|
+
expect(count_occurrences(result, 'TrackedMap')).toBe(1);
|
|
278
|
+
expect(count_occurrences(result, 'TM')).toBe(2);
|
|
279
|
+
expect(count_occurrences(result, 'createRefKey')).toBe(1);
|
|
280
|
+
expect(count_occurrences(result, 'crk')).toBe(2);
|
|
275
281
|
});
|
|
276
282
|
|
|
277
283
|
it('adds missing imports for shorthand syntax', () => {
|
|
@@ -287,11 +293,11 @@ component App() {
|
|
|
287
293
|
`;
|
|
288
294
|
const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
289
295
|
|
|
290
|
-
expect(count_occurrences(result,
|
|
291
|
-
expect(count_occurrences(result,
|
|
292
|
-
expect(count_occurrences(result,
|
|
293
|
-
expect(count_occurrences(result,
|
|
294
|
-
expect(count_occurrences(result,
|
|
296
|
+
expect(count_occurrences(result, 'TrackedArray')).toBe(2);
|
|
297
|
+
expect(count_occurrences(result, 'TrackedObject')).toBe(2);
|
|
298
|
+
expect(count_occurrences(result, 'TrackedSet')).toBe(2);
|
|
299
|
+
expect(count_occurrences(result, 'TrackedMap')).toBe(2);
|
|
300
|
+
expect(count_occurrences(result, 'createRefKey')).toBe(2);
|
|
295
301
|
});
|
|
296
302
|
|
|
297
303
|
it('only adds missing imports for shorthand syntax, reusing existing ones', () => {
|
|
@@ -309,11 +315,34 @@ component App() {
|
|
|
309
315
|
`;
|
|
310
316
|
const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
311
317
|
|
|
312
|
-
expect(count_occurrences(result,
|
|
313
|
-
expect(count_occurrences(result,
|
|
314
|
-
expect(count_occurrences(result,
|
|
315
|
-
expect(count_occurrences(result,
|
|
316
|
-
expect(count_occurrences(result,
|
|
317
|
-
expect(count_occurrences(result,
|
|
318
|
+
expect(count_occurrences(result, 'TrackedArray')).toBe(2);
|
|
319
|
+
expect(count_occurrences(result, 'TrackedObject')).toBe(2);
|
|
320
|
+
expect(count_occurrences(result, 'TrackedSet')).toBe(2);
|
|
321
|
+
expect(count_occurrences(result, 'TrackedMap')).toBe(2);
|
|
322
|
+
expect(count_occurrences(result, 'createRefKey')).toBe(1);
|
|
323
|
+
expect(count_occurrences(result, 'crk')).toBe(2);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should not error on having js below markup in the same scope', () => {
|
|
327
|
+
const code = `
|
|
328
|
+
component Card(props) {
|
|
329
|
+
<div class="card">
|
|
330
|
+
<props.children />
|
|
331
|
+
</div>
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export component App() {
|
|
335
|
+
<Card>
|
|
336
|
+
component children() {
|
|
337
|
+
<p>{'Card content here'}</p>
|
|
338
|
+
}
|
|
339
|
+
</Card>
|
|
340
|
+
|
|
341
|
+
const test = 5;
|
|
342
|
+
|
|
343
|
+
<div>{test}</div>
|
|
344
|
+
}
|
|
345
|
+
`;
|
|
346
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
318
347
|
});
|
|
319
348
|
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { compile } from 'ripple/compiler';
|
|
2
|
+
import { track } from 'ripple';
|
|
3
|
+
|
|
4
|
+
describe('Compiler: Tracked Object Direct Access Checks', () => {
|
|
5
|
+
|
|
6
|
+
it('should error on direct access to __v of a tracked object', () => {
|
|
7
|
+
const code = `
|
|
8
|
+
export default component App() {
|
|
9
|
+
let count = track(0);
|
|
10
|
+
console.log(count.__v);
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
expect(() => compile(code, 'test.ripple')).toThrow(/Directly accessing internal property "__v" of a tracked object is not allowed/);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should error on direct access to "a" (get/set config) of a tracked object', () => {
|
|
17
|
+
const code = `
|
|
18
|
+
export default component App() {
|
|
19
|
+
let myTracked = track(0);
|
|
20
|
+
console.log(myTracked.a);
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
expect(() => compile(code, 'test.ripple')).toThrow(/Directly accessing internal property "a" of a tracked object is not allowed/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should error on direct access to "b" (block) of a tracked object', () => {
|
|
27
|
+
const code = `
|
|
28
|
+
export default component App() {
|
|
29
|
+
let myTracked = track(0);
|
|
30
|
+
console.log(myTracked.b);
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
expect(() => compile(code, 'test.ripple')).toThrow(/Directly accessing internal property "b" of a tracked object is not allowed/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should error on direct access to "c" (clock) of a tracked object', () => {
|
|
37
|
+
const code = `
|
|
38
|
+
export default component App() {
|
|
39
|
+
let myTracked = track(0);
|
|
40
|
+
console.log(myTracked.c);
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
expect(() => compile(code, 'test.ripple')).toThrow(/Directly accessing internal property "c" of a tracked object is not allowed/);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should error on direct access to "f" (flags) of a tracked object', () => {
|
|
47
|
+
const code = `
|
|
48
|
+
export default component App() {
|
|
49
|
+
let myTracked = track(0);
|
|
50
|
+
console.log(myTracked.f);
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
expect(() => compile(code, 'test.ripple')).toThrow(/Directly accessing internal property "f" of a tracked object is not allowed/);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should compile successfully with correct @ syntax access', () => {
|
|
57
|
+
const code = `
|
|
58
|
+
export default component App() {
|
|
59
|
+
let count = track(0);
|
|
60
|
+
console.log(@count);
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should compile successfully with correct get() function access', () => {
|
|
67
|
+
const code = `
|
|
68
|
+
import { get, track } from 'ripple';
|
|
69
|
+
export default component App() {
|
|
70
|
+
let count = track(0);
|
|
71
|
+
console.log(get(count));
|
|
72
|
+
}
|
|
73
|
+
`;
|
|
74
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should not error on accessing __v of a non-tracked object', () => {
|
|
78
|
+
const code = `
|
|
79
|
+
export default component App() {
|
|
80
|
+
let obj = { __v: 123 };
|
|
81
|
+
console.log(obj.__v);
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should not error on accessing __v of a non-tracked object (member expression)', () => {
|
|
88
|
+
const code = `
|
|
89
|
+
export default component App() {
|
|
90
|
+
let data = { value: { __v: 456 } };
|
|
91
|
+
console.log(data.value.__v);
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should not error on accessing a property named like an internal one on a non-tracked object', () => {
|
|
98
|
+
const code = `
|
|
99
|
+
export default component App() {
|
|
100
|
+
let config = { a: 'some_value', b: 'another_value' };
|
|
101
|
+
console.log(config.a);
|
|
102
|
+
console.log(config.b);
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
});
|
|
@@ -398,4 +398,123 @@ describe('dynamic DOM elements', () => {
|
|
|
398
398
|
expect(stillHasScopedClass).toBe(true);
|
|
399
399
|
expect(newClasses.includes('even')).toBe(true);
|
|
400
400
|
});
|
|
401
|
-
|
|
401
|
+
|
|
402
|
+
it('adds scoping class to dynamic elements', () => {
|
|
403
|
+
component App() {
|
|
404
|
+
let tag = track('div');
|
|
405
|
+
|
|
406
|
+
<@tag class="scoped"><p>{'Scoped dynamic element'}</p></@tag>
|
|
407
|
+
|
|
408
|
+
<style>
|
|
409
|
+
.scoped {
|
|
410
|
+
color: blue;
|
|
411
|
+
}
|
|
412
|
+
</style>
|
|
413
|
+
}
|
|
414
|
+
render(App);
|
|
415
|
+
|
|
416
|
+
const div = container.querySelector('div');
|
|
417
|
+
const p = div.querySelector('p');
|
|
418
|
+
|
|
419
|
+
expect(Array.from(div.classList).some(c => c.startsWith('ripple-'))).toBe(true);
|
|
420
|
+
expect(Array.from(p.classList).some(c => c.startsWith('ripple-'))).toBe(true);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('adds scoping class to dynamic elements when selector targets by tag name', () => {
|
|
424
|
+
component App() {
|
|
425
|
+
let tag = track('div');
|
|
426
|
+
|
|
427
|
+
<@tag class="scoped"><p>{'Scoped dynamic element'}</p></@tag>
|
|
428
|
+
|
|
429
|
+
<style>
|
|
430
|
+
div {
|
|
431
|
+
color: blue;
|
|
432
|
+
}
|
|
433
|
+
</style>
|
|
434
|
+
}
|
|
435
|
+
render(App);
|
|
436
|
+
|
|
437
|
+
const div = container.querySelector('div');
|
|
438
|
+
const p = div.querySelector('p');
|
|
439
|
+
|
|
440
|
+
expect(Array.from(div.classList).some(c => c.startsWith('ripple-'))).toBe(true);
|
|
441
|
+
expect(Array.from(p.classList).some(c => c.startsWith('ripple-'))).toBe(true);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it("doesn't add scoping class to components inside dynamic element", () => {
|
|
445
|
+
component Child() {
|
|
446
|
+
<div class="child"><p>{"I am a child component"}</p></div>
|
|
447
|
+
|
|
448
|
+
<style>
|
|
449
|
+
.child {
|
|
450
|
+
color: blue;
|
|
451
|
+
}
|
|
452
|
+
</style>
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
component App() {
|
|
456
|
+
let tag = track('div');
|
|
457
|
+
|
|
458
|
+
<@tag class="scoped">
|
|
459
|
+
<p>{'Scoped dynamic element'}</p>
|
|
460
|
+
<Child />
|
|
461
|
+
</@tag>
|
|
462
|
+
|
|
463
|
+
<style>
|
|
464
|
+
div {
|
|
465
|
+
color: blue;
|
|
466
|
+
}
|
|
467
|
+
</style>
|
|
468
|
+
}
|
|
469
|
+
render(App);
|
|
470
|
+
|
|
471
|
+
const outerDiv = container.querySelector('.scoped');
|
|
472
|
+
const innerDiv = outerDiv.querySelector('.child');
|
|
473
|
+
const innerP = innerDiv.querySelector('p');
|
|
474
|
+
|
|
475
|
+
const outerScope = Array.from(outerDiv.classList).find(c => c.startsWith('ripple-'));
|
|
476
|
+
const innerScopes = Array.from(innerDiv.classList).filter(c => c.startsWith('ripple-'));
|
|
477
|
+
const innerInnerScopes = Array.from(innerP.classList).filter(c => c.startsWith('ripple-'));
|
|
478
|
+
|
|
479
|
+
expect(outerScope).toBeTruthy();
|
|
480
|
+
|
|
481
|
+
expect(innerScopes).toHaveLength(1);
|
|
482
|
+
expect(innerScopes[0]).not.toBe(outerScope);
|
|
483
|
+
|
|
484
|
+
expect(innerInnerScopes).toHaveLength(0);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it("doesn't add scoping class to dynamically rendered component", () => {
|
|
488
|
+
component Child() {
|
|
489
|
+
<div class="child"><p>{"I am a child component"}</p></div>
|
|
490
|
+
|
|
491
|
+
<style>
|
|
492
|
+
.child {
|
|
493
|
+
color: green;
|
|
494
|
+
}
|
|
495
|
+
</style>
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
component App() {
|
|
499
|
+
let tag = track(() => Child);
|
|
500
|
+
|
|
501
|
+
<@tag />
|
|
502
|
+
|
|
503
|
+
<style>
|
|
504
|
+
.child {
|
|
505
|
+
color: red;
|
|
506
|
+
}
|
|
507
|
+
</style>
|
|
508
|
+
}
|
|
509
|
+
render(App);
|
|
510
|
+
|
|
511
|
+
const div = container.querySelector('.child');
|
|
512
|
+
const p = div.querySelector('p');
|
|
513
|
+
|
|
514
|
+
const outerScopes = Array.from(div.classList).filter(c => c.startsWith('ripple-'));
|
|
515
|
+
const innerScopes = Array.from(p.classList).filter(c => c.startsWith('ripple-'));
|
|
516
|
+
|
|
517
|
+
expect(outerScopes).toHaveLength(1);
|
|
518
|
+
expect(innerScopes).toHaveLength(0);
|
|
519
|
+
});
|
|
520
|
+
});
|