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