ripple 0.3.67 → 0.3.69
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 +57 -0
- package/package.json +3 -3
- package/src/jsx-runtime.d.ts +2 -2
- package/src/runtime/element.js +1 -1
- package/src/runtime/index-client.js +11 -11
- package/src/runtime/index-server.js +7 -4
- package/src/runtime/internal/client/bindings.js +1 -1
- package/src/runtime/internal/client/blocks.js +13 -4
- package/src/runtime/internal/client/component.js +55 -0
- package/src/runtime/internal/client/composite.js +4 -2
- package/src/runtime/internal/client/expression.js +65 -7
- package/src/runtime/internal/client/hmr.js +54 -43
- package/src/runtime/internal/client/index.js +5 -1
- package/src/runtime/internal/client/portal.js +70 -69
- package/src/runtime/internal/client/render.js +3 -0
- package/src/runtime/internal/server/index.js +92 -8
- package/tests/client/__snapshots__/html.test.tsrx.snap +3 -3
- package/tests/client/array/array.copy-within.test.tsrx +33 -31
- package/tests/client/array/array.derived.test.tsrx +186 -169
- package/tests/client/array/array.iteration.test.tsrx +40 -37
- package/tests/client/array/array.mutations.test.tsrx +113 -101
- package/tests/client/array/array.static.test.tsrx +119 -101
- package/tests/client/array/array.to-methods.test.tsrx +24 -21
- package/tests/client/async-suspend.test.tsrx +247 -246
- package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +0 -1
- package/tests/client/basic/basic.attributes.test.tsrx +428 -423
- package/tests/client/basic/basic.collections.test.tsrx +109 -102
- package/tests/client/basic/basic.components.test.tsrx +323 -205
- package/tests/client/basic/basic.errors.test.tsrx +91 -91
- package/tests/client/basic/basic.events.test.tsrx +114 -115
- package/tests/client/basic/basic.get-set.test.tsrx +97 -87
- package/tests/client/basic/basic.hmr.test.tsrx +19 -16
- package/tests/client/basic/basic.reactivity.test.tsrx +199 -191
- package/tests/client/basic/basic.rendering.test.tsrx +272 -182
- package/tests/client/basic/basic.styling.test.tsrx +23 -22
- package/tests/client/basic/basic.utilities.test.tsrx +10 -8
- package/tests/client/boundaries.test.tsrx +26 -26
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +5 -5
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +5 -5
- package/tests/client/compiler/compiler.assignments.test.tsrx +77 -81
- package/tests/client/compiler/compiler.attributes.test.tsrx +15 -15
- package/tests/client/compiler/compiler.basic.test.tsrx +322 -314
- package/tests/client/compiler/compiler.regex.test.tsrx +44 -47
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +38 -38
- package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
- package/tests/client/compiler/compiler.typescript.test.tsrx +2 -2
- package/tests/client/composite/composite.dynamic-components.test.tsrx +47 -48
- package/tests/client/composite/composite.generics.test.tsrx +168 -192
- package/tests/client/composite/composite.props.test.tsrx +97 -81
- package/tests/client/composite/composite.reactivity.test.tsrx +177 -147
- package/tests/client/composite/composite.render.test.tsrx +122 -105
- package/tests/client/computed-properties.test.tsrx +28 -28
- package/tests/client/context.test.tsrx +21 -21
- package/tests/client/css/global-additional-cases.test.tsrx +58 -58
- package/tests/client/css/global-advanced-selectors.test.tsrx +16 -16
- package/tests/client/css/global-at-rules.test.tsrx +10 -10
- package/tests/client/css/global-basic.test.tsrx +14 -14
- package/tests/client/css/global-classes-ids.test.tsrx +14 -14
- package/tests/client/css/global-combinators.test.tsrx +10 -10
- package/tests/client/css/global-complex-nesting.test.tsrx +14 -14
- package/tests/client/css/global-edge-cases.test.tsrx +18 -18
- package/tests/client/css/global-keyframes.test.tsrx +12 -12
- package/tests/client/css/global-nested.test.tsrx +10 -10
- package/tests/client/css/global-pseudo.test.tsrx +12 -12
- package/tests/client/css/global-scoping.test.tsrx +20 -20
- package/tests/client/css/style-identifier.test.tsrx +143 -291
- package/tests/client/date.test.tsrx +146 -133
- package/tests/client/dynamic-elements.test.tsrx +398 -365
- package/tests/client/events.test.tsrx +292 -290
- package/tests/client/for.test.tsrx +156 -153
- package/tests/client/head.test.tsrx +105 -96
- package/tests/client/html.test.tsrx +122 -26
- package/tests/client/input-value.test.tsrx +1361 -1314
- package/tests/client/lazy-array.test.tsrx +16 -13
- package/tests/client/lazy-destructuring.test.tsrx +257 -213
- package/tests/client/map.test.tsrx +65 -60
- package/tests/client/media-query.test.tsrx +22 -20
- package/tests/client/object.test.tsrx +87 -81
- package/tests/client/portal.test.tsrx +57 -51
- package/tests/client/ref.test.tsrx +233 -202
- package/tests/client/return.test.tsrx +71 -2560
- package/tests/client/set.test.tsrx +54 -45
- package/tests/client/svg.test.tsrx +216 -186
- package/tests/client/switch.test.tsrx +194 -193
- package/tests/client/track-async-hydration.test.tsrx +18 -14
- package/tests/client/tracked-index-access.test.tsrx +28 -18
- package/tests/client/try.test.tsrx +675 -548
- package/tests/client/tsx.test.tsrx +373 -311
- package/tests/client/typescript-generics.test.tsrx +145 -145
- package/tests/client/url/url.derived.test.tsrx +33 -28
- package/tests/client/url/url.parsing.test.tsrx +61 -51
- package/tests/client/url/url.partial-removal.test.tsrx +56 -48
- package/tests/client/url/url.reactivity.test.tsrx +142 -125
- package/tests/client/url/url.serialization.test.tsrx +13 -11
- package/tests/client/url-search-params/url-search-params.derived.test.tsrx +34 -29
- package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +25 -21
- package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +50 -45
- package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +111 -99
- package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +49 -43
- package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +14 -12
- package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +16 -14
- package/tests/hydration/basic.test.js +3 -3
- package/tests/hydration/compiled/client/basic.js +586 -651
- package/tests/hydration/compiled/client/composite.js +79 -104
- package/tests/hydration/compiled/client/events.js +140 -148
- package/tests/hydration/compiled/client/for.js +1005 -1018
- package/tests/hydration/compiled/client/head.js +124 -134
- package/tests/hydration/compiled/client/hmr.js +41 -48
- package/tests/hydration/compiled/client/html-in-template.js +38 -41
- package/tests/hydration/compiled/client/html.js +970 -1314
- package/tests/hydration/compiled/client/if-children.js +234 -249
- package/tests/hydration/compiled/client/if.js +182 -189
- package/tests/hydration/compiled/client/mixed-control-flow.js +347 -303
- package/tests/hydration/compiled/client/nested-control-flow.js +1084 -832
- package/tests/hydration/compiled/client/portal.js +65 -85
- package/tests/hydration/compiled/client/reactivity.js +84 -90
- package/tests/hydration/compiled/client/return.js +38 -1939
- package/tests/hydration/compiled/client/switch.js +218 -224
- package/tests/hydration/compiled/client/track-async-serialization.js +250 -259
- package/tests/hydration/compiled/client/try.js +123 -132
- package/tests/hydration/compiled/server/basic.js +773 -831
- package/tests/hydration/compiled/server/composite.js +166 -191
- package/tests/hydration/compiled/server/events.js +170 -184
- package/tests/hydration/compiled/server/for.js +851 -909
- package/tests/hydration/compiled/server/head.js +206 -216
- package/tests/hydration/compiled/server/hmr.js +64 -72
- package/tests/hydration/compiled/server/html-in-template.js +42 -76
- package/tests/hydration/compiled/server/html.js +1362 -1667
- package/tests/hydration/compiled/server/if-children.js +419 -445
- package/tests/hydration/compiled/server/if.js +194 -208
- package/tests/hydration/compiled/server/mixed-control-flow.js +249 -257
- package/tests/hydration/compiled/server/nested-control-flow.js +491 -515
- package/tests/hydration/compiled/server/portal.js +152 -160
- package/tests/hydration/compiled/server/reactivity.js +94 -106
- package/tests/hydration/compiled/server/return.js +28 -2172
- package/tests/hydration/compiled/server/switch.js +274 -286
- package/tests/hydration/compiled/server/track-async-serialization.js +340 -358
- package/tests/hydration/compiled/server/try.js +167 -185
- package/tests/hydration/components/basic.tsrx +320 -272
- package/tests/hydration/components/composite.tsrx +44 -32
- package/tests/hydration/components/events.tsrx +101 -91
- package/tests/hydration/components/for.tsrx +510 -452
- package/tests/hydration/components/head.tsrx +87 -80
- package/tests/hydration/components/hmr.tsrx +22 -17
- package/tests/hydration/components/html-in-template.tsrx +22 -17
- package/tests/hydration/components/html.tsrx +525 -443
- package/tests/hydration/components/if-children.tsrx +158 -148
- package/tests/hydration/components/if.tsrx +109 -95
- package/tests/hydration/components/mixed-control-flow.tsrx +100 -96
- package/tests/hydration/components/nested-control-flow.tsrx +215 -203
- package/tests/hydration/components/portal.tsrx +41 -34
- package/tests/hydration/components/reactivity.tsrx +37 -27
- package/tests/hydration/components/return.tsrx +12 -556
- package/tests/hydration/components/switch.tsrx +120 -114
- package/tests/hydration/components/track-async-serialization.tsrx +107 -91
- package/tests/hydration/components/try.tsrx +55 -40
- package/tests/hydration/html.test.js +4 -4
- package/tests/hydration/return.test.js +13 -532
- package/tests/server/await.test.tsrx +3 -3
- package/tests/server/basic.attributes.test.tsrx +264 -195
- package/tests/server/basic.components.test.tsrx +296 -169
- package/tests/server/basic.test.tsrx +300 -198
- package/tests/server/compiler.test.tsrx +62 -60
- package/tests/server/composite.props.test.tsrx +77 -63
- package/tests/server/composite.test.tsrx +168 -192
- package/tests/server/context.test.tsrx +18 -12
- package/tests/server/dynamic-elements.test.tsrx +197 -180
- package/tests/server/for.test.tsrx +85 -78
- package/tests/server/head.test.tsrx +50 -43
- package/tests/server/html-nesting-validation.test.tsrx +8 -8
- package/tests/server/if.test.tsrx +57 -51
- package/tests/server/lazy-destructuring.test.tsrx +366 -294
- package/tests/server/return.test.tsrx +76 -1355
- package/tests/server/streaming-ssr.test.tsrx +4 -75
- package/tests/server/style-identifier.test.tsrx +169 -148
- package/tests/server/switch.test.tsrx +91 -85
- package/tests/server/track-async-serialization.test.tsrx +105 -85
- package/tests/server/try.test.tsrx +374 -280
- package/tests/utils/compiler-compat-config.test.js +2 -2
- package/tests/utils/runtime-imports.test.js +10 -0
- package/types/index.d.ts +8 -0
- package/tests/client/__snapshots__/html.test.rsrx.snap +0 -40
|
@@ -3,10 +3,11 @@ import { createRefKey, effect, flushSync, track } from 'ripple';
|
|
|
3
3
|
|
|
4
4
|
describe('dynamic DOM elements', () => {
|
|
5
5
|
it('renders static dynamic element', () => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
function App() {
|
|
7
|
+
return <>
|
|
8
|
+
let tag = track('div');
|
|
9
|
+
<@tag>{'Hello World'}</@tag>
|
|
10
|
+
</>;
|
|
10
11
|
}
|
|
11
12
|
render(App);
|
|
12
13
|
|
|
@@ -18,10 +19,11 @@ describe('dynamic DOM elements', () => {
|
|
|
18
19
|
// The ts errors below are due to limitations in our current tsx generation for dynamic elements.
|
|
19
20
|
// They can be ignored for now. But we'll fix them via jsx() vs <jsx>
|
|
20
21
|
it('renders static dynamic element from a plain object with a tracked property', () => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
function App() {
|
|
23
|
+
return <>
|
|
24
|
+
let obj = { tag: track('div') };
|
|
25
|
+
<obj.tag.value>{'Hello World'}</obj.tag.value>
|
|
26
|
+
</>;
|
|
25
27
|
}
|
|
26
28
|
render(App);
|
|
27
29
|
|
|
@@ -31,11 +33,12 @@ describe('dynamic DOM elements', () => {
|
|
|
31
33
|
});
|
|
32
34
|
|
|
33
35
|
it('renders static dynamic element from a tracked object with a tracked property', () => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
function App() {
|
|
37
|
+
return <>
|
|
38
|
+
let obj = track({ tag: track('div') });
|
|
39
|
+
let tag = obj.value.tag;
|
|
40
|
+
<@tag>{'Hello World'}</@tag>
|
|
41
|
+
</>;
|
|
39
42
|
}
|
|
40
43
|
render(App);
|
|
41
44
|
|
|
@@ -47,11 +50,12 @@ describe('dynamic DOM elements', () => {
|
|
|
47
50
|
it(
|
|
48
51
|
'renders static dynamic element from a tracked object with a computed tracked property',
|
|
49
52
|
() => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
function App() {
|
|
54
|
+
return <>
|
|
55
|
+
let obj = track({ tag: track('div') });
|
|
56
|
+
let tag = obj.value['tag'];
|
|
57
|
+
<@tag>{'Hello World'}</@tag>
|
|
58
|
+
</>;
|
|
55
59
|
}
|
|
56
60
|
render(App);
|
|
57
61
|
|
|
@@ -62,17 +66,18 @@ describe('dynamic DOM elements', () => {
|
|
|
62
66
|
);
|
|
63
67
|
|
|
64
68
|
it('renders reactive dynamic element', () => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
function App() {
|
|
70
|
+
return <>
|
|
71
|
+
let &[tag] = track('div');
|
|
72
|
+
<button
|
|
73
|
+
onClick={() => {
|
|
74
|
+
tag = 'span';
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{'Change Tag'}
|
|
78
|
+
</button>
|
|
79
|
+
<@tag id="dynamic">{'Hello World'}</@tag>
|
|
80
|
+
</>;
|
|
76
81
|
}
|
|
77
82
|
render(App);
|
|
78
83
|
|
|
@@ -93,10 +98,11 @@ describe('dynamic DOM elements', () => {
|
|
|
93
98
|
});
|
|
94
99
|
|
|
95
100
|
it('renders self-closing dynamic element', () => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
function App() {
|
|
102
|
+
return <>
|
|
103
|
+
let tag = track('input');
|
|
104
|
+
<@tag type="text" value="test" />
|
|
105
|
+
</>;
|
|
100
106
|
}
|
|
101
107
|
render(App);
|
|
102
108
|
|
|
@@ -107,11 +113,12 @@ describe('dynamic DOM elements', () => {
|
|
|
107
113
|
});
|
|
108
114
|
|
|
109
115
|
it('handles dynamic element with attributes', () => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
function App() {
|
|
117
|
+
return <>
|
|
118
|
+
let tag = track('div');
|
|
119
|
+
let &[className] = track('test-class');
|
|
120
|
+
<@tag class={className} id="test" data-testid="dynamic-element">{'Content'}</@tag>
|
|
121
|
+
</>;
|
|
115
122
|
}
|
|
116
123
|
render(App);
|
|
117
124
|
|
|
@@ -123,13 +130,14 @@ describe('dynamic DOM elements', () => {
|
|
|
123
130
|
});
|
|
124
131
|
|
|
125
132
|
it('handles nested dynamic elements', () => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
function App() {
|
|
134
|
+
return <>
|
|
135
|
+
let outerTag = track('div');
|
|
136
|
+
let innerTag = track('span');
|
|
137
|
+
<@outerTag class="outer">
|
|
138
|
+
<@innerTag class="inner">{'Nested content'}</@innerTag>
|
|
139
|
+
</@outerTag>
|
|
140
|
+
</>;
|
|
133
141
|
}
|
|
134
142
|
render(App);
|
|
135
143
|
|
|
@@ -143,11 +151,14 @@ describe('dynamic DOM elements', () => {
|
|
|
143
151
|
});
|
|
144
152
|
|
|
145
153
|
it('handles dynamic element with class object', () => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
function App() {
|
|
155
|
+
return <>
|
|
156
|
+
let tag = track('div');
|
|
157
|
+
let &[active] = track(true);
|
|
158
|
+
<@tag class={{ active: active, 'dynamic-element': true }}>
|
|
159
|
+
{'Element with class object'}
|
|
160
|
+
</@tag>
|
|
161
|
+
</>;
|
|
151
162
|
}
|
|
152
163
|
render(App);
|
|
153
164
|
|
|
@@ -158,18 +169,19 @@ describe('dynamic DOM elements', () => {
|
|
|
158
169
|
});
|
|
159
170
|
|
|
160
171
|
it('handles dynamic element with style object', () => {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
172
|
+
function App() {
|
|
173
|
+
return <>
|
|
174
|
+
let tag = track('span');
|
|
175
|
+
<@tag
|
|
176
|
+
style={{
|
|
177
|
+
color: 'red',
|
|
178
|
+
fontSize: '16px',
|
|
179
|
+
fontWeight: 'bold',
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
{'Styled dynamic element'}
|
|
183
|
+
</@tag>
|
|
184
|
+
</>;
|
|
173
185
|
}
|
|
174
186
|
render(App);
|
|
175
187
|
|
|
@@ -181,15 +193,16 @@ describe('dynamic DOM elements', () => {
|
|
|
181
193
|
});
|
|
182
194
|
|
|
183
195
|
it('handles dynamic element with spread attributes', () => {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
196
|
+
function App() {
|
|
197
|
+
return <>
|
|
198
|
+
let tag = track('section');
|
|
199
|
+
const attrs = {
|
|
200
|
+
id: 'spread-section',
|
|
201
|
+
'data-testid': 'spread-test',
|
|
202
|
+
class: 'spread-class',
|
|
203
|
+
};
|
|
204
|
+
<@tag {...attrs} data-extra="additional">{'Element with spread attributes'}</@tag>
|
|
205
|
+
</>;
|
|
193
206
|
}
|
|
194
207
|
render(App);
|
|
195
208
|
|
|
@@ -204,17 +217,18 @@ describe('dynamic DOM elements', () => {
|
|
|
204
217
|
it('handles dynamic element with ref', () => {
|
|
205
218
|
let capturedElement: HTMLElement | null = null;
|
|
206
219
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
220
|
+
function App() {
|
|
221
|
+
return <>
|
|
222
|
+
let tag = track('article');
|
|
223
|
+
<@tag
|
|
224
|
+
ref={(node: HTMLElement) => {
|
|
225
|
+
capturedElement = node;
|
|
226
|
+
}}
|
|
227
|
+
id="ref-test"
|
|
228
|
+
>
|
|
229
|
+
{'Element with ref'}
|
|
230
|
+
</@tag>
|
|
231
|
+
</>;
|
|
218
232
|
}
|
|
219
233
|
render(App);
|
|
220
234
|
flushSync();
|
|
@@ -225,30 +239,32 @@ describe('dynamic DOM elements', () => {
|
|
|
225
239
|
expect(capturedElement!.textContent).toBe('Element with ref');
|
|
226
240
|
});
|
|
227
241
|
|
|
228
|
-
it('handles
|
|
242
|
+
it('handles multiple ref targets on dynamic DOM elements', () => {
|
|
229
243
|
let refAttrElement: HTMLInputElement | null = null;
|
|
230
244
|
let anonymousRefElement: HTMLInputElement | null = null;
|
|
231
245
|
let namedRefElement: HTMLInputElement | null = null;
|
|
232
246
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
247
|
+
function App() {
|
|
248
|
+
return <>
|
|
249
|
+
let tag = track('input');
|
|
250
|
+
let input: HTMLInputElement | undefined;
|
|
251
|
+
const state: { anonymous?: HTMLInputElement } = {};
|
|
252
|
+
<@tag
|
|
253
|
+
id="dynamic-ref-combo"
|
|
254
|
+
type="text"
|
|
255
|
+
ref={[
|
|
256
|
+
input,
|
|
257
|
+
state.anonymous,
|
|
258
|
+
(node: HTMLInputElement | null) => {
|
|
259
|
+
namedRefElement = node;
|
|
260
|
+
},
|
|
261
|
+
]}
|
|
262
|
+
/>
|
|
263
|
+
effect(() => {
|
|
264
|
+
refAttrElement = input ?? null;
|
|
265
|
+
anonymousRefElement = state.anonymous ?? null;
|
|
266
|
+
});
|
|
267
|
+
</>;
|
|
252
268
|
}
|
|
253
269
|
|
|
254
270
|
render(App);
|
|
@@ -263,32 +279,34 @@ describe('dynamic DOM elements', () => {
|
|
|
263
279
|
expect(element!.hasAttribute('input_ref')).toBe(false);
|
|
264
280
|
});
|
|
265
281
|
|
|
266
|
-
it('forwards dynamic
|
|
282
|
+
it('forwards multiple dynamic refs through a dynamic component that spreads props', () => {
|
|
267
283
|
let refAttrElement: HTMLInputElement | null = null;
|
|
268
284
|
let anonymousRefElement: HTMLInputElement | null = null;
|
|
269
285
|
let namedRefElement: HTMLInputElement | null = null;
|
|
270
286
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
287
|
+
function Child(props: PropsWithExtras<{}>) {
|
|
288
|
+
return <><input id="dynamic-component-ref-combo" type="text" {...props} /></>;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function App() {
|
|
292
|
+
return <>
|
|
293
|
+
let dynamic = track(() => Child);
|
|
294
|
+
let input: HTMLInputElement | undefined;
|
|
295
|
+
const state: { anonymous?: HTMLInputElement } = {};
|
|
296
|
+
<@dynamic
|
|
297
|
+
ref={[
|
|
298
|
+
input,
|
|
299
|
+
state.anonymous,
|
|
300
|
+
(node: HTMLInputElement | null) => {
|
|
301
|
+
namedRefElement = node;
|
|
302
|
+
},
|
|
303
|
+
]}
|
|
304
|
+
/>
|
|
305
|
+
effect(() => {
|
|
306
|
+
refAttrElement = input ?? null;
|
|
307
|
+
anonymousRefElement = state.anonymous ?? null;
|
|
308
|
+
});
|
|
309
|
+
</>;
|
|
292
310
|
}
|
|
293
311
|
|
|
294
312
|
render(App);
|
|
@@ -304,22 +322,21 @@ describe('dynamic DOM elements', () => {
|
|
|
304
322
|
});
|
|
305
323
|
|
|
306
324
|
it('handles dynamic element with createRefKey in spread', () => {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
<@tag {...dynamicProps}>{'Element with spread ref'}</@tag>
|
|
325
|
+
function App() {
|
|
326
|
+
return <>
|
|
327
|
+
let tag = track('header');
|
|
328
|
+
function elementRef(node: HTMLElement) {
|
|
329
|
+
// Set an attribute on the element to prove ref was called
|
|
330
|
+
node.setAttribute('data-spread-ref-called', 'true');
|
|
331
|
+
node.setAttribute('data-spread-ref-tag', node.tagName.toLowerCase());
|
|
332
|
+
}
|
|
333
|
+
const dynamicProps = {
|
|
334
|
+
id: 'spread-ref-test',
|
|
335
|
+
class: 'ref-element',
|
|
336
|
+
[createRefKey()]: elementRef,
|
|
337
|
+
};
|
|
338
|
+
<@tag {...dynamicProps}>{'Element with spread ref'}</@tag>
|
|
339
|
+
</>;
|
|
323
340
|
}
|
|
324
341
|
render(App);
|
|
325
342
|
flushSync();
|
|
@@ -334,25 +351,26 @@ describe('dynamic DOM elements', () => {
|
|
|
334
351
|
});
|
|
335
352
|
|
|
336
353
|
it('has reactive attributes on dynamic elements', () => {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
354
|
+
function App() {
|
|
355
|
+
return <>
|
|
356
|
+
let tag = track('div');
|
|
357
|
+
let &[count] = track(0);
|
|
358
|
+
<button
|
|
359
|
+
onClick={() => {
|
|
360
|
+
count++;
|
|
361
|
+
}}
|
|
362
|
+
>
|
|
363
|
+
{'Increment'}
|
|
364
|
+
</button>
|
|
365
|
+
<@tag
|
|
366
|
+
id={count % 2 ? 'even' : 'odd'}
|
|
367
|
+
class={count % 2 ? 'even-class' : 'odd-class'}
|
|
368
|
+
data-count={count}
|
|
369
|
+
>
|
|
370
|
+
{'Count: '}
|
|
371
|
+
{count}
|
|
372
|
+
</@tag>
|
|
373
|
+
</>;
|
|
356
374
|
}
|
|
357
375
|
|
|
358
376
|
render(App);
|
|
@@ -388,16 +406,16 @@ describe('dynamic DOM elements', () => {
|
|
|
388
406
|
});
|
|
389
407
|
|
|
390
408
|
it('applies scoped CSS to dynamic elements', () => {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
409
|
+
function App() {
|
|
410
|
+
return <>
|
|
411
|
+
let tag = track('div');
|
|
412
|
+
<@tag class="test-class">{'Dynamic element'}</@tag>
|
|
413
|
+
<style>
|
|
414
|
+
.test-class {
|
|
415
|
+
color: red;
|
|
416
|
+
}
|
|
417
|
+
</style>
|
|
418
|
+
</>;
|
|
401
419
|
}
|
|
402
420
|
|
|
403
421
|
render(App);
|
|
@@ -413,31 +431,31 @@ describe('dynamic DOM elements', () => {
|
|
|
413
431
|
});
|
|
414
432
|
|
|
415
433
|
it('applies scoped CSS to dynamic elements with reactive classes', () => {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
434
|
+
function App() {
|
|
435
|
+
return <>
|
|
436
|
+
let tag = track('button');
|
|
437
|
+
let &[count] = track(0);
|
|
438
|
+
<@tag
|
|
439
|
+
class={count % 2 ? 'even' : 'odd'}
|
|
440
|
+
id={count % 2 ? 'even' : 'odd'}
|
|
441
|
+
onClick={() => {
|
|
442
|
+
count++;
|
|
443
|
+
}}
|
|
444
|
+
>
|
|
445
|
+
{'Count: '}
|
|
446
|
+
{count}
|
|
447
|
+
</@tag>
|
|
448
|
+
<style>
|
|
449
|
+
.even {
|
|
450
|
+
background-color: green;
|
|
451
|
+
color: white;
|
|
452
|
+
}
|
|
453
|
+
.odd {
|
|
454
|
+
background-color: red;
|
|
455
|
+
color: white;
|
|
456
|
+
}
|
|
457
|
+
</style>
|
|
458
|
+
</>;
|
|
441
459
|
}
|
|
442
460
|
|
|
443
461
|
render(App);
|
|
@@ -483,33 +501,36 @@ describe('dynamic DOM elements', () => {
|
|
|
483
501
|
});
|
|
484
502
|
|
|
485
503
|
it('handles spread attributes with class and CSS scoping ', () => {
|
|
486
|
-
|
|
504
|
+
function DynamicButton(&{ ...rest }: PropsWithExtras<{
|
|
487
505
|
class: string;
|
|
488
506
|
id: string;
|
|
489
507
|
onClick: EventListener;
|
|
490
508
|
}>) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
count
|
|
511
|
-
|
|
512
|
-
|
|
509
|
+
return <>
|
|
510
|
+
const tag = track('button');
|
|
511
|
+
<@tag {...rest}>{rest.class}</@tag>
|
|
512
|
+
<style>
|
|
513
|
+
.even {
|
|
514
|
+
background-color: green;
|
|
515
|
+
}
|
|
516
|
+
.odd {
|
|
517
|
+
background-color: red;
|
|
518
|
+
}
|
|
519
|
+
</style>
|
|
520
|
+
</>;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function App() {
|
|
524
|
+
return <>
|
|
525
|
+
let &[count] = track(0);
|
|
526
|
+
<DynamicButton
|
|
527
|
+
class={count % 2 ? 'even' : 'odd'}
|
|
528
|
+
id={count % 2 ? 'even' : 'odd'}
|
|
529
|
+
onClick={() => {
|
|
530
|
+
count++;
|
|
531
|
+
}}
|
|
532
|
+
/>
|
|
533
|
+
</>;
|
|
513
534
|
}
|
|
514
535
|
|
|
515
536
|
render(App);
|
|
@@ -544,18 +565,18 @@ describe('dynamic DOM elements', () => {
|
|
|
544
565
|
});
|
|
545
566
|
|
|
546
567
|
it('adds scoping class to dynamic elements', () => {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
568
|
+
function App() {
|
|
569
|
+
return <>
|
|
570
|
+
let tag = track('div');
|
|
571
|
+
<@tag class="scoped">
|
|
572
|
+
<p>{'Scoped dynamic element'}</p>
|
|
573
|
+
</@tag>
|
|
574
|
+
<style>
|
|
575
|
+
.scoped {
|
|
576
|
+
color: blue;
|
|
577
|
+
}
|
|
578
|
+
</style>
|
|
579
|
+
</>;
|
|
559
580
|
}
|
|
560
581
|
render(App);
|
|
561
582
|
|
|
@@ -567,18 +588,18 @@ describe('dynamic DOM elements', () => {
|
|
|
567
588
|
});
|
|
568
589
|
|
|
569
590
|
it('adds scoping class to dynamic elements when selector targets by tag name', () => {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
591
|
+
function App() {
|
|
592
|
+
return <>
|
|
593
|
+
let tag = track('div');
|
|
594
|
+
<@tag class="scoped">
|
|
595
|
+
<p>{'Scoped dynamic element'}</p>
|
|
596
|
+
</@tag>
|
|
597
|
+
<style>
|
|
598
|
+
div {
|
|
599
|
+
color: blue;
|
|
600
|
+
}
|
|
601
|
+
</style>
|
|
602
|
+
</>;
|
|
582
603
|
}
|
|
583
604
|
render(App);
|
|
584
605
|
|
|
@@ -590,31 +611,32 @@ describe('dynamic DOM elements', () => {
|
|
|
590
611
|
});
|
|
591
612
|
|
|
592
613
|
it('doesn\'t add scoping class to components inside dynamic element', () => {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
<
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
614
|
+
function Child() {
|
|
615
|
+
return <>
|
|
616
|
+
<div class="child">
|
|
617
|
+
<p>{'I am a child component'}</p>
|
|
618
|
+
</div>
|
|
619
|
+
<style>
|
|
620
|
+
.child {
|
|
621
|
+
color: blue;
|
|
622
|
+
}
|
|
623
|
+
</style>
|
|
624
|
+
</>;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function App() {
|
|
628
|
+
return <>
|
|
629
|
+
let tag = track('div');
|
|
630
|
+
<@tag class="scoped">
|
|
631
|
+
<p>{'Scoped dynamic element'}</p>
|
|
632
|
+
<Child />
|
|
633
|
+
</@tag>
|
|
634
|
+
<style>
|
|
635
|
+
div {
|
|
636
|
+
color: blue;
|
|
637
|
+
}
|
|
638
|
+
</style>
|
|
639
|
+
</>;
|
|
618
640
|
}
|
|
619
641
|
render(App);
|
|
620
642
|
|
|
@@ -635,28 +657,29 @@ describe('dynamic DOM elements', () => {
|
|
|
635
657
|
});
|
|
636
658
|
|
|
637
659
|
it('doesn\'t add scoping class to dynamically rendered component', () => {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
<
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
+
function Child() {
|
|
661
|
+
return <>
|
|
662
|
+
<div class="child">
|
|
663
|
+
<p>{'I am a child component'}</p>
|
|
664
|
+
</div>
|
|
665
|
+
<style>
|
|
666
|
+
.child {
|
|
667
|
+
color: green;
|
|
668
|
+
}
|
|
669
|
+
</style>
|
|
670
|
+
</>;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function App() {
|
|
674
|
+
return <>
|
|
675
|
+
let tag = track(() => Child);
|
|
676
|
+
<@tag />
|
|
677
|
+
<style>
|
|
678
|
+
.child {
|
|
679
|
+
color: red;
|
|
680
|
+
}
|
|
681
|
+
</style>
|
|
682
|
+
</>;
|
|
660
683
|
}
|
|
661
684
|
render(App);
|
|
662
685
|
|
|
@@ -674,26 +697,29 @@ describe('dynamic DOM elements', () => {
|
|
|
674
697
|
let capturedElement: HTMLElement | null = null;
|
|
675
698
|
let refCallCount = 0;
|
|
676
699
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
active
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
700
|
+
function Button(props: any) {
|
|
701
|
+
return <>
|
|
702
|
+
const el = track('button');
|
|
703
|
+
<@el {...props} />
|
|
704
|
+
</>;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function App() {
|
|
708
|
+
return <>
|
|
709
|
+
let &[active] = track(false);
|
|
710
|
+
<Button
|
|
711
|
+
data-active={String(active)}
|
|
712
|
+
onClick={() => {
|
|
713
|
+
active = !active;
|
|
714
|
+
}}
|
|
715
|
+
ref={(el: HTMLElement) => {
|
|
716
|
+
capturedElement = el;
|
|
717
|
+
refCallCount++;
|
|
718
|
+
}}
|
|
719
|
+
>
|
|
720
|
+
{'content'}
|
|
721
|
+
</Button>
|
|
722
|
+
</>;
|
|
697
723
|
}
|
|
698
724
|
|
|
699
725
|
render(App);
|
|
@@ -717,32 +743,34 @@ describe('dynamic DOM elements', () => {
|
|
|
717
743
|
it('handles ref on dynamic element with spread props containing reactive values', () => {
|
|
718
744
|
let capturedElement: HTMLElement | null = null;
|
|
719
745
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
+
function Button(props: any) {
|
|
747
|
+
return <>
|
|
748
|
+
const el = track('button');
|
|
749
|
+
<@el {...props} />
|
|
750
|
+
</>;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function App() {
|
|
754
|
+
return <>
|
|
755
|
+
let &[active] = track(false);
|
|
756
|
+
let &[buttonProps] = track(
|
|
757
|
+
() => ({
|
|
758
|
+
'data-active': active,
|
|
759
|
+
}),
|
|
760
|
+
);
|
|
761
|
+
<Button
|
|
762
|
+
{...buttonProps}
|
|
763
|
+
onClick={() => {
|
|
764
|
+
active = !active;
|
|
765
|
+
}}
|
|
766
|
+
ref={(el: HTMLElement) => {
|
|
767
|
+
capturedElement = el;
|
|
768
|
+
}}
|
|
769
|
+
>
|
|
770
|
+
{'content: '}
|
|
771
|
+
{active}
|
|
772
|
+
</Button>
|
|
773
|
+
</>;
|
|
746
774
|
}
|
|
747
775
|
|
|
748
776
|
render(App);
|
|
@@ -764,29 +792,32 @@ describe('dynamic DOM elements', () => {
|
|
|
764
792
|
let refCallCount = 0;
|
|
765
793
|
let capturedElement: HTMLElement | null = null;
|
|
766
794
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
active
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
795
|
+
function Button(props: any) {
|
|
796
|
+
return <>
|
|
797
|
+
const el = track('button');
|
|
798
|
+
<@el {...props} />
|
|
799
|
+
</>;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function App() {
|
|
803
|
+
return <>
|
|
804
|
+
let &[active] = track(false);
|
|
805
|
+
<Button
|
|
806
|
+
data-active={String(active)}
|
|
807
|
+
onClick={() => {
|
|
808
|
+
active = !active;
|
|
809
|
+
}}
|
|
810
|
+
ref={(el: HTMLElement) => {
|
|
811
|
+
capturedElement = el;
|
|
812
|
+
refCallCount++;
|
|
813
|
+
return () => {
|
|
814
|
+
cleanupCount++;
|
|
815
|
+
};
|
|
816
|
+
}}
|
|
817
|
+
>
|
|
818
|
+
{'content'}
|
|
819
|
+
</Button>
|
|
820
|
+
</>;
|
|
790
821
|
}
|
|
791
822
|
|
|
792
823
|
render(App);
|
|
@@ -806,14 +837,16 @@ describe('dynamic DOM elements', () => {
|
|
|
806
837
|
});
|
|
807
838
|
|
|
808
839
|
it('should remove and add back a text node in a conditional statement with a tracked', () => {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
{
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
840
|
+
function App() {
|
|
841
|
+
return <>
|
|
842
|
+
let &[b] = track(true);
|
|
843
|
+
<div>
|
|
844
|
+
if (b) {
|
|
845
|
+
{'Inside if'}
|
|
846
|
+
}
|
|
847
|
+
</div>
|
|
848
|
+
<button onClick={() => (b = !b)}>{'Toggle b'}</button>
|
|
849
|
+
</>;
|
|
817
850
|
}
|
|
818
851
|
|
|
819
852
|
render(App);
|