ripple 0.3.68 → 0.3.70

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.
Files changed (182) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/package.json +3 -3
  3. package/src/jsx-runtime.d.ts +2 -2
  4. package/src/runtime/element.js +1 -1
  5. package/src/runtime/index-client.js +11 -11
  6. package/src/runtime/index-server.js +7 -4
  7. package/src/runtime/internal/client/bindings.js +1 -1
  8. package/src/runtime/internal/client/blocks.js +13 -4
  9. package/src/runtime/internal/client/component.js +55 -0
  10. package/src/runtime/internal/client/composite.js +4 -2
  11. package/src/runtime/internal/client/expression.js +65 -7
  12. package/src/runtime/internal/client/hmr.js +54 -43
  13. package/src/runtime/internal/client/index.js +5 -1
  14. package/src/runtime/internal/client/portal.js +70 -69
  15. package/src/runtime/internal/client/render.js +3 -0
  16. package/src/runtime/internal/server/index.js +92 -8
  17. package/tests/client/__snapshots__/html.test.tsrx.snap +3 -3
  18. package/tests/client/array/array.copy-within.test.tsrx +33 -31
  19. package/tests/client/array/array.derived.test.tsrx +186 -169
  20. package/tests/client/array/array.iteration.test.tsrx +40 -37
  21. package/tests/client/array/array.mutations.test.tsrx +113 -101
  22. package/tests/client/array/array.static.test.tsrx +119 -101
  23. package/tests/client/array/array.to-methods.test.tsrx +24 -21
  24. package/tests/client/async-suspend.test.tsrx +247 -246
  25. package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +0 -1
  26. package/tests/client/basic/basic.attributes.test.tsrx +428 -423
  27. package/tests/client/basic/basic.collections.test.tsrx +109 -102
  28. package/tests/client/basic/basic.components.test.tsrx +323 -205
  29. package/tests/client/basic/basic.errors.test.tsrx +91 -91
  30. package/tests/client/basic/basic.events.test.tsrx +114 -115
  31. package/tests/client/basic/basic.get-set.test.tsrx +97 -87
  32. package/tests/client/basic/basic.hmr.test.tsrx +19 -16
  33. package/tests/client/basic/basic.reactivity.test.tsrx +199 -191
  34. package/tests/client/basic/basic.rendering.test.tsrx +272 -182
  35. package/tests/client/basic/basic.styling.test.tsrx +23 -22
  36. package/tests/client/basic/basic.utilities.test.tsrx +10 -8
  37. package/tests/client/boundaries.test.tsrx +26 -26
  38. package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +5 -5
  39. package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +5 -5
  40. package/tests/client/compiler/compiler.assignments.test.tsrx +77 -81
  41. package/tests/client/compiler/compiler.attributes.test.tsrx +15 -15
  42. package/tests/client/compiler/compiler.basic.test.tsrx +322 -314
  43. package/tests/client/compiler/compiler.regex.test.tsrx +44 -47
  44. package/tests/client/compiler/compiler.tracked-access.test.tsrx +38 -38
  45. package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
  46. package/tests/client/compiler/compiler.typescript.test.tsrx +2 -2
  47. package/tests/client/composite/composite.dynamic-components.test.tsrx +47 -48
  48. package/tests/client/composite/composite.generics.test.tsrx +168 -192
  49. package/tests/client/composite/composite.props.test.tsrx +97 -81
  50. package/tests/client/composite/composite.reactivity.test.tsrx +177 -147
  51. package/tests/client/composite/composite.render.test.tsrx +122 -105
  52. package/tests/client/computed-properties.test.tsrx +28 -28
  53. package/tests/client/context.test.tsrx +21 -21
  54. package/tests/client/css/global-additional-cases.test.tsrx +58 -58
  55. package/tests/client/css/global-advanced-selectors.test.tsrx +16 -16
  56. package/tests/client/css/global-at-rules.test.tsrx +10 -10
  57. package/tests/client/css/global-basic.test.tsrx +14 -14
  58. package/tests/client/css/global-classes-ids.test.tsrx +14 -14
  59. package/tests/client/css/global-combinators.test.tsrx +10 -10
  60. package/tests/client/css/global-complex-nesting.test.tsrx +14 -14
  61. package/tests/client/css/global-edge-cases.test.tsrx +18 -18
  62. package/tests/client/css/global-keyframes.test.tsrx +12 -12
  63. package/tests/client/css/global-nested.test.tsrx +10 -10
  64. package/tests/client/css/global-pseudo.test.tsrx +12 -12
  65. package/tests/client/css/global-scoping.test.tsrx +20 -20
  66. package/tests/client/css/style-identifier.test.tsrx +126 -259
  67. package/tests/client/date.test.tsrx +146 -133
  68. package/tests/client/dynamic-elements.test.tsrx +398 -365
  69. package/tests/client/events.test.tsrx +292 -290
  70. package/tests/client/for.test.tsrx +156 -153
  71. package/tests/client/head.test.tsrx +105 -96
  72. package/tests/client/html.test.tsrx +122 -26
  73. package/tests/client/input-value.test.tsrx +1361 -1314
  74. package/tests/client/lazy-array.test.tsrx +16 -13
  75. package/tests/client/lazy-destructuring.test.tsrx +257 -213
  76. package/tests/client/map.test.tsrx +65 -60
  77. package/tests/client/media-query.test.tsrx +22 -20
  78. package/tests/client/object.test.tsrx +87 -81
  79. package/tests/client/portal.test.tsrx +57 -51
  80. package/tests/client/ref.test.tsrx +233 -202
  81. package/tests/client/return.test.tsrx +71 -2560
  82. package/tests/client/set.test.tsrx +54 -45
  83. package/tests/client/svg.test.tsrx +216 -186
  84. package/tests/client/switch.test.tsrx +194 -193
  85. package/tests/client/track-async-hydration.test.tsrx +18 -14
  86. package/tests/client/tracked-index-access.test.tsrx +28 -18
  87. package/tests/client/try.test.tsrx +675 -548
  88. package/tests/client/tsx.test.tsrx +373 -311
  89. package/tests/client/typescript-generics.test.tsrx +145 -145
  90. package/tests/client/url/url.derived.test.tsrx +33 -28
  91. package/tests/client/url/url.parsing.test.tsrx +61 -51
  92. package/tests/client/url/url.partial-removal.test.tsrx +56 -48
  93. package/tests/client/url/url.reactivity.test.tsrx +142 -125
  94. package/tests/client/url/url.serialization.test.tsrx +13 -11
  95. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +34 -29
  96. package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +25 -21
  97. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +50 -45
  98. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +111 -99
  99. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +49 -43
  100. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +14 -12
  101. package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +16 -14
  102. package/tests/hydration/basic.test.js +3 -3
  103. package/tests/hydration/compiled/client/basic.js +586 -651
  104. package/tests/hydration/compiled/client/composite.js +79 -104
  105. package/tests/hydration/compiled/client/events.js +140 -148
  106. package/tests/hydration/compiled/client/for.js +1005 -1018
  107. package/tests/hydration/compiled/client/head.js +124 -134
  108. package/tests/hydration/compiled/client/hmr.js +41 -48
  109. package/tests/hydration/compiled/client/html-in-template.js +38 -41
  110. package/tests/hydration/compiled/client/html.js +970 -1314
  111. package/tests/hydration/compiled/client/if-children.js +234 -249
  112. package/tests/hydration/compiled/client/if.js +182 -189
  113. package/tests/hydration/compiled/client/mixed-control-flow.js +347 -303
  114. package/tests/hydration/compiled/client/nested-control-flow.js +1084 -832
  115. package/tests/hydration/compiled/client/portal.js +65 -85
  116. package/tests/hydration/compiled/client/reactivity.js +84 -90
  117. package/tests/hydration/compiled/client/return.js +38 -1939
  118. package/tests/hydration/compiled/client/switch.js +218 -224
  119. package/tests/hydration/compiled/client/track-async-serialization.js +250 -259
  120. package/tests/hydration/compiled/client/try.js +123 -132
  121. package/tests/hydration/compiled/server/basic.js +773 -831
  122. package/tests/hydration/compiled/server/composite.js +166 -191
  123. package/tests/hydration/compiled/server/events.js +170 -184
  124. package/tests/hydration/compiled/server/for.js +851 -909
  125. package/tests/hydration/compiled/server/head.js +206 -216
  126. package/tests/hydration/compiled/server/hmr.js +64 -72
  127. package/tests/hydration/compiled/server/html-in-template.js +42 -76
  128. package/tests/hydration/compiled/server/html.js +1362 -1667
  129. package/tests/hydration/compiled/server/if-children.js +419 -445
  130. package/tests/hydration/compiled/server/if.js +194 -208
  131. package/tests/hydration/compiled/server/mixed-control-flow.js +249 -257
  132. package/tests/hydration/compiled/server/nested-control-flow.js +491 -515
  133. package/tests/hydration/compiled/server/portal.js +152 -160
  134. package/tests/hydration/compiled/server/reactivity.js +94 -106
  135. package/tests/hydration/compiled/server/return.js +28 -2172
  136. package/tests/hydration/compiled/server/switch.js +274 -286
  137. package/tests/hydration/compiled/server/track-async-serialization.js +340 -358
  138. package/tests/hydration/compiled/server/try.js +167 -185
  139. package/tests/hydration/components/basic.tsrx +320 -272
  140. package/tests/hydration/components/composite.tsrx +44 -32
  141. package/tests/hydration/components/events.tsrx +101 -91
  142. package/tests/hydration/components/for.tsrx +510 -452
  143. package/tests/hydration/components/head.tsrx +87 -80
  144. package/tests/hydration/components/hmr.tsrx +22 -17
  145. package/tests/hydration/components/html-in-template.tsrx +22 -17
  146. package/tests/hydration/components/html.tsrx +525 -443
  147. package/tests/hydration/components/if-children.tsrx +158 -148
  148. package/tests/hydration/components/if.tsrx +109 -95
  149. package/tests/hydration/components/mixed-control-flow.tsrx +100 -96
  150. package/tests/hydration/components/nested-control-flow.tsrx +215 -203
  151. package/tests/hydration/components/portal.tsrx +41 -34
  152. package/tests/hydration/components/reactivity.tsrx +37 -27
  153. package/tests/hydration/components/return.tsrx +12 -556
  154. package/tests/hydration/components/switch.tsrx +120 -114
  155. package/tests/hydration/components/track-async-serialization.tsrx +107 -91
  156. package/tests/hydration/components/try.tsrx +55 -40
  157. package/tests/hydration/html.test.js +4 -4
  158. package/tests/hydration/return.test.js +13 -532
  159. package/tests/server/await.test.tsrx +3 -3
  160. package/tests/server/basic.attributes.test.tsrx +264 -195
  161. package/tests/server/basic.components.test.tsrx +296 -169
  162. package/tests/server/basic.test.tsrx +300 -198
  163. package/tests/server/compiler.test.tsrx +62 -60
  164. package/tests/server/composite.props.test.tsrx +77 -63
  165. package/tests/server/composite.test.tsrx +168 -192
  166. package/tests/server/context.test.tsrx +18 -12
  167. package/tests/server/dynamic-elements.test.tsrx +197 -180
  168. package/tests/server/for.test.tsrx +85 -78
  169. package/tests/server/head.test.tsrx +50 -43
  170. package/tests/server/html-nesting-validation.test.tsrx +8 -8
  171. package/tests/server/if.test.tsrx +57 -51
  172. package/tests/server/lazy-destructuring.test.tsrx +366 -294
  173. package/tests/server/return.test.tsrx +76 -1355
  174. package/tests/server/streaming-ssr.test.tsrx +4 -75
  175. package/tests/server/style-identifier.test.tsrx +169 -131
  176. package/tests/server/switch.test.tsrx +91 -85
  177. package/tests/server/track-async-serialization.test.tsrx +105 -85
  178. package/tests/server/try.test.tsrx +374 -280
  179. package/tests/utils/compiler-compat-config.test.js +2 -2
  180. package/tests/utils/runtime-imports.test.js +10 -0
  181. package/types/index.d.ts +8 -0
  182. 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
- component App() {
7
- let tag = track('div');
8
-
9
- <@tag>{'Hello World'}</@tag>
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
- component App() {
22
- let obj = { tag: track('div') };
23
-
24
- <obj.tag.value>{'Hello World'}</obj.tag.value>
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
- component App() {
35
- let obj = track({ tag: track('div') });
36
- let tag = obj.value.tag;
37
-
38
- <@tag>{'Hello World'}</@tag>
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
- component App() {
51
- let obj = track({ tag: track('div') });
52
- let tag = obj.value['tag'];
53
-
54
- <@tag>{'Hello World'}</@tag>
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
- component App() {
66
- let &[tag] = track('div');
67
-
68
- <button
69
- onClick={() => {
70
- tag = 'span';
71
- }}
72
- >
73
- {'Change Tag'}
74
- </button>
75
- <@tag id="dynamic">{'Hello World'}</@tag>
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
- component App() {
97
- let tag = track('input');
98
-
99
- <@tag type="text" value="test" />
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
- component App() {
111
- let tag = track('div');
112
- let &[className] = track('test-class');
113
-
114
- <@tag class={className} id="test" data-testid="dynamic-element">{'Content'}</@tag>
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
- component App() {
127
- let outerTag = track('div');
128
- let innerTag = track('span');
129
-
130
- <@outerTag class="outer">
131
- <@innerTag class="inner">{'Nested content'}</@innerTag>
132
- </@outerTag>
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
- component App() {
147
- let tag = track('div');
148
- let &[active] = track(true);
149
-
150
- <@tag class={{ active: active, 'dynamic-element': true }}>{'Element with class object'}</@tag>
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
- component App() {
162
- let tag = track('span');
163
-
164
- <@tag
165
- style={{
166
- color: 'red',
167
- fontSize: '16px',
168
- fontWeight: 'bold',
169
- }}
170
- >
171
- {'Styled dynamic element'}
172
- </@tag>
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
- component App() {
185
- let tag = track('section');
186
- const attrs = {
187
- id: 'spread-section',
188
- 'data-testid': 'spread-test',
189
- class: 'spread-class',
190
- };
191
-
192
- <@tag {...attrs} data-extra="additional">{'Element with spread attributes'}</@tag>
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
- component App() {
208
- let tag = track('article');
209
-
210
- <@tag
211
- {ref (node: HTMLElement) => {
212
- capturedElement = node;
213
- }}
214
- id="ref-test"
215
- >
216
- {'Element with ref'}
217
- </@tag>
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 ref={...}, {ref ...}, and named ref props on dynamic DOM elements', () => {
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
- component App() {
234
- let tag = track('input');
235
- let input: HTMLInputElement | undefined;
236
- const state: { anonymous?: HTMLInputElement } = {};
237
-
238
- <@tag
239
- id="dynamic-ref-combo"
240
- type="text"
241
- ref={input}
242
- {ref state.anonymous}
243
- input_ref={ref (node: HTMLInputElement | null) => {
244
- namedRefElement = node;
245
- }}
246
- />
247
-
248
- effect(() => {
249
- refAttrElement = input ?? null;
250
- anonymousRefElement = state.anonymous ?? null;
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 ref forms through a dynamic component that spreads props', () => {
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
- component Child(props: PropsWithExtras<{}>) {
272
- <input id="dynamic-component-ref-combo" type="text" {...props} />
273
- }
274
-
275
- component App() {
276
- let dynamic = track(() => Child);
277
- let input: HTMLInputElement | undefined;
278
- const state: { anonymous?: HTMLInputElement } = {};
279
-
280
- <@dynamic
281
- ref={input}
282
- {ref state.anonymous}
283
- input_ref={ref (node: HTMLInputElement | null) => {
284
- namedRefElement = node;
285
- }}
286
- />
287
-
288
- effect(() => {
289
- refAttrElement = input ?? null;
290
- anonymousRefElement = state.anonymous ?? null;
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
- component App() {
308
- let tag = track('header');
309
-
310
- function elementRef(node: HTMLElement) {
311
- // Set an attribute on the element to prove ref was called
312
- node.setAttribute('data-spread-ref-called', 'true');
313
- node.setAttribute('data-spread-ref-tag', node.tagName.toLowerCase());
314
- }
315
-
316
- const dynamicProps = {
317
- id: 'spread-ref-test',
318
- class: 'ref-element',
319
- [createRefKey()]: elementRef,
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
- component App() {
338
- let tag = track('div');
339
- let &[count] = track(0);
340
-
341
- <button
342
- onClick={() => {
343
- count++;
344
- }}
345
- >
346
- {'Increment'}
347
- </button>
348
- <@tag
349
- id={count % 2 ? 'even' : 'odd'}
350
- class={count % 2 ? 'even-class' : 'odd-class'}
351
- data-count={count}
352
- >
353
- {'Count: '}
354
- {count}
355
- </@tag>
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
- component App() {
392
- let tag = track('div');
393
-
394
- <@tag class="test-class">{'Dynamic element'}</@tag>
395
-
396
- <style>
397
- .test-class {
398
- color: red;
399
- }
400
- </style>
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
- component App() {
417
- let tag = track('button');
418
- let &[count] = track(0);
419
-
420
- <@tag
421
- class={count % 2 ? 'even' : 'odd'}
422
- id={count % 2 ? 'even' : 'odd'}
423
- onClick={() => {
424
- count++;
425
- }}
426
- >
427
- {'Count: '}
428
- {count}
429
- </@tag>
430
-
431
- <style>
432
- .even {
433
- background-color: green;
434
- color: white;
435
- }
436
- .odd {
437
- background-color: red;
438
- color: white;
439
- }
440
- </style>
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
- component DynamicButton(&{ ...rest }: PropsWithExtras<{
504
+ function DynamicButton(&{ ...rest }: PropsWithExtras<{
487
505
  class: string;
488
506
  id: string;
489
507
  onClick: EventListener;
490
508
  }>) {
491
- const tag = track('button');
492
- <@tag {...rest}>{rest.class}</@tag>
493
-
494
- <style>
495
- .even {
496
- background-color: green;
497
- }
498
- .odd {
499
- background-color: red;
500
- }
501
- </style>
502
- }
503
-
504
- component App() {
505
- let &[count] = track(0);
506
- <DynamicButton
507
- class={count % 2 ? 'even' : 'odd'}
508
- id={count % 2 ? 'even' : 'odd'}
509
- onClick={() => {
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
- component App() {
548
- let tag = track('div');
549
-
550
- <@tag class="scoped">
551
- <p>{'Scoped dynamic element'}</p>
552
- </@tag>
553
-
554
- <style>
555
- .scoped {
556
- color: blue;
557
- }
558
- </style>
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
- component App() {
571
- let tag = track('div');
572
-
573
- <@tag class="scoped">
574
- <p>{'Scoped dynamic element'}</p>
575
- </@tag>
576
-
577
- <style>
578
- div {
579
- color: blue;
580
- }
581
- </style>
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
- component Child() {
594
- <div class="child">
595
- <p>{'I am a child component'}</p>
596
- </div>
597
-
598
- <style>
599
- .child {
600
- color: blue;
601
- }
602
- </style>
603
- }
604
-
605
- component App() {
606
- let tag = track('div');
607
-
608
- <@tag class="scoped">
609
- <p>{'Scoped dynamic element'}</p>
610
- <Child />
611
- </@tag>
612
-
613
- <style>
614
- div {
615
- color: blue;
616
- }
617
- </style>
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
- component Child() {
639
- <div class="child">
640
- <p>{'I am a child component'}</p>
641
- </div>
642
-
643
- <style>
644
- .child {
645
- color: green;
646
- }
647
- </style>
648
- }
649
-
650
- component App() {
651
- let tag = track(() => Child);
652
-
653
- <@tag />
654
-
655
- <style>
656
- .child {
657
- color: red;
658
- }
659
- </style>
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
- component Button(props: any) {
678
- const el = track('button');
679
- <@el {...props} />
680
- }
681
-
682
- component App() {
683
- let &[active] = track(false);
684
-
685
- <Button
686
- data-active={String(active)}
687
- onClick={() => {
688
- active = !active;
689
- }}
690
- {ref (el: HTMLElement) => {
691
- capturedElement = el;
692
- refCallCount++;
693
- }}
694
- >
695
- {'content'}
696
- </Button>
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
- component Button(props: any) {
721
- const el = track('button');
722
- <@el {...props} />
723
- }
724
-
725
- component App() {
726
- let &[active] = track(false);
727
-
728
- let &[buttonProps] = track(
729
- () => ({
730
- 'data-active': active,
731
- }),
732
- );
733
-
734
- <Button
735
- {...buttonProps}
736
- onClick={() => {
737
- active = !active;
738
- }}
739
- {ref (el: HTMLElement) => {
740
- capturedElement = el;
741
- }}
742
- >
743
- {'content: '}
744
- {active}
745
- </Button>
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
- component Button(props: any) {
768
- const el = track('button');
769
- <@el {...props} />
770
- }
771
-
772
- component App() {
773
- let &[active] = track(false);
774
-
775
- <Button
776
- data-active={String(active)}
777
- onClick={() => {
778
- active = !active;
779
- }}
780
- {ref (el: HTMLElement) => {
781
- capturedElement = el;
782
- refCallCount++;
783
- return () => {
784
- cleanupCount++;
785
- };
786
- }}
787
- >
788
- {'content'}
789
- </Button>
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
- component App() {
810
- let &[b] = track(true);
811
- <div>
812
- if (b) {
813
- {'Inside if'}
814
- }
815
- </div>
816
- <button onClick={() => (b = !b)}>{'Toggle b'}</button>
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);