cx 26.0.11 → 26.0.13

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 (366) hide show
  1. package/build/charts/ScatterGraph.d.ts +4 -4
  2. package/build/charts/ScatterGraph.js +2 -2
  3. package/build/data/ArrayElementView.spec.d.ts +1 -0
  4. package/build/data/ArrayElementView.spec.js +81 -0
  5. package/build/data/Binding.spec.d.ts +1 -0
  6. package/build/data/Binding.spec.js +61 -0
  7. package/build/data/Expression.spec.d.ts +1 -0
  8. package/build/data/Expression.spec.js +196 -0
  9. package/build/data/Grouper.spec.d.ts +1 -0
  10. package/build/data/Grouper.spec.js +48 -0
  11. package/build/data/Ref.spec.d.ts +1 -0
  12. package/build/data/Ref.spec.js +72 -0
  13. package/build/data/Selector.d.ts +1 -1
  14. package/build/data/Store.spec.d.ts +1 -0
  15. package/build/data/Store.spec.js +19 -0
  16. package/build/data/StoreRef.spec.d.ts +1 -0
  17. package/build/data/StoreRef.spec.js +22 -0
  18. package/build/data/StringTemplate.spec.d.ts +1 -0
  19. package/build/data/StringTemplate.spec.js +112 -0
  20. package/build/data/StructuredSelector.spec.d.ts +1 -0
  21. package/build/data/StructuredSelector.spec.js +102 -0
  22. package/build/data/View.spec.d.ts +1 -0
  23. package/build/data/View.spec.js +44 -0
  24. package/build/data/ZoomIntoPropertyView.spec.d.ts +1 -0
  25. package/build/data/ZoomIntoPropertyView.spec.js +54 -0
  26. package/build/data/comparer.spec.d.ts +1 -0
  27. package/build/data/comparer.spec.js +50 -0
  28. package/build/data/computable.d.ts +3 -6
  29. package/build/data/computable.spec.d.ts +1 -0
  30. package/build/data/computable.spec.js +56 -0
  31. package/build/data/createAccessorModelProxy.d.ts +5 -3
  32. package/build/data/createAccessorModelProxy.spec.d.ts +1 -0
  33. package/build/data/createAccessorModelProxy.spec.js +30 -0
  34. package/build/data/createStructuredSelector.spec.d.ts +1 -0
  35. package/build/data/createStructuredSelector.spec.js +42 -0
  36. package/build/data/diff/diffs.spec.d.ts +1 -0
  37. package/build/data/diff/diffs.spec.js +45 -0
  38. package/build/data/getAccessor.spec.d.ts +1 -0
  39. package/build/data/getAccessor.spec.js +10 -0
  40. package/build/data/getSelector.spec.d.ts +1 -0
  41. package/build/data/getSelector.spec.js +36 -0
  42. package/build/data/ops/append.spec.d.ts +1 -0
  43. package/build/data/ops/append.spec.js +24 -0
  44. package/build/data/ops/filter.spec.d.ts +1 -0
  45. package/build/data/ops/filter.spec.js +25 -0
  46. package/build/data/ops/findTreeNode.d.ts +1 -1
  47. package/build/data/ops/findTreeNode.js +1 -1
  48. package/build/data/ops/findTreeNode.spec.d.ts +1 -0
  49. package/build/data/ops/findTreeNode.spec.js +20 -0
  50. package/build/data/ops/findTreePath.d.ts +1 -1
  51. package/build/data/ops/merge.spec.d.ts +1 -0
  52. package/build/data/ops/merge.spec.js +23 -0
  53. package/build/data/ops/removeTreeNodes.d.ts +1 -1
  54. package/build/data/ops/removeTreeNodes.js +1 -1
  55. package/build/data/ops/removeTreeNodes.spec.d.ts +1 -0
  56. package/build/data/ops/removeTreeNodes.spec.js +35 -0
  57. package/build/data/ops/updateArray.spec.d.ts +1 -0
  58. package/build/data/ops/updateArray.spec.js +33 -0
  59. package/build/data/ops/updateTree.d.ts +1 -1
  60. package/build/data/ops/updateTree.spec.d.ts +1 -0
  61. package/build/data/ops/updateTree.spec.js +44 -0
  62. package/build/hooks/invokeCallback.spec.d.ts +1 -0
  63. package/build/hooks/invokeCallback.spec.js +44 -0
  64. package/build/hooks/resolveCallback.spec.d.ts +1 -0
  65. package/build/hooks/resolveCallback.spec.js +35 -0
  66. package/build/hooks/store.spec.d.ts +1 -0
  67. package/build/hooks/store.spec.js +48 -0
  68. package/build/hooks/useTrigger.spec.d.ts +1 -0
  69. package/build/hooks/useTrigger.spec.js +59 -0
  70. package/build/jsx-runtime.d.ts +10 -10
  71. package/build/jsx-runtime.js +6 -0
  72. package/build/svg/util/Rect.d.ts +1 -1
  73. package/build/ui/ContentResolver.d.ts +16 -6
  74. package/build/ui/Controller.d.ts +7 -0
  75. package/build/ui/Controller.js +2 -1
  76. package/build/ui/Controller.spec.d.ts +1 -0
  77. package/build/ui/Controller.spec.js +247 -0
  78. package/build/ui/Cx.spec.d.ts +1 -0
  79. package/build/ui/Cx.spec.js +153 -0
  80. package/build/ui/DataProxy.spec.d.ts +1 -0
  81. package/build/ui/DataProxy.spec.js +208 -0
  82. package/build/ui/Instance.d.ts +1 -1
  83. package/build/ui/Instance.js +10 -10
  84. package/build/ui/IsolatedScope.spec.d.ts +1 -0
  85. package/build/ui/IsolatedScope.spec.js +42 -0
  86. package/build/ui/Prop.d.ts +12 -1
  87. package/build/ui/PureContainer.spec.d.ts +1 -0
  88. package/build/ui/PureContainer.spec.js +149 -0
  89. package/build/ui/Repeater.d.ts +3 -3
  90. package/build/ui/Repeater.spec.d.ts +1 -0
  91. package/build/ui/Repeater.spec.js +109 -0
  92. package/build/ui/Rescope.spec.d.ts +1 -0
  93. package/build/ui/Rescope.spec.js +134 -0
  94. package/build/ui/Restate.spec.d.ts +1 -0
  95. package/build/ui/Restate.spec.js +257 -0
  96. package/build/ui/Text.d.ts +14 -2
  97. package/build/ui/Text.js +3 -0
  98. package/build/ui/adapter/ArrayAdapter.js +4 -1
  99. package/build/ui/adapter/ArrayAdapter.spec.d.ts +1 -0
  100. package/build/ui/adapter/ArrayAdapter.spec.js +44 -0
  101. package/build/ui/adapter/TreeAdapter.spec.d.ts +1 -0
  102. package/build/ui/adapter/TreeAdapter.spec.js +71 -0
  103. package/build/ui/app/Url.spec.d.ts +1 -0
  104. package/build/ui/app/Url.spec.js +43 -0
  105. package/build/ui/app/startHotAppLoop.js +1 -1
  106. package/build/ui/createFunctionalComponent.d.ts +14 -1
  107. package/build/ui/createFunctionalComponent.js +7 -4
  108. package/build/ui/createFunctionalComponent.spec.d.ts +1 -0
  109. package/build/ui/createFunctionalComponent.spec.js +272 -0
  110. package/build/ui/expr.d.ts +3 -1
  111. package/build/ui/exprHelpers.d.ts +32 -0
  112. package/build/ui/exprHelpers.js +61 -0
  113. package/build/ui/index.d.ts +1 -0
  114. package/build/ui/index.js +1 -0
  115. package/build/ui/layout/ContentPlaceholder.spec.d.ts +1 -0
  116. package/build/ui/layout/ContentPlaceholder.spec.js +333 -0
  117. package/build/ui/layout/FirstVisibleChildLayout.spec.d.ts +1 -0
  118. package/build/ui/layout/FirstVisibleChildLayout.spec.js +101 -0
  119. package/build/ui/selection/KeySelection.d.ts +0 -8
  120. package/build/ui/selection/Selection.d.ts +9 -3
  121. package/build/util/Console.d.ts +1 -0
  122. package/build/util/Console.js +7 -3
  123. package/build/util/Format.spec.d.ts +1 -0
  124. package/build/util/Format.spec.js +58 -0
  125. package/build/util/TraversalStack.spec.d.ts +1 -0
  126. package/build/util/TraversalStack.spec.js +43 -0
  127. package/build/util/date/upperBoundCheck.spec.d.ts +1 -0
  128. package/build/util/date/upperBoundCheck.spec.js +22 -0
  129. package/build/util/getSearchQueryPredicate.spec.d.ts +1 -0
  130. package/build/util/getSearchQueryPredicate.spec.js +33 -0
  131. package/build/util/isValidIdentifierName.spec.d.ts +1 -0
  132. package/build/util/isValidIdentifierName.spec.js +28 -0
  133. package/build/util/routeAppend.spec.d.ts +1 -0
  134. package/build/util/routeAppend.spec.js +14 -0
  135. package/build/widgets/AccessorBindings.spec.d.ts +1 -0
  136. package/build/widgets/AccessorBindings.spec.js +40 -0
  137. package/build/widgets/Button.d.ts +3 -6
  138. package/build/widgets/Button.js +1 -1
  139. package/build/widgets/DocumentTitle.d.ts +2 -0
  140. package/build/widgets/Heading.d.ts +2 -2
  141. package/build/widgets/HtmlElement.d.ts +33 -8
  142. package/build/widgets/HtmlElement.js +7 -9
  143. package/build/widgets/HtmlElement.spec.d.ts +1 -0
  144. package/build/widgets/HtmlElement.spec.js +38 -0
  145. package/build/widgets/Icon.d.ts +3 -13
  146. package/build/widgets/List.d.ts +4 -0
  147. package/build/widgets/ReactElementWrapper.d.ts +29 -0
  148. package/build/widgets/ReactElementWrapper.js +59 -0
  149. package/build/widgets/drag-drop/DragSource.d.ts +3 -3
  150. package/build/widgets/drag-drop/DragSource.js +2 -3
  151. package/build/widgets/drag-drop/DropZone.d.ts +3 -3
  152. package/build/widgets/drag-drop/DropZone.js +2 -3
  153. package/build/widgets/form/Checkbox.d.ts +2 -0
  154. package/build/widgets/form/ColorField.d.ts +2 -0
  155. package/build/widgets/form/DateTimeField.d.ts +2 -0
  156. package/build/widgets/form/Field.d.ts +0 -2
  157. package/build/widgets/form/LabeledContainer.d.ts +9 -8
  158. package/build/widgets/form/LabeledContainer.js +9 -9
  159. package/build/widgets/form/LookupField.d.ts +57 -9
  160. package/build/widgets/form/MonthField.d.ts +2 -0
  161. package/build/widgets/form/NumberField.d.ts +2 -0
  162. package/build/widgets/form/Radio.d.ts +2 -0
  163. package/build/widgets/form/Select.d.ts +2 -0
  164. package/build/widgets/form/Slider.d.ts +3 -0
  165. package/build/widgets/form/Switch.d.ts +2 -0
  166. package/build/widgets/form/TextField.d.ts +34 -0
  167. package/build/widgets/form/TimeList.d.ts +16 -1
  168. package/build/widgets/form/TimeList.js +34 -62
  169. package/build/widgets/form/UploadButton.d.ts +34 -2
  170. package/build/widgets/form/UploadButton.js +3 -1
  171. package/build/widgets/form/ValidationGroup.spec.d.ts +1 -0
  172. package/build/widgets/form/ValidationGroup.spec.js +62 -0
  173. package/build/widgets/form/Validator.d.ts +33 -2
  174. package/build/widgets/form/Validator.js +3 -0
  175. package/build/widgets/grid/Grid.d.ts +9 -9
  176. package/build/widgets/grid/TreeNode.d.ts +6 -0
  177. package/build/widgets/index.d.ts +1 -0
  178. package/build/widgets/index.js +1 -0
  179. package/build/widgets/nav/MenuItem.d.ts +3 -2
  180. package/build/widgets/nav/Route.spec.d.ts +1 -0
  181. package/build/widgets/nav/Route.spec.js +15 -0
  182. package/build/widgets/nav/Scroller.d.ts +4 -6
  183. package/build/widgets/nav/Scroller.js +6 -3
  184. package/build/widgets/nav/Tab.d.ts +2 -2
  185. package/build/widgets/overlay/ContextMenu.d.ts +3 -3
  186. package/build/widgets/overlay/Overlay.d.ts +2 -1
  187. package/build/widgets/overlay/Overlay.js +1 -1
  188. package/build.js +133 -133
  189. package/dist/data.js +2 -2
  190. package/dist/jsx-runtime.js +6 -1
  191. package/dist/manifest.d.ts +1443 -0
  192. package/dist/manifest.js +852 -804
  193. package/dist/ui.js +91 -5
  194. package/dist/util.js +3 -0
  195. package/dist/widgets.js +520 -161
  196. package/package.json +46 -20
  197. package/src/charts/Chart.ts +108 -108
  198. package/src/charts/ScatterGraph.tsx +6 -6
  199. package/src/data/ArrayElementView.ts +90 -90
  200. package/src/data/AugmentedViewBase.ts +88 -88
  201. package/src/data/Binding.ts +104 -104
  202. package/src/data/ExposedRecordView.ts +95 -95
  203. package/src/data/ExposedValueView.ts +89 -89
  204. package/src/data/Expression.spec.ts +229 -229
  205. package/src/data/Expression.ts +233 -233
  206. package/src/data/Grouper.spec.ts +57 -57
  207. package/src/data/Grouper.ts +158 -158
  208. package/src/data/NestedDataView.ts +43 -43
  209. package/src/data/ReadOnlyDataView.ts +39 -39
  210. package/src/data/Ref.ts +104 -104
  211. package/src/data/Selector.ts +10 -10
  212. package/src/data/Store.ts +52 -52
  213. package/src/data/StoreProxy.ts +19 -19
  214. package/src/data/StoreRef.ts +66 -66
  215. package/src/data/StringTemplate.spec.ts +132 -132
  216. package/src/data/StringTemplate.ts +93 -93
  217. package/src/data/StructuredSelector.spec.ts +113 -113
  218. package/src/data/StructuredSelector.ts +146 -146
  219. package/src/data/SubscribableView.ts +63 -63
  220. package/src/data/ZoomIntoPropertyView.spec.ts +64 -64
  221. package/src/data/ZoomIntoPropertyView.ts +45 -45
  222. package/src/data/computable.spec.ts +87 -62
  223. package/src/data/computable.ts +3 -6
  224. package/src/data/createAccessorModelProxy.spec.tsx +102 -1
  225. package/src/data/createAccessorModelProxy.ts +9 -3
  226. package/src/data/createStructuredSelector.ts +62 -62
  227. package/src/data/getAccessor.spec.ts +11 -11
  228. package/src/data/getAccessor.ts +74 -74
  229. package/src/data/getSelector.spec.ts +43 -43
  230. package/src/data/getSelector.ts +66 -66
  231. package/src/data/ops/filter.spec.ts +35 -35
  232. package/src/data/ops/filter.ts +9 -9
  233. package/src/data/ops/findTreeNode.ts +1 -5
  234. package/src/data/ops/findTreePath.ts +1 -1
  235. package/src/data/ops/merge.ts +13 -13
  236. package/src/data/ops/removeTreeNodes.spec.ts +37 -37
  237. package/src/data/ops/removeTreeNodes.ts +2 -2
  238. package/src/data/ops/updateArray.spec.ts +69 -69
  239. package/src/data/ops/updateArray.ts +31 -31
  240. package/src/data/ops/updateTree.ts +1 -1
  241. package/src/data/test-types.ts +7 -7
  242. package/src/hooks/resolveCallback.spec.tsx +30 -7
  243. package/src/hooks/useTrigger.ts +26 -26
  244. package/src/index.scss +6 -6
  245. package/src/jsx-dev-runtime.ts +4 -4
  246. package/src/jsx-runtime.spec.tsx +402 -0
  247. package/src/jsx-runtime.ts +26 -22
  248. package/src/svg/BoundedObject.ts +101 -101
  249. package/src/svg/util/Rect.ts +105 -105
  250. package/src/ui/CSSHelper.ts +17 -17
  251. package/src/ui/ContentResolver.spec.tsx +172 -19
  252. package/src/ui/ContentResolver.ts +16 -8
  253. package/src/ui/Controller.ts +15 -2
  254. package/src/ui/Culture.ts +159 -159
  255. package/src/ui/DataProxy.ts +55 -55
  256. package/src/ui/FocusManager.ts +171 -171
  257. package/src/ui/Instance.ts +866 -868
  258. package/src/ui/Prop.ts +140 -112
  259. package/src/ui/RenderingContext.ts +99 -99
  260. package/src/ui/Repeater.ts +3 -12
  261. package/src/ui/Rescope.ts +49 -49
  262. package/src/ui/StructuredInstanceDataAccessor.ts +32 -32
  263. package/src/ui/Text.ts +21 -2
  264. package/src/ui/VDOM.ts +34 -34
  265. package/src/ui/adapter/ArrayAdapter.spec.ts +55 -55
  266. package/src/ui/adapter/ArrayAdapter.ts +4 -1
  267. package/src/ui/adapter/TreeAdapter.spec.ts +76 -76
  268. package/src/ui/adapter/TreeAdapter.ts +185 -185
  269. package/src/ui/app/History.ts +133 -133
  270. package/src/ui/app/Url.spec.ts +50 -50
  271. package/src/ui/app/startHotAppLoop.ts +41 -41
  272. package/src/ui/createFunctionalComponent.spec.tsx +53 -0
  273. package/src/ui/createFunctionalComponent.ts +86 -65
  274. package/src/ui/expr.ts +4 -1
  275. package/src/ui/exprHelpers.spec.ts +379 -0
  276. package/src/ui/exprHelpers.ts +78 -0
  277. package/src/ui/index.ts +47 -46
  278. package/src/ui/layout/Content.ts +30 -30
  279. package/src/ui/layout/FirstVisibleChildLayout.spec.tsx +1 -1
  280. package/src/ui/layout/FirstVisibleChildLayout.ts +60 -60
  281. package/src/ui/selection/KeySelection.ts +0 -12
  282. package/src/ui/selection/PropertySelection.ts +87 -87
  283. package/src/ui/selection/Selection.ts +13 -3
  284. package/src/util/Console.ts +13 -11
  285. package/src/util/Format.ts +267 -267
  286. package/src/util/addEventListenerWithOptions.ts +41 -41
  287. package/src/util/browserSupportsPassiveEventHandlers.ts +20 -20
  288. package/src/util/color/rgbToHsl.ts +35 -35
  289. package/src/util/getActiveElement.ts +4 -4
  290. package/src/util/hasKey.ts +18 -18
  291. package/src/util/index.ts +55 -55
  292. package/src/util/innerTextTrim.ts +10 -10
  293. package/src/util/isArray.ts +3 -3
  294. package/src/util/isDataRecord.ts +5 -5
  295. package/src/util/isDefined.ts +3 -3
  296. package/src/util/isString.ts +3 -3
  297. package/src/widgets/AccessorBindings.spec.tsx +26 -0
  298. package/src/widgets/Button.tsx +5 -17
  299. package/src/widgets/DocumentTitle.ts +95 -92
  300. package/src/widgets/Heading.ts +2 -2
  301. package/src/widgets/HtmlElement.spec.helpers.tsx +108 -0
  302. package/src/widgets/HtmlElement.spec.tsx +20 -12
  303. package/src/widgets/HtmlElement.tsx +82 -24
  304. package/src/widgets/Icon.ts +3 -17
  305. package/src/widgets/List.tsx +6 -0
  306. package/src/widgets/ReactElementWrapper.spec.tsx +452 -0
  307. package/src/widgets/ReactElementWrapper.tsx +108 -0
  308. package/src/widgets/Sandbox.ts +103 -103
  309. package/src/widgets/autoFocus.ts +9 -9
  310. package/src/widgets/cx.ts +63 -63
  311. package/src/widgets/drag-drop/DragSource.tsx +3 -4
  312. package/src/widgets/drag-drop/DropZone.tsx +3 -4
  313. package/src/widgets/form/Checkbox.tsx +3 -0
  314. package/src/widgets/form/ColorField.tsx +3 -0
  315. package/src/widgets/form/DateTimeField.tsx +5 -0
  316. package/src/widgets/form/Field.tsx +0 -3
  317. package/src/widgets/form/Label.tsx +1 -0
  318. package/src/widgets/form/LabeledContainer.ts +22 -26
  319. package/src/widgets/form/LookupField.spec.tsx +93 -0
  320. package/src/widgets/form/LookupField.tsx +104 -9
  321. package/src/widgets/form/MonthField.tsx +5 -0
  322. package/src/widgets/form/NumberField.tsx +3 -0
  323. package/src/widgets/form/Radio.tsx +5 -0
  324. package/src/widgets/form/Select.tsx +5 -0
  325. package/src/widgets/form/Slider.tsx +4 -0
  326. package/src/widgets/form/Switch.tsx +3 -0
  327. package/src/widgets/form/TextField.tsx +62 -0
  328. package/src/widgets/form/TimeList.tsx +84 -73
  329. package/src/widgets/form/UploadButton.tsx +53 -2
  330. package/src/widgets/form/Validator.ts +40 -3
  331. package/src/widgets/grid/Grid.tsx +9 -12
  332. package/src/widgets/grid/GridCell.ts +143 -143
  333. package/src/widgets/grid/TreeNode.tsx +9 -0
  334. package/src/widgets/icons/calendar.tsx +17 -17
  335. package/src/widgets/icons/check.tsx +13 -13
  336. package/src/widgets/icons/clear.tsx +15 -15
  337. package/src/widgets/icons/close.tsx +20 -20
  338. package/src/widgets/icons/cx.tsx +38 -38
  339. package/src/widgets/icons/drop-down.tsx +15 -15
  340. package/src/widgets/icons/file.tsx +13 -13
  341. package/src/widgets/icons/folder-open.tsx +15 -15
  342. package/src/widgets/icons/folder.tsx +13 -13
  343. package/src/widgets/icons/forward.tsx +22 -22
  344. package/src/widgets/icons/loading.tsx +24 -24
  345. package/src/widgets/icons/menu.tsx +17 -17
  346. package/src/widgets/icons/pixel-picker.tsx +18 -18
  347. package/src/widgets/icons/search.tsx +13 -13
  348. package/src/widgets/icons/sort-asc.tsx +14 -14
  349. package/src/widgets/icons/square.tsx +18 -18
  350. package/src/widgets/index.ts +1 -0
  351. package/src/widgets/nav/MenuItem.tsx +3 -2
  352. package/src/widgets/nav/Route.ts +142 -142
  353. package/src/widgets/nav/Scroller.tsx +8 -9
  354. package/src/widgets/nav/Tab.ts +2 -2
  355. package/src/widgets/overlay/ContextMenu.ts +42 -42
  356. package/src/widgets/overlay/Dropdown.tsx +762 -762
  357. package/src/widgets/overlay/MsgBox.tsx +141 -141
  358. package/src/widgets/overlay/Overlay.tsx +5 -4
  359. package/src/widgets/overlay/Toast.ts +111 -111
  360. package/src/widgets/overlay/Window.tsx +299 -299
  361. package/src/widgets/overlay/alerts.ts +46 -46
  362. package/src/widgets/overlay/captureMouse.ts +195 -195
  363. package/src/widgets/overlay/createHotPromiseWindowFactory.ts +72 -72
  364. package/src/widgets/overlay/index.d.ts +11 -11
  365. package/src/widgets/overlay/index.ts +11 -11
  366. package/src/widgets/overlay/tooltip-ops.ts +173 -173
@@ -0,0 +1,452 @@
1
+ import { Store } from "../data/Store";
2
+ import assert from "assert";
3
+ import { createTestRenderer } from "../util/test/createTestRenderer";
4
+ import { VDOM } from "../ui/Widget";
5
+ import { act } from "react-test-renderer";
6
+ import {
7
+ ReactFunctionComponent,
8
+ ReactCounterComponent,
9
+ ReactClassComponent,
10
+ ReactPureComponent,
11
+ ReactRefEffectComponent,
12
+ ReactEffectStateComponent,
13
+ ReactPropsComponent,
14
+ } from "./HtmlElement.spec.helpers";
15
+ import { Controller } from "../ui/Controller";
16
+ import { createAccessorModelProxy } from "../data/createAccessorModelProxy";
17
+ import { ReactElementWrapper } from "./ReactElementWrapper";
18
+
19
+ describe("ReactElementWrapper", () => {
20
+ it("renders React components as tag", () => {
21
+ class MyReactComponent extends VDOM.Component<any> {
22
+ render() {
23
+ return VDOM.createElement("div", { className: "my-component" }, this.props.children);
24
+ }
25
+ }
26
+
27
+ let store = new Store();
28
+
29
+ const component = createTestRenderer(
30
+ store,
31
+ <cx>
32
+ <MyReactComponent>
33
+ <span>Child content</span>
34
+ </MyReactComponent>
35
+ </cx>,
36
+ );
37
+
38
+ let tree = component.toJSON();
39
+ assert(tree && !Array.isArray(tree), "Expected single element");
40
+ assert.equal(tree.type, "div");
41
+ assert.equal(tree.props.className, "my-component");
42
+ assert(tree.children && tree.children.length === 1, "Expected one child");
43
+ });
44
+
45
+ it("renders React function components with props", () => {
46
+ let store = new Store();
47
+
48
+ const component = createTestRenderer(
49
+ store,
50
+ <cx>
51
+ <ReactFunctionComponent title="Test Title">
52
+ <span>Child content</span>
53
+ </ReactFunctionComponent>
54
+ </cx>,
55
+ );
56
+
57
+ let tree = component.toJSON();
58
+ assert(tree && !Array.isArray(tree), "Expected single element");
59
+ assert.equal(tree.type, "div");
60
+ assert.equal(tree.props.className, "react-function-component");
61
+ assert(tree.children && tree.children.length === 2, "Expected two children (h3 and div)");
62
+
63
+ let h3 = tree.children[0] as any;
64
+ assert.equal(h3.type, "h3");
65
+ assert.deepEqual(h3.children, ["Test Title"]);
66
+
67
+ let contentDiv = tree.children[1] as any;
68
+ assert.equal(contentDiv.type, "div");
69
+ assert.equal(contentDiv.props.className, "content");
70
+ });
71
+
72
+ it("renders React function components with hooks", () => {
73
+ let store = new Store();
74
+
75
+ const component = createTestRenderer(
76
+ store,
77
+ <cx>
78
+ <ReactCounterComponent initialCount={5} />
79
+ </cx>,
80
+ );
81
+
82
+ let tree = component.toJSON();
83
+ assert(tree && !Array.isArray(tree), "Expected single element");
84
+ assert.equal(tree.type, "div");
85
+ assert.equal(tree.props.className, "react-counter");
86
+ assert(tree.children && tree.children.length === 2, "Expected two children (span and button)");
87
+
88
+ let span = tree.children[0] as any;
89
+ assert.equal(span.type, "span");
90
+ assert.equal(span.props.className, "count");
91
+ assert.deepEqual(span.children, ["5"]);
92
+
93
+ let button = tree.children[1] as any;
94
+ assert.equal(button.type, "button");
95
+ });
96
+
97
+ it("renders React class components with props", () => {
98
+ let store = new Store();
99
+
100
+ const component = createTestRenderer(
101
+ store,
102
+ <cx>
103
+ <ReactClassComponent label="Test Label">
104
+ <span>Class child content</span>
105
+ </ReactClassComponent>
106
+ </cx>,
107
+ );
108
+
109
+ let tree = component.toJSON();
110
+ assert(tree && !Array.isArray(tree), "Expected single element");
111
+ assert.equal(tree.type, "div");
112
+ assert(tree.props.className.includes("react-class-component"), "Expected react-class-component class");
113
+
114
+ assert(tree.children && tree.children.length === 2, "Expected two children (label and div)");
115
+
116
+ let label = tree.children[0] as any;
117
+ assert.equal(label.type, "label");
118
+ assert.deepEqual(label.children, ["Test Label"]);
119
+
120
+ let bodyDiv = tree.children[1] as any;
121
+ assert.equal(bodyDiv.type, "div");
122
+ assert.equal(bodyDiv.props.className, "body");
123
+ });
124
+
125
+ it("renders React PureComponent", () => {
126
+ let store = new Store();
127
+
128
+ const component = createTestRenderer(
129
+ store,
130
+ <cx>
131
+ <ReactPureComponent value="Pure Value" />
132
+ </cx>,
133
+ );
134
+
135
+ let tree = component.toJSON();
136
+ assert(tree && !Array.isArray(tree), "Expected single element");
137
+ assert.equal(tree.type, "span");
138
+ assert.equal(tree.props.className, "react-pure-component");
139
+ assert.deepEqual(tree.children, ["Pure Value"]);
140
+ });
141
+
142
+ it("renders React function component with useRef and useEffect", () => {
143
+ let store = new Store();
144
+ let mountedElement: HTMLDivElement | null = null;
145
+
146
+ const component = createTestRenderer(
147
+ store,
148
+ <cx>
149
+ <ReactRefEffectComponent
150
+ onMount={(el) => {
151
+ mountedElement = el;
152
+ }}
153
+ />
154
+ </cx>,
155
+ );
156
+
157
+ let tree = component.toJSON();
158
+ assert(tree && !Array.isArray(tree), "Expected single element");
159
+ assert.equal(tree.type, "div");
160
+ assert.equal(tree.props.className, "react-ref-effect-component");
161
+ assert.deepEqual(tree.children, ["Component with ref and effect"]);
162
+ });
163
+
164
+ it("renders React function component with useEffect that updates state", () => {
165
+ let store = new Store();
166
+ let component: ReturnType<typeof createTestRenderer>;
167
+
168
+ act(() => {
169
+ component = createTestRenderer(
170
+ store,
171
+ <cx>
172
+ <ReactEffectStateComponent value="Test" />
173
+ </cx>,
174
+ );
175
+ });
176
+
177
+ let tree = component!.toJSON();
178
+ assert(tree && !Array.isArray(tree), "Expected single element");
179
+ assert.equal(tree.type, "div");
180
+ assert.equal(tree.props.className, "react-effect-state-component");
181
+ assert(tree.children && tree.children.length === 2, "Expected two children");
182
+
183
+ let processedSpan = tree.children[0] as any;
184
+ assert.equal(processedSpan.type, "span");
185
+ assert.equal(processedSpan.props.className, "processed");
186
+ // After act(), useEffect should have run and updated state
187
+ assert.deepEqual(processedSpan.children, ["Processed: Test"]);
188
+ });
189
+
190
+ it("translates CxJS accessor bindings to React component props", () => {
191
+ interface StoreModel {
192
+ text: string;
193
+ count: number;
194
+ enabled: boolean;
195
+ tags: string[];
196
+ }
197
+
198
+ let $store = createAccessorModelProxy<StoreModel>();
199
+
200
+ // First verify jsx transform output
201
+ const widget = (
202
+ <cx>
203
+ <ReactPropsComponent text={$store.text} count={$store.count} enabled={$store.enabled} tags={$store.tags} />
204
+ </cx>
205
+ );
206
+
207
+ assert.equal(widget.$type, ReactElementWrapper, "React component should use ReactElementWrapper as $type");
208
+ assert.equal(widget.componentType, ReactPropsComponent, "React component should be set as componentType");
209
+ assert.equal(typeof widget.text, "function", "Accessor chain text should be a function");
210
+ assert.equal(widget.text.toString(), "text", "Accessor chain text should resolve to path");
211
+
212
+ // Now verify rendering
213
+ let store = new Store<StoreModel>({
214
+ data: {
215
+ text: "Bound Text",
216
+ count: 42,
217
+ enabled: true,
218
+ tags: ["a", "b", "c"],
219
+ },
220
+ });
221
+
222
+ const component = createTestRenderer(store, widget);
223
+
224
+ let tree = component.toJSON();
225
+ assert(tree && !Array.isArray(tree), "Expected single element");
226
+ assert.equal(tree.type, "div");
227
+
228
+ let textSpan = tree.children![0] as any;
229
+ assert.deepEqual(textSpan.children, ["Bound Text"]);
230
+
231
+ let countSpan = tree.children![1] as any;
232
+ assert.deepEqual(countSpan.children, ["42"]);
233
+
234
+ let enabledSpan = tree.children![2] as any;
235
+ assert.deepEqual(enabledSpan.children, ["yes"]);
236
+
237
+ let tagsSpan = tree.children![3] as any;
238
+ assert.deepEqual(tagsSpan.children, ["a, b, c"]);
239
+ });
240
+
241
+ it("supports visible prop on React components", () => {
242
+ interface StoreModel {
243
+ show: boolean;
244
+ }
245
+
246
+ let $store = createAccessorModelProxy<StoreModel>();
247
+
248
+ let store = new Store<StoreModel>({
249
+ data: {
250
+ show: false,
251
+ },
252
+ });
253
+
254
+ const component = createTestRenderer(
255
+ store,
256
+ <cx>
257
+ <div>
258
+ <ReactFunctionComponent title="Visible Test" visible={$store.show} />
259
+ <span>Always visible</span>
260
+ </div>
261
+ </cx>,
262
+ );
263
+
264
+ let tree = component.toJSON();
265
+ assert(tree && !Array.isArray(tree), "Expected single element");
266
+ // When visible=false, the React component should not render
267
+ assert.equal(tree.children!.length, 1, "Expected only the span to be visible");
268
+ assert.equal((tree.children![0] as any).type, "span");
269
+
270
+ // Update store to make component visible
271
+ store.set("show", true);
272
+ let tree2 = component.toJSON();
273
+ assert(tree2 && !Array.isArray(tree2), "Expected single element");
274
+ assert.equal(tree2.children!.length, 2, "Expected both children to be visible");
275
+ });
276
+
277
+ it("supports controller prop on React components", () => {
278
+ let controllerInitialized = false;
279
+
280
+ class TestController extends Controller {
281
+ onInit() {
282
+ controllerInitialized = true;
283
+ }
284
+ }
285
+
286
+ interface StoreModel {
287
+ text: string;
288
+ }
289
+
290
+ let $store = createAccessorModelProxy<StoreModel>();
291
+
292
+ let store = new Store<StoreModel>({
293
+ data: {
294
+ text: "Controller Test",
295
+ },
296
+ });
297
+
298
+ const component = createTestRenderer(
299
+ store,
300
+ <cx>
301
+ <ReactPropsComponent text={$store.text} count={10} enabled={true} controller={TestController} />
302
+ </cx>,
303
+ );
304
+
305
+ let tree = component.toJSON();
306
+ assert(tree && !Array.isArray(tree), "Expected single element");
307
+ assert.equal(controllerInitialized, true, "Controller should be initialized");
308
+
309
+ let textSpan = tree.children![0] as any;
310
+ assert.deepEqual(textSpan.children, ["Controller Test"]);
311
+ });
312
+
313
+ it("updates React component when bound store data changes", () => {
314
+ interface StoreModel {
315
+ text: string;
316
+ count: number;
317
+ enabled: boolean;
318
+ }
319
+
320
+ let $store = createAccessorModelProxy<StoreModel>();
321
+
322
+ let store = new Store<StoreModel>({
323
+ data: {
324
+ text: "Initial",
325
+ count: 1,
326
+ enabled: false,
327
+ },
328
+ });
329
+
330
+ const component = createTestRenderer(
331
+ store,
332
+ <cx>
333
+ <ReactPropsComponent text={$store.text} count={$store.count} enabled={$store.enabled} />
334
+ </cx>,
335
+ );
336
+
337
+ let tree1 = component.toJSON() as any;
338
+ assert.deepEqual(tree1.children[0].children, ["Initial"]);
339
+ assert.deepEqual(tree1.children[1].children, ["1"]);
340
+ assert.deepEqual(tree1.children[2].children, ["no"]);
341
+
342
+ // Update store
343
+ store.set("text", "Updated");
344
+ store.set("count", 99);
345
+ store.set("enabled", true);
346
+
347
+ let tree2 = component.toJSON() as any;
348
+ assert.deepEqual(tree2.children[0].children, ["Updated"]);
349
+ assert.deepEqual(tree2.children[1].children, ["99"]);
350
+ assert.deepEqual(tree2.children[2].children, ["yes"]);
351
+ });
352
+
353
+ describe("React component type inference", () => {
354
+ interface RequiredPropsComponentProps {
355
+ label: string;
356
+ value: number;
357
+ onChange: (value: number) => void;
358
+ disabled?: boolean;
359
+ }
360
+
361
+ function RequiredPropsComponent(_props: RequiredPropsComponentProps) {
362
+ return null;
363
+ }
364
+
365
+ it("accepts all required props", () => {
366
+ const widget = (
367
+ <cx>
368
+ <RequiredPropsComponent label="Amount" value={100} onChange={(v) => console.log(v)} />
369
+ </cx>
370
+ );
371
+ assert.ok(widget);
372
+ });
373
+
374
+ it("accepts required props with optional prop", () => {
375
+ const widget = (
376
+ <cx>
377
+ <RequiredPropsComponent label="Amount" value={100} onChange={(v) => console.log(v)} disabled={true} />
378
+ </cx>
379
+ );
380
+ assert.ok(widget);
381
+ });
382
+
383
+ it("rejects missing required prop (label)", () => {
384
+ const widget = (
385
+ <cx>
386
+ {/* @ts-expect-error - label is required but missing */}
387
+ <RequiredPropsComponent value={100} onChange={(v) => console.log(v)} />
388
+ </cx>
389
+ );
390
+ assert.ok(widget);
391
+ });
392
+
393
+ it("rejects missing required prop (onChange)", () => {
394
+ const widget = (
395
+ <cx>
396
+ {/* @ts-expect-error - onChange is required but missing */}
397
+ <RequiredPropsComponent label="Amount" value={100} />
398
+ </cx>
399
+ );
400
+ assert.ok(widget);
401
+ });
402
+
403
+ it("rejects missing all required props", () => {
404
+ const widget = (
405
+ <cx>
406
+ {/* @ts-expect-error - label, value, and onChange are required but missing */}
407
+ <RequiredPropsComponent disabled={true} />
408
+ </cx>
409
+ );
410
+ assert.ok(widget);
411
+ });
412
+
413
+ it("rejects wrong prop type (string for number)", () => {
414
+ const widget = (
415
+ <cx>
416
+ {/* @ts-expect-error - value should be number, not string */}
417
+ <RequiredPropsComponent label="Amount" value="100" onChange={(v) => console.log(v)} />
418
+ </cx>
419
+ );
420
+ assert.ok(widget);
421
+ });
422
+
423
+ it("rejects wrong prop type (number for string)", () => {
424
+ const widget = (
425
+ <cx>
426
+ {/* @ts-expect-error - label should be string, not number */}
427
+ <RequiredPropsComponent label={123} value={100} onChange={(v) => console.log(v)} />
428
+ </cx>
429
+ );
430
+ assert.ok(widget);
431
+ });
432
+
433
+ it("provides instance through this", () => {
434
+ class TestController extends Controller {
435
+ change(_v: number) {}
436
+ }
437
+ const widget = (
438
+ <cx>
439
+ <RequiredPropsComponent
440
+ label={"label"}
441
+ value={100}
442
+ controller={TestController}
443
+ onChange={function (v) {
444
+ this.getControllerByType(TestController).change(v);
445
+ }}
446
+ />
447
+ </cx>
448
+ );
449
+ assert.ok(widget);
450
+ });
451
+ });
452
+ });
@@ -0,0 +1,108 @@
1
+ import { ContainerBase, ContainerConfig } from "../ui/Container";
2
+ import { Instance } from "../ui/Instance";
3
+ import { Prop } from "../ui/Prop";
4
+ import type { RenderingContext } from "../ui/RenderingContext";
5
+ import { VDOM, Widget } from "../ui/Widget";
6
+ import { isFunction, isNonEmptyArray } from "../util";
7
+ import { isArray } from "../util/isArray";
8
+
9
+ type ReactElementWrapperConfigBase = Omit<ContainerConfig, "items">;
10
+
11
+ // CxJS callback type - can be string (controller method) or callback with Instance as this
12
+ type CxCallback<T> = T extends (...args: infer A) => infer R ? (this: Instance, ...args: A) => R : T;
13
+
14
+ // Transform a single prop: functions to CxCallback, data to Prop<T>
15
+ type TransformProp<K, V> = K extends keyof ReactElementWrapperConfigBase
16
+ ? ReactElementWrapperConfigBase[K]
17
+ : NonNullable<V> extends Function
18
+ ? CxCallback<V>
19
+ : Prop<V>;
20
+
21
+ // Transform React component props - functions to CxCallback, data props to Prop<T>
22
+ // Preserves required/optional status
23
+ type TransformReactElementProps<T> = {
24
+ [K in keyof T]: TransformProp<K, T[K]>;
25
+ };
26
+
27
+ /** ReactElementWrapper configuration with component-specific props */
28
+ // componentType is not included here - it's added by the jsx-runtime and declared in the class
29
+ export type ReactElementWrapperConfig<P> = ReactElementWrapperConfigBase & TransformReactElementProps<P>;
30
+
31
+ interface ReactElementWrapperInstance extends Instance {
32
+ events?: Record<string, Function>;
33
+ }
34
+
35
+ export class ReactElementWrapper<C extends React.ComponentType<any> = React.ComponentType<any>> extends ContainerBase<
36
+ ReactElementWrapperConfigBase & { componentType: C; jsxAttributes?: string[] },
37
+ ReactElementWrapperInstance
38
+ > {
39
+ declare public componentType: React.ComponentType<any>;
40
+ declare public jsxAttributes?: string[];
41
+ declare public props?: Record<string, unknown>;
42
+ [key: string]: unknown;
43
+
44
+ init(): void {
45
+ // Collect all props to pass to the React component
46
+ this.props = {};
47
+
48
+ if (this.jsxAttributes) {
49
+ for (const attr of this.jsxAttributes) {
50
+ // Skip CxJS reserved attributes and children-related attributes
51
+ if (
52
+ attr === "componentType" ||
53
+ attr === "visible" ||
54
+ attr === "if" ||
55
+ attr === "controller" ||
56
+ attr === "jsxAttributes" ||
57
+ attr === "layout" ||
58
+ attr === "outerLayout" ||
59
+ attr === "putInto" ||
60
+ attr === "contentFor" ||
61
+ attr === "children" ||
62
+ attr === "items"
63
+ )
64
+ continue;
65
+
66
+ this.props[attr] = this[attr];
67
+ }
68
+ }
69
+
70
+ super.init();
71
+ }
72
+
73
+ public initInstance(_context: RenderingContext, _instance: ReactElementWrapperInstance): void {
74
+ let events: Record<string, Function> | undefined;
75
+ if (!isNonEmptyArray(this.jsxAttributes)) return;
76
+ events = {};
77
+ for (let prop of this.jsxAttributes) {
78
+ let f = this[prop];
79
+ if (prop.startsWith("on") && isFunction(f)) events[prop] = (...args: any[]) => f.apply(_instance, args);
80
+ }
81
+ }
82
+
83
+ declareData(...args: Record<string, unknown>[]): void {
84
+ super.declareData(
85
+ {
86
+ props: { structured: true },
87
+ },
88
+ ...args,
89
+ );
90
+ }
91
+
92
+ render(context: RenderingContext, instance: ReactElementWrapperInstance, key: string): React.ReactNode {
93
+ const { data } = instance;
94
+
95
+ // Render CxJS children to React elements
96
+ let children = this.renderChildren(context, instance);
97
+ if (children && isArray(children) && children.length === 0) children = undefined;
98
+
99
+ const props = {
100
+ key,
101
+ ...data.props,
102
+ ...instance.events,
103
+ children,
104
+ };
105
+
106
+ return VDOM.createElement(this.componentType, props);
107
+ }
108
+ }