cx 26.0.12 → 26.0.14

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 (365) 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/util/Console.d.ts +1 -0
  120. package/build/util/Console.js +7 -3
  121. package/build/util/Format.spec.d.ts +1 -0
  122. package/build/util/Format.spec.js +58 -0
  123. package/build/util/TraversalStack.spec.d.ts +1 -0
  124. package/build/util/TraversalStack.spec.js +43 -0
  125. package/build/util/date/upperBoundCheck.spec.d.ts +1 -0
  126. package/build/util/date/upperBoundCheck.spec.js +22 -0
  127. package/build/util/getSearchQueryPredicate.spec.d.ts +1 -0
  128. package/build/util/getSearchQueryPredicate.spec.js +33 -0
  129. package/build/util/isValidIdentifierName.spec.d.ts +1 -0
  130. package/build/util/isValidIdentifierName.spec.js +28 -0
  131. package/build/util/routeAppend.spec.d.ts +1 -0
  132. package/build/util/routeAppend.spec.js +14 -0
  133. package/build/widgets/AccessorBindings.spec.d.ts +1 -0
  134. package/build/widgets/AccessorBindings.spec.js +40 -0
  135. package/build/widgets/Button.d.ts +3 -6
  136. package/build/widgets/Button.js +1 -1
  137. package/build/widgets/DocumentTitle.d.ts +2 -0
  138. package/build/widgets/Heading.d.ts +2 -2
  139. package/build/widgets/HtmlElement.d.ts +31 -8
  140. package/build/widgets/HtmlElement.js +7 -9
  141. package/build/widgets/HtmlElement.spec.d.ts +1 -0
  142. package/build/widgets/HtmlElement.spec.js +38 -0
  143. package/build/widgets/Icon.d.ts +3 -13
  144. package/build/widgets/List.d.ts +4 -0
  145. package/build/widgets/ReactElementWrapper.d.ts +29 -0
  146. package/build/widgets/ReactElementWrapper.js +59 -0
  147. package/build/widgets/drag-drop/DragSource.d.ts +3 -3
  148. package/build/widgets/drag-drop/DragSource.js +2 -3
  149. package/build/widgets/drag-drop/DropZone.d.ts +3 -3
  150. package/build/widgets/drag-drop/DropZone.js +2 -3
  151. package/build/widgets/form/Checkbox.d.ts +2 -0
  152. package/build/widgets/form/ColorField.d.ts +2 -0
  153. package/build/widgets/form/DateTimeField.d.ts +2 -0
  154. package/build/widgets/form/Field.d.ts +0 -2
  155. package/build/widgets/form/LabeledContainer.d.ts +9 -8
  156. package/build/widgets/form/LabeledContainer.js +9 -9
  157. package/build/widgets/form/LookupField.d.ts +57 -9
  158. package/build/widgets/form/MonthField.d.ts +2 -0
  159. package/build/widgets/form/NumberField.d.ts +2 -0
  160. package/build/widgets/form/Radio.d.ts +2 -0
  161. package/build/widgets/form/Select.d.ts +2 -0
  162. package/build/widgets/form/Slider.d.ts +3 -0
  163. package/build/widgets/form/Switch.d.ts +2 -0
  164. package/build/widgets/form/TextField.d.ts +34 -0
  165. package/build/widgets/form/TimeList.d.ts +16 -1
  166. package/build/widgets/form/TimeList.js +34 -62
  167. package/build/widgets/form/UploadButton.d.ts +34 -2
  168. package/build/widgets/form/UploadButton.js +3 -1
  169. package/build/widgets/form/ValidationGroup.spec.d.ts +1 -0
  170. package/build/widgets/form/ValidationGroup.spec.js +62 -0
  171. package/build/widgets/form/Validator.d.ts +33 -2
  172. package/build/widgets/form/Validator.js +3 -0
  173. package/build/widgets/grid/Grid.d.ts +9 -9
  174. package/build/widgets/grid/TreeNode.d.ts +6 -0
  175. package/build/widgets/index.d.ts +1 -0
  176. package/build/widgets/index.js +1 -0
  177. package/build/widgets/nav/MenuItem.d.ts +3 -2
  178. package/build/widgets/nav/Route.spec.d.ts +1 -0
  179. package/build/widgets/nav/Route.spec.js +15 -0
  180. package/build/widgets/nav/Scroller.d.ts +4 -6
  181. package/build/widgets/nav/Scroller.js +6 -3
  182. package/build/widgets/nav/Tab.d.ts +2 -2
  183. package/build/widgets/overlay/ContextMenu.d.ts +3 -3
  184. package/build/widgets/overlay/Overlay.d.ts +2 -1
  185. package/build/widgets/overlay/Overlay.js +1 -1
  186. package/build/widgets/overlay/createHotPromiseWindowFactory.js +0 -1
  187. package/build.js +133 -133
  188. package/dist/data.js +2 -2
  189. package/dist/jsx-runtime.js +6 -1
  190. package/dist/manifest.d.ts +1443 -0
  191. package/dist/manifest.js +761 -713
  192. package/dist/ui.js +91 -5
  193. package/dist/util.js +3 -0
  194. package/dist/widgets.js +520 -162
  195. package/package.json +46 -20
  196. package/src/charts/Chart.ts +108 -108
  197. package/src/charts/ScatterGraph.tsx +6 -6
  198. package/src/data/ArrayElementView.ts +90 -90
  199. package/src/data/AugmentedViewBase.ts +88 -88
  200. package/src/data/Binding.ts +104 -104
  201. package/src/data/ExposedRecordView.ts +95 -95
  202. package/src/data/ExposedValueView.ts +89 -89
  203. package/src/data/Expression.spec.ts +229 -229
  204. package/src/data/Expression.ts +233 -233
  205. package/src/data/Grouper.spec.ts +57 -57
  206. package/src/data/Grouper.ts +158 -158
  207. package/src/data/NestedDataView.ts +43 -43
  208. package/src/data/ReadOnlyDataView.ts +39 -39
  209. package/src/data/Ref.ts +104 -104
  210. package/src/data/Selector.ts +10 -10
  211. package/src/data/Store.ts +52 -52
  212. package/src/data/StoreProxy.ts +19 -19
  213. package/src/data/StoreRef.ts +66 -66
  214. package/src/data/StringTemplate.spec.ts +132 -132
  215. package/src/data/StringTemplate.ts +93 -93
  216. package/src/data/StructuredSelector.spec.ts +113 -113
  217. package/src/data/StructuredSelector.ts +146 -146
  218. package/src/data/SubscribableView.ts +63 -63
  219. package/src/data/ZoomIntoPropertyView.spec.ts +64 -64
  220. package/src/data/ZoomIntoPropertyView.ts +45 -45
  221. package/src/data/computable.spec.ts +87 -62
  222. package/src/data/computable.ts +3 -6
  223. package/src/data/createAccessorModelProxy.spec.tsx +102 -1
  224. package/src/data/createAccessorModelProxy.ts +9 -3
  225. package/src/data/createStructuredSelector.ts +62 -62
  226. package/src/data/getAccessor.spec.ts +11 -11
  227. package/src/data/getAccessor.ts +74 -74
  228. package/src/data/getSelector.spec.ts +43 -43
  229. package/src/data/getSelector.ts +66 -66
  230. package/src/data/ops/filter.spec.ts +35 -35
  231. package/src/data/ops/filter.ts +9 -9
  232. package/src/data/ops/findTreeNode.ts +1 -5
  233. package/src/data/ops/findTreePath.ts +1 -1
  234. package/src/data/ops/merge.ts +13 -13
  235. package/src/data/ops/removeTreeNodes.spec.ts +37 -37
  236. package/src/data/ops/removeTreeNodes.ts +2 -2
  237. package/src/data/ops/updateArray.spec.ts +69 -69
  238. package/src/data/ops/updateArray.ts +31 -31
  239. package/src/data/ops/updateTree.ts +1 -1
  240. package/src/data/test-types.ts +7 -7
  241. package/src/hooks/resolveCallback.spec.tsx +30 -7
  242. package/src/hooks/useTrigger.ts +26 -26
  243. package/src/index.scss +6 -6
  244. package/src/jsx-dev-runtime.ts +4 -4
  245. package/src/jsx-runtime.spec.tsx +431 -0
  246. package/src/jsx-runtime.ts +26 -22
  247. package/src/svg/BoundedObject.ts +101 -101
  248. package/src/svg/util/Rect.ts +105 -105
  249. package/src/ui/CSSHelper.ts +17 -17
  250. package/src/ui/ContentResolver.spec.tsx +172 -19
  251. package/src/ui/ContentResolver.ts +16 -8
  252. package/src/ui/Controller.ts +15 -2
  253. package/src/ui/Culture.ts +159 -159
  254. package/src/ui/DataProxy.ts +55 -55
  255. package/src/ui/FocusManager.ts +171 -171
  256. package/src/ui/Instance.ts +866 -868
  257. package/src/ui/Prop.ts +140 -112
  258. package/src/ui/RenderingContext.ts +99 -99
  259. package/src/ui/Repeater.ts +3 -12
  260. package/src/ui/Rescope.ts +49 -49
  261. package/src/ui/StructuredInstanceDataAccessor.ts +32 -32
  262. package/src/ui/Text.ts +21 -2
  263. package/src/ui/VDOM.ts +34 -34
  264. package/src/ui/adapter/ArrayAdapter.spec.ts +55 -55
  265. package/src/ui/adapter/ArrayAdapter.ts +4 -1
  266. package/src/ui/adapter/TreeAdapter.spec.ts +76 -76
  267. package/src/ui/adapter/TreeAdapter.ts +185 -185
  268. package/src/ui/app/History.ts +133 -133
  269. package/src/ui/app/Url.spec.ts +50 -50
  270. package/src/ui/app/startHotAppLoop.ts +41 -41
  271. package/src/ui/createFunctionalComponent.spec.tsx +53 -0
  272. package/src/ui/createFunctionalComponent.ts +86 -65
  273. package/src/ui/expr.ts +4 -1
  274. package/src/ui/exprHelpers.spec.ts +379 -0
  275. package/src/ui/exprHelpers.ts +78 -0
  276. package/src/ui/index.ts +47 -46
  277. package/src/ui/layout/Content.ts +30 -30
  278. package/src/ui/layout/FirstVisibleChildLayout.spec.tsx +1 -1
  279. package/src/ui/layout/FirstVisibleChildLayout.ts +60 -60
  280. package/src/ui/selection/KeySelection.ts +153 -153
  281. package/src/ui/selection/Selection.ts +128 -128
  282. package/src/util/Console.ts +13 -11
  283. package/src/util/Format.spec.ts +41 -41
  284. package/src/util/Format.ts +267 -267
  285. package/src/util/addEventListenerWithOptions.ts +41 -41
  286. package/src/util/browserSupportsPassiveEventHandlers.ts +20 -20
  287. package/src/util/color/rgbToHsl.ts +35 -35
  288. package/src/util/getActiveElement.ts +4 -4
  289. package/src/util/hasKey.ts +18 -18
  290. package/src/util/index.ts +55 -55
  291. package/src/util/innerTextTrim.ts +10 -10
  292. package/src/util/isArray.ts +3 -3
  293. package/src/util/isDataRecord.ts +5 -5
  294. package/src/util/isDefined.ts +3 -3
  295. package/src/util/isString.ts +3 -3
  296. package/src/widgets/AccessorBindings.spec.tsx +26 -0
  297. package/src/widgets/Button.tsx +5 -17
  298. package/src/widgets/DocumentTitle.ts +95 -92
  299. package/src/widgets/Heading.ts +2 -2
  300. package/src/widgets/HtmlElement.spec.helpers.tsx +108 -0
  301. package/src/widgets/HtmlElement.spec.tsx +20 -12
  302. package/src/widgets/HtmlElement.tsx +77 -23
  303. package/src/widgets/Icon.ts +3 -17
  304. package/src/widgets/List.tsx +6 -0
  305. package/src/widgets/ReactElementWrapper.spec.tsx +452 -0
  306. package/src/widgets/ReactElementWrapper.tsx +108 -0
  307. package/src/widgets/Sandbox.ts +103 -103
  308. package/src/widgets/autoFocus.ts +9 -9
  309. package/src/widgets/cx.ts +63 -63
  310. package/src/widgets/drag-drop/DragSource.tsx +3 -4
  311. package/src/widgets/drag-drop/DropZone.tsx +3 -4
  312. package/src/widgets/form/Checkbox.tsx +3 -0
  313. package/src/widgets/form/ColorField.tsx +3 -0
  314. package/src/widgets/form/DateTimeField.tsx +5 -0
  315. package/src/widgets/form/Field.tsx +0 -3
  316. package/src/widgets/form/Label.tsx +1 -0
  317. package/src/widgets/form/LabeledContainer.ts +22 -26
  318. package/src/widgets/form/LookupField.spec.tsx +93 -0
  319. package/src/widgets/form/LookupField.tsx +104 -9
  320. package/src/widgets/form/MonthField.tsx +5 -0
  321. package/src/widgets/form/NumberField.tsx +3 -0
  322. package/src/widgets/form/Radio.tsx +5 -0
  323. package/src/widgets/form/Select.tsx +5 -0
  324. package/src/widgets/form/Slider.tsx +4 -0
  325. package/src/widgets/form/Switch.tsx +3 -0
  326. package/src/widgets/form/TextField.tsx +62 -0
  327. package/src/widgets/form/TimeList.tsx +84 -73
  328. package/src/widgets/form/UploadButton.tsx +53 -2
  329. package/src/widgets/form/Validator.ts +40 -3
  330. package/src/widgets/grid/Grid.tsx +9 -12
  331. package/src/widgets/grid/GridCell.ts +143 -143
  332. package/src/widgets/grid/TreeNode.tsx +9 -0
  333. package/src/widgets/icons/calendar.tsx +17 -17
  334. package/src/widgets/icons/check.tsx +13 -13
  335. package/src/widgets/icons/clear.tsx +15 -15
  336. package/src/widgets/icons/close.tsx +20 -20
  337. package/src/widgets/icons/cx.tsx +38 -38
  338. package/src/widgets/icons/drop-down.tsx +15 -15
  339. package/src/widgets/icons/file.tsx +13 -13
  340. package/src/widgets/icons/folder-open.tsx +15 -15
  341. package/src/widgets/icons/folder.tsx +13 -13
  342. package/src/widgets/icons/forward.tsx +22 -22
  343. package/src/widgets/icons/loading.tsx +24 -24
  344. package/src/widgets/icons/menu.tsx +17 -17
  345. package/src/widgets/icons/pixel-picker.tsx +18 -18
  346. package/src/widgets/icons/search.tsx +13 -13
  347. package/src/widgets/icons/sort-asc.tsx +14 -14
  348. package/src/widgets/icons/square.tsx +18 -18
  349. package/src/widgets/index.ts +1 -0
  350. package/src/widgets/nav/MenuItem.tsx +3 -2
  351. package/src/widgets/nav/Route.ts +142 -142
  352. package/src/widgets/nav/Scroller.tsx +8 -9
  353. package/src/widgets/nav/Tab.ts +2 -2
  354. package/src/widgets/overlay/ContextMenu.ts +42 -42
  355. package/src/widgets/overlay/Dropdown.tsx +762 -762
  356. package/src/widgets/overlay/MsgBox.tsx +141 -141
  357. package/src/widgets/overlay/Overlay.tsx +5 -4
  358. package/src/widgets/overlay/Toast.ts +111 -111
  359. package/src/widgets/overlay/Window.tsx +299 -299
  360. package/src/widgets/overlay/alerts.ts +46 -46
  361. package/src/widgets/overlay/captureMouse.ts +195 -195
  362. package/src/widgets/overlay/createHotPromiseWindowFactory.ts +0 -1
  363. package/src/widgets/overlay/index.d.ts +11 -11
  364. package/src/widgets/overlay/index.ts +11 -11
  365. 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
+ }