@rangka/client 0.1.0 → 0.1.2

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 (352) hide show
  1. package/dist/App.d.ts.map +1 -1
  2. package/dist/App.js +7 -1
  3. package/dist/App.js.map +1 -1
  4. package/dist/components/ui/input-group.d.ts +1 -1
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/main.d.ts.map +1 -1
  10. package/dist/main.js +20 -0
  11. package/dist/main.js.map +1 -1
  12. package/dist/shell/assets/AttachmentWidget-BTTGroHP.js +1 -0
  13. package/dist/shell/assets/AttachmentsWidget-DZgvmO9P.js +1 -0
  14. package/dist/shell/assets/BadgeWidget-DN6GsAAG.js +1 -0
  15. package/dist/shell/assets/CardWidget-qX-UvqJh.js +1 -0
  16. package/dist/shell/assets/CheckboxWidget-CYPZ85ih.js +1 -0
  17. package/dist/shell/assets/CodeWidget-DnVtHG1d.js +1 -0
  18. package/dist/shell/assets/ColumnWidget-CYmFCVOf.js +1 -0
  19. package/dist/shell/assets/ComputedWidget-BQApkfir.js +1 -0
  20. package/dist/shell/assets/DatePickerWidget-pEusSy9D.js +1 -0
  21. package/dist/shell/assets/DatetimeWidget-M0fEGDTD.js +1 -0
  22. package/dist/shell/assets/DividerWidget-hXVGhIKC.js +1 -0
  23. package/dist/shell/assets/DrawerWidget-DynL7EIy.js +1 -0
  24. package/dist/shell/assets/DynamicLinkWidget-ChMJ5DAf.js +1 -0
  25. package/dist/shell/assets/IconWidget-Cgo9txq1.js +1 -0
  26. package/dist/shell/assets/ImageWidget-YUv1_OQK.js +1 -0
  27. package/dist/shell/assets/JsonWidget-RHUHhXHJ.js +1 -0
  28. package/dist/shell/assets/LinkWidget-CIu7qmb2.js +1 -0
  29. package/dist/shell/assets/ManyToManyWidget-DCQnygvZ.js +1 -0
  30. package/dist/shell/assets/ModalWidget-FggzYaS2.js +1 -0
  31. package/dist/shell/assets/MoneyWidget-CKjOSucD.js +1 -0
  32. package/dist/shell/assets/RepeatWidget-Dfp4zW4d.js +1 -0
  33. package/dist/shell/assets/ScrollAreaWidget-CdFdf4rv.js +1 -0
  34. package/dist/shell/assets/SequenceWidget-DIXHZslz.js +1 -0
  35. package/dist/shell/assets/SpacerWidget-BgM4dC6x.js +1 -0
  36. package/dist/shell/assets/SplitWidget-_mcQkjQk.js +1 -0
  37. package/dist/shell/assets/StackWidget-BI_VIreo.js +1 -0
  38. package/dist/shell/assets/TableWidget-DuIvd6qR.js +1 -0
  39. package/dist/shell/assets/TextareaWidget-CATua7Ls.js +1 -0
  40. package/dist/shell/assets/TreeWidget-BoHlhfza.js +1 -0
  41. package/dist/shell/assets/calendar-DqckiKt1.js +1 -0
  42. package/dist/shell/assets/index-63v1sBS3.css +1 -0
  43. package/dist/shell/assets/index-D1wStSrO.js +8635 -0
  44. package/dist/shell/assets/popover-DywIKFaQ.js +1 -0
  45. package/dist/shell/assets/textarea-BVCZevA6.js +1 -0
  46. package/dist/shell/assets/useSurfaceContext-kzu9770H.js +1 -0
  47. package/dist/shell/assets/vendor-query-dRdWN_eK.js +1 -0
  48. package/dist/shell/assets/vendor-radix-D-Trh8JA.js +69 -0
  49. package/dist/shell/assets/vendor-router-B1WM9yRc.js +17 -0
  50. package/dist/shell/index.html +5 -2
  51. package/dist/theme.css +82 -0
  52. package/dist/widgets/components/lazy-manifest.d.ts +11 -0
  53. package/dist/widgets/components/lazy-manifest.d.ts.map +1 -0
  54. package/dist/widgets/components/lazy-manifest.js +32 -0
  55. package/dist/widgets/components/lazy-manifest.js.map +1 -0
  56. package/dist/widgets/components/register.d.ts.map +1 -1
  57. package/dist/widgets/components/register.js +0 -58
  58. package/dist/widgets/components/register.js.map +1 -1
  59. package/dist/widgets/loader.d.ts +3 -0
  60. package/dist/widgets/loader.d.ts.map +1 -0
  61. package/dist/widgets/loader.js +99 -0
  62. package/dist/widgets/loader.js.map +1 -0
  63. package/dist/widgets/renderer/LazyWidget.d.ts +8 -0
  64. package/dist/widgets/renderer/LazyWidget.d.ts.map +1 -0
  65. package/dist/widgets/renderer/LazyWidget.js +31 -0
  66. package/dist/widgets/renderer/LazyWidget.js.map +1 -0
  67. package/dist/widgets/renderer/WidgetErrorBoundary.d.ts +17 -0
  68. package/dist/widgets/renderer/WidgetErrorBoundary.d.ts.map +1 -0
  69. package/dist/widgets/renderer/WidgetErrorBoundary.js +18 -0
  70. package/dist/widgets/renderer/WidgetErrorBoundary.js.map +1 -0
  71. package/dist/widgets/renderer/WidgetRenderer.d.ts.map +1 -1
  72. package/dist/widgets/renderer/WidgetRenderer.js +8 -6
  73. package/dist/widgets/renderer/WidgetRenderer.js.map +1 -1
  74. package/package.json +7 -4
  75. package/.claude/skills/add-widget/SKILL.md +0 -101
  76. package/.turbo/turbo-build.log +0 -29
  77. package/CHANGELOG.md +0 -18
  78. package/CLAUDE.md +0 -236
  79. package/components.json +0 -25
  80. package/dist/components/ui/chart.d.ts +0 -45
  81. package/dist/components/ui/chart.d.ts.map +0 -1
  82. package/dist/components/ui/chart.js +0 -119
  83. package/dist/components/ui/chart.js.map +0 -1
  84. package/dist/shell/assets/index--35CAvcP.js +0 -8715
  85. package/dist/shell/assets/index-COLmoPYo.css +0 -1
  86. package/index.html +0 -12
  87. package/src/App.tsx +0 -44
  88. package/src/__tests__/setup.ts +0 -1
  89. package/src/api/auth.ts +0 -41
  90. package/src/api/boot.ts +0 -10
  91. package/src/api/client.ts +0 -26
  92. package/src/api/paths.ts +0 -3
  93. package/src/api/token.ts +0 -13
  94. package/src/auth/LoginForm.tsx +0 -67
  95. package/src/auth/SessionExpired.tsx +0 -24
  96. package/src/auth/SetupForm.tsx +0 -76
  97. package/src/boot/BootGate.tsx +0 -35
  98. package/src/boot/BootProvider.tsx +0 -28
  99. package/src/boot/types.ts +0 -9
  100. package/src/boot/useBoot.ts +0 -111
  101. package/src/components/Icon.tsx +0 -17
  102. package/src/components/ui/accordion.tsx +0 -82
  103. package/src/components/ui/alert-dialog.tsx +0 -180
  104. package/src/components/ui/alert.tsx +0 -76
  105. package/src/components/ui/aspect-ratio.tsx +0 -9
  106. package/src/components/ui/avatar.tsx +0 -94
  107. package/src/components/ui/badge.tsx +0 -45
  108. package/src/components/ui/breadcrumb.tsx +0 -104
  109. package/src/components/ui/button-group.tsx +0 -78
  110. package/src/components/ui/button.tsx +0 -65
  111. package/src/components/ui/calendar.tsx +0 -187
  112. package/src/components/ui/card.tsx +0 -85
  113. package/src/components/ui/carousel.tsx +0 -229
  114. package/src/components/ui/chart.tsx +0 -339
  115. package/src/components/ui/checkbox.tsx +0 -27
  116. package/src/components/ui/collapsible.tsx +0 -21
  117. package/src/components/ui/combobox.tsx +0 -275
  118. package/src/components/ui/command.tsx +0 -178
  119. package/src/components/ui/context-menu.tsx +0 -242
  120. package/src/components/ui/dialog.tsx +0 -146
  121. package/src/components/ui/direction.tsx +0 -20
  122. package/src/components/ui/drawer.tsx +0 -118
  123. package/src/components/ui/dropdown-menu.tsx +0 -247
  124. package/src/components/ui/empty.tsx +0 -94
  125. package/src/components/ui/field.tsx +0 -224
  126. package/src/components/ui/hover-card.tsx +0 -36
  127. package/src/components/ui/input-group.tsx +0 -142
  128. package/src/components/ui/input-otp.tsx +0 -86
  129. package/src/components/ui/input.tsx +0 -19
  130. package/src/components/ui/item.tsx +0 -182
  131. package/src/components/ui/kbd.tsx +0 -26
  132. package/src/components/ui/label.tsx +0 -19
  133. package/src/components/ui/menubar.tsx +0 -260
  134. package/src/components/ui/native-select.tsx +0 -55
  135. package/src/components/ui/navigation-menu.tsx +0 -160
  136. package/src/components/ui/pagination.tsx +0 -112
  137. package/src/components/ui/popover.tsx +0 -74
  138. package/src/components/ui/progress.tsx +0 -31
  139. package/src/components/ui/radio-group.tsx +0 -42
  140. package/src/components/ui/resizable.tsx +0 -42
  141. package/src/components/ui/scroll-area.tsx +0 -53
  142. package/src/components/ui/select.tsx +0 -185
  143. package/src/components/ui/separator.tsx +0 -26
  144. package/src/components/ui/sheet.tsx +0 -128
  145. package/src/components/ui/sidebar.tsx +0 -669
  146. package/src/components/ui/skeleton.tsx +0 -13
  147. package/src/components/ui/slider.tsx +0 -54
  148. package/src/components/ui/sonner.tsx +0 -43
  149. package/src/components/ui/spinner.tsx +0 -16
  150. package/src/components/ui/switch.tsx +0 -33
  151. package/src/components/ui/table.tsx +0 -87
  152. package/src/components/ui/tabs.tsx +0 -80
  153. package/src/components/ui/textarea.tsx +0 -18
  154. package/src/components/ui/toggle-group.tsx +0 -86
  155. package/src/components/ui/toggle.tsx +0 -44
  156. package/src/components/ui/tooltip.tsx +0 -53
  157. package/src/context/MetaContext.tsx +0 -22
  158. package/src/context/ModuleContext.tsx +0 -62
  159. package/src/context/PermissionsContext.tsx +0 -39
  160. package/src/context/ShellProviders.tsx +0 -33
  161. package/src/context/UserContext.tsx +0 -16
  162. package/src/data/QueryProvider.tsx +0 -7
  163. package/src/data/queryClient.ts +0 -18
  164. package/src/data/useModelMeta.ts +0 -17
  165. package/src/data/useMutation.ts +0 -60
  166. package/src/data/useRecord.ts +0 -29
  167. package/src/data/useSource.ts +0 -112
  168. package/src/hooks/use-mobile.ts +0 -19
  169. package/src/index.css +0 -260
  170. package/src/index.ts +0 -16
  171. package/src/lib/utils.ts +0 -6
  172. package/src/main.tsx +0 -17
  173. package/src/router/NotFound.tsx +0 -8
  174. package/src/router/RouterProvider.tsx +0 -7
  175. package/src/router/buildRouteTree.tsx +0 -63
  176. package/src/router/createShellRouter.ts +0 -9
  177. package/src/router/hooks.ts +0 -43
  178. package/src/shell/CommandPalette.tsx +0 -76
  179. package/src/shell/ConfirmDialog.tsx +0 -34
  180. package/src/shell/ConfirmProvider.tsx +0 -56
  181. package/src/shell/DrawerContext.tsx +0 -44
  182. package/src/shell/HeaderActions.tsx +0 -31
  183. package/src/shell/ModuleSelectorPage.tsx +0 -149
  184. package/src/shell/PageOutlet.tsx +0 -21
  185. package/src/shell/ShellContext.tsx +0 -45
  186. package/src/shell/ShellDevTools.tsx +0 -153
  187. package/src/shell/ShellLayout.tsx +0 -231
  188. package/src/shell/Toast.tsx +0 -58
  189. package/src/shell/ToastProvider.tsx +0 -60
  190. package/src/shell/app-sidebar/AppSidebar.tsx +0 -44
  191. package/src/shell/app-sidebar/ModuleSwitcher.tsx +0 -87
  192. package/src/shell/app-sidebar/NavMain.tsx +0 -64
  193. package/src/shell/app-sidebar/NavUser.tsx +0 -97
  194. package/src/shell/app-sidebar/SearchMenu.tsx +0 -22
  195. package/src/shell/app-sidebar/index.ts +0 -8
  196. package/src/shell/app-sidebar/types.ts +0 -38
  197. package/src/shell/types.ts +0 -6
  198. package/src/shell/useBreadcrumbs.ts +0 -42
  199. package/src/studio/bridge.ts +0 -125
  200. package/src/studio/index.ts +0 -3
  201. package/src/studio/overlay.ts +0 -47
  202. package/src/studio/types.ts +0 -32
  203. package/src/studio/walker.ts +0 -48
  204. package/src/vite-env.d.ts +0 -1
  205. package/src/widgets/__tests__/action-edge-cases.test.ts +0 -281
  206. package/src/widgets/__tests__/action.test.ts +0 -236
  207. package/src/widgets/__tests__/attachment-widget.test.tsx +0 -85
  208. package/src/widgets/__tests__/attachments-widget.test.tsx +0 -109
  209. package/src/widgets/__tests__/binding.test.ts +0 -76
  210. package/src/widgets/__tests__/button-widget.test.tsx +0 -145
  211. package/src/widgets/__tests__/checkbox-widget.test.tsx +0 -158
  212. package/src/widgets/__tests__/code-widget.test.tsx +0 -64
  213. package/src/widgets/__tests__/computed-widget.test.tsx +0 -62
  214. package/src/widgets/__tests__/condition-edge-cases.test.ts +0 -120
  215. package/src/widgets/__tests__/condition.test.ts +0 -221
  216. package/src/widgets/__tests__/context.test.ts +0 -99
  217. package/src/widgets/__tests__/data-widget.test.tsx +0 -204
  218. package/src/widgets/__tests__/datepicker-widget.test.tsx +0 -66
  219. package/src/widgets/__tests__/datetime-widget.test.tsx +0 -67
  220. package/src/widgets/__tests__/drawer-widget.test.tsx +0 -149
  221. package/src/widgets/__tests__/dynamic-link-widget.test.tsx +0 -52
  222. package/src/widgets/__tests__/edge-cases.test.ts +0 -232
  223. package/src/widgets/__tests__/evaluator.test.ts +0 -107
  224. package/src/widgets/__tests__/functions.test.ts +0 -147
  225. package/src/widgets/__tests__/grid-widget.test.tsx +0 -137
  226. package/src/widgets/__tests__/hooks.test.tsx +0 -249
  227. package/src/widgets/__tests__/icon-widget.test.tsx +0 -129
  228. package/src/widgets/__tests__/input-widget.test.tsx +0 -264
  229. package/src/widgets/__tests__/integration.test.ts +0 -116
  230. package/src/widgets/__tests__/json-widget.test.tsx +0 -70
  231. package/src/widgets/__tests__/link-widget.test.tsx +0 -92
  232. package/src/widgets/__tests__/many-to-many-widget.test.tsx +0 -93
  233. package/src/widgets/__tests__/modal-widget.test.tsx +0 -148
  234. package/src/widgets/__tests__/money-widget.test.tsx +0 -97
  235. package/src/widgets/__tests__/parser.test.ts +0 -171
  236. package/src/widgets/__tests__/reactive-variables.test.ts +0 -383
  237. package/src/widgets/__tests__/renderer.test.tsx +0 -300
  238. package/src/widgets/__tests__/repeat-widget.test.tsx +0 -229
  239. package/src/widgets/__tests__/select-widget.test.tsx +0 -231
  240. package/src/widgets/__tests__/sequence-widget.test.tsx +0 -58
  241. package/src/widgets/__tests__/shell-integration.test.tsx +0 -1343
  242. package/src/widgets/__tests__/split-widget.test.tsx +0 -133
  243. package/src/widgets/__tests__/state-edge-cases.test.ts +0 -118
  244. package/src/widgets/__tests__/state.test.ts +0 -106
  245. package/src/widgets/__tests__/table-data-binding.test.tsx +0 -482
  246. package/src/widgets/__tests__/table-filter-popover.test.tsx +0 -486
  247. package/src/widgets/__tests__/table-search.test.tsx +0 -305
  248. package/src/widgets/__tests__/table-widget.test.tsx +0 -509
  249. package/src/widgets/__tests__/textarea-widget.test.tsx +0 -105
  250. package/src/widgets/__tests__/tracker-validator-edge-cases.test.ts +0 -242
  251. package/src/widgets/__tests__/tracker.test.ts +0 -133
  252. package/src/widgets/__tests__/tree-widget.test.tsx +0 -97
  253. package/src/widgets/__tests__/use-model-source.test.ts +0 -67
  254. package/src/widgets/__tests__/validator.test.ts +0 -208
  255. package/src/widgets/action/dispatcher.ts +0 -334
  256. package/src/widgets/action/index.ts +0 -2
  257. package/src/widgets/binding/index.ts +0 -2
  258. package/src/widgets/binding/resolver.ts +0 -61
  259. package/src/widgets/components/AttachmentWidget.tsx +0 -111
  260. package/src/widgets/components/AttachmentsWidget.tsx +0 -121
  261. package/src/widgets/components/BadgeWidget.tsx +0 -35
  262. package/src/widgets/components/ButtonWidget.tsx +0 -43
  263. package/src/widgets/components/CardWidget.tsx +0 -68
  264. package/src/widgets/components/CheckboxWidget.tsx +0 -39
  265. package/src/widgets/components/CodeWidget.tsx +0 -44
  266. package/src/widgets/components/ColumnWidget.tsx +0 -22
  267. package/src/widgets/components/ComputedWidget.tsx +0 -49
  268. package/src/widgets/components/DataWidget.tsx +0 -189
  269. package/src/widgets/components/DatePickerWidget.tsx +0 -73
  270. package/src/widgets/components/DatetimeWidget.tsx +0 -160
  271. package/src/widgets/components/DividerWidget.tsx +0 -37
  272. package/src/widgets/components/DrawerWidget.tsx +0 -52
  273. package/src/widgets/components/DynamicLinkWidget.tsx +0 -130
  274. package/src/widgets/components/GridWidget.tsx +0 -134
  275. package/src/widgets/components/GroupWidget.tsx +0 -111
  276. package/src/widgets/components/IconWidget.tsx +0 -29
  277. package/src/widgets/components/ImageWidget.tsx +0 -28
  278. package/src/widgets/components/InputWidget.tsx +0 -70
  279. package/src/widgets/components/JsonWidget.tsx +0 -78
  280. package/src/widgets/components/LinkWidget.tsx +0 -99
  281. package/src/widgets/components/ManyToManyWidget.tsx +0 -125
  282. package/src/widgets/components/ModalWidget.tsx +0 -52
  283. package/src/widgets/components/MoneyWidget.tsx +0 -80
  284. package/src/widgets/components/RepeatWidget.tsx +0 -66
  285. package/src/widgets/components/ScrollAreaWidget.tsx +0 -40
  286. package/src/widgets/components/SectionWidget.tsx +0 -78
  287. package/src/widgets/components/SelectWidget.tsx +0 -63
  288. package/src/widgets/components/SequenceWidget.tsx +0 -32
  289. package/src/widgets/components/SpacerWidget.tsx +0 -29
  290. package/src/widgets/components/SplitWidget.tsx +0 -60
  291. package/src/widgets/components/StackWidget.tsx +0 -44
  292. package/src/widgets/components/TableWidget.tsx +0 -366
  293. package/src/widgets/components/TextWidget.tsx +0 -44
  294. package/src/widgets/components/TextareaWidget.tsx +0 -49
  295. package/src/widgets/components/TreeWidget.tsx +0 -109
  296. package/src/widgets/components/index.ts +0 -30
  297. package/src/widgets/components/register.ts +0 -93
  298. package/src/widgets/components/table/CellRenderers.tsx +0 -83
  299. package/src/widgets/components/table/TablePagination.tsx +0 -45
  300. package/src/widgets/components/table/TableToolbar.tsx +0 -285
  301. package/src/widgets/components/table/filter-operators.ts +0 -134
  302. package/src/widgets/components/table/index.ts +0 -11
  303. package/src/widgets/condition/evaluator.ts +0 -57
  304. package/src/widgets/condition/index.ts +0 -1
  305. package/src/widgets/context/builder.ts +0 -99
  306. package/src/widgets/context/index.ts +0 -8
  307. package/src/widgets/context/types.ts +0 -37
  308. package/src/widgets/data/index.ts +0 -5
  309. package/src/widgets/data/useModelQuery.ts +0 -116
  310. package/src/widgets/data/useModelRecord.ts +0 -37
  311. package/src/widgets/expression/evaluator.ts +0 -100
  312. package/src/widgets/expression/functions.ts +0 -131
  313. package/src/widgets/expression/index.ts +0 -13
  314. package/src/widgets/expression/parser.ts +0 -229
  315. package/src/widgets/expression/types.ts +0 -45
  316. package/src/widgets/form/FormContext.ts +0 -29
  317. package/src/widgets/form/FormProvider.tsx +0 -84
  318. package/src/widgets/form/FormWidget.tsx +0 -42
  319. package/src/widgets/form/index.ts +0 -4
  320. package/src/widgets/form/useFormState.ts +0 -127
  321. package/src/widgets/form/useFormSubmit.ts +0 -90
  322. package/src/widgets/form/useFormValidation.ts +0 -62
  323. package/src/widgets/hooks/index.ts +0 -8
  324. package/src/widgets/hooks/useAction.ts +0 -83
  325. package/src/widgets/hooks/useBind.ts +0 -34
  326. package/src/widgets/hooks/useCondition.ts +0 -21
  327. package/src/widgets/hooks/useDataQuery.ts +0 -48
  328. package/src/widgets/hooks/useExpression.ts +0 -14
  329. package/src/widgets/hooks/usePageState.ts +0 -21
  330. package/src/widgets/hooks/useSurfaceContext.ts +0 -11
  331. package/src/widgets/hooks/useWidgetContext.ts +0 -14
  332. package/src/widgets/index.ts +0 -80
  333. package/src/widgets/lib/layout-props.ts +0 -135
  334. package/src/widgets/reactivity/index.ts +0 -11
  335. package/src/widgets/reactivity/tracker.ts +0 -139
  336. package/src/widgets/reactivity/variables.ts +0 -213
  337. package/src/widgets/registry.ts +0 -41
  338. package/src/widgets/renderer/SlotRenderer.tsx +0 -47
  339. package/src/widgets/renderer/WidgetRenderer.tsx +0 -191
  340. package/src/widgets/renderer/index.ts +0 -4
  341. package/src/widgets/shell/WidgetSlotRenderer.tsx +0 -73
  342. package/src/widgets/shell/index.ts +0 -4
  343. package/src/widgets/shell/useActionHandlers.ts +0 -170
  344. package/src/widgets/state/index.ts +0 -2
  345. package/src/widgets/state/store.ts +0 -96
  346. package/src/widgets/types.ts +0 -28
  347. package/src/widgets/validation/index.ts +0 -2
  348. package/src/widgets/validation/validator.ts +0 -140
  349. package/tsconfig.json +0 -27
  350. package/tsconfig.tsbuildinfo +0 -1
  351. package/vite.config.ts +0 -21
  352. package/vitest.config.ts +0 -16
@@ -1,116 +0,0 @@
1
- import { describe, test, expect } from 'vitest';
2
- import { parse, evaluate } from '../expression/index.js';
3
- import { evaluateConditions } from '../condition/index.js';
4
- import { StateStore } from '../state/index.js';
5
- import { createRootContext } from '../context/index.js';
6
- import { resolveBinding } from '../binding/index.js';
7
- import { dispatch } from '../action/index.js';
8
- import { buildDependencyMap, getAffectedWidgets } from '../reactivity/index.js';
9
- import type { ActionContext, ActionHandlers } from '../action/index.js';
10
- import type { WidgetNode } from '@rangka/shared';
11
-
12
- describe('Integration: Form scenario', () => {
13
- test('field change → expression recomputes → condition re-evaluates → action fires', () => {
14
- const record = { qty: 5, rate: 100, total: 500, status: 'draft' };
15
- const _ctx = createRootContext(record, 'sales.order', 'edit');
16
-
17
- record.qty = 10;
18
- const exprAst = parse('{{qty * rate}}');
19
- const newTotal = evaluate(exprAst, record);
20
- expect(newTotal).toBe(1000);
21
-
22
- record.total = newTotal as number;
23
- const visible = evaluateConditions({ field: 'total', operator: 'gt', value: 500 }, record);
24
- expect(visible).toBe(true);
25
- });
26
- });
27
-
28
- describe('Integration: Table row context', () => {
29
- test('model binding → row context → action in row uses correct record', async () => {
30
- const parentRecord = { id: '1', customer: 'Acme' };
31
- const parentCtx = createRootContext(parentRecord, 'sales.order', 'view');
32
-
33
- const rowRecord = { id: '10', product: 'Widget', qty: 5, rate: 20 };
34
- const rowCtx = {
35
- record: rowRecord,
36
- model: 'sales.orderItem',
37
- mode: 'view' as const,
38
- index: 0,
39
- parent: parentCtx,
40
- };
41
-
42
- const binding = resolveBinding({ expression: '{{qty * rate}}' }, rowCtx);
43
- expect(binding!.value).toBe(100);
44
-
45
- const deleted: [string, string][] = [];
46
- const handlers: ActionHandlers = {
47
- modelDelete: async (model, id) => {
48
- deleted.push([model, id]);
49
- },
50
- };
51
- const actionCtx: ActionContext = {
52
- widgetContext: rowCtx,
53
- state: new StateStore(),
54
- };
55
- await dispatch({ type: 'model.delete' }, actionCtx, handlers);
56
- expect(deleted).toEqual([['sales.orderItem', '10']]);
57
- });
58
- });
59
-
60
- describe('Integration: $state reactivity scenario', () => {
61
- test('action writes $state → dependent widgets flagged for re-render', async () => {
62
- const state = new StateStore();
63
- const nodes: WidgetNode[] = [
64
- { id: 'step1', type: 'section', visible: { field: '$state.step', operator: 'eq', value: 1 } },
65
- { id: 'step2', type: 'section', visible: { field: '$state.step', operator: 'eq', value: 2 } },
66
- {
67
- id: 'nextBtn',
68
- type: 'button',
69
- props: { label: '{{if($state.step == 2, "Submit", "Next")}}' },
70
- },
71
- ];
72
-
73
- const depMap = buildDependencyMap(nodes);
74
-
75
- const actionCtx: ActionContext = {
76
- widgetContext: createRootContext({}, 'app.page', 'view'),
77
- state,
78
- };
79
- await dispatch({ type: 'setValue', field: '$state.step', value: 2 }, actionCtx, {});
80
-
81
- expect(state.get('step')).toBe(2);
82
-
83
- const affected = getAffectedWidgets(depMap, 'step', true);
84
- expect(affected).toContain('step1');
85
- expect(affected).toContain('step2');
86
- expect(affected).toContain('nextBtn');
87
- });
88
- });
89
-
90
- describe('Integration: Wizard scenario', () => {
91
- test('$state.step changes → visibility toggles → props update', async () => {
92
- const state = new StateStore();
93
- state.set('step', 1);
94
-
95
- const flat = { $state: { step: 1 } };
96
-
97
- expect(evaluateConditions({ field: '$state.step', operator: 'eq', value: 1 }, flat)).toBe(true);
98
- expect(evaluateConditions({ field: '$state.step', operator: 'eq', value: 2 }, flat)).toBe(
99
- false,
100
- );
101
-
102
- const labelAst = parse('{{if($state.step == 2, "Submit", "Next")}}');
103
- expect(evaluate(labelAst, flat)).toBe('Next');
104
-
105
- state.set('step', 2);
106
- const flat2 = { $state: { step: 2 } };
107
-
108
- expect(evaluateConditions({ field: '$state.step', operator: 'eq', value: 1 }, flat2)).toBe(
109
- false,
110
- );
111
- expect(evaluateConditions({ field: '$state.step', operator: 'eq', value: 2 }, flat2)).toBe(
112
- true,
113
- );
114
- expect(evaluate(labelAst, flat2)).toBe('Submit');
115
- });
116
- });
@@ -1,70 +0,0 @@
1
- import { describe, it, expect, vi, afterEach } from 'vitest';
2
- import React from 'react';
3
- import { render, screen, fireEvent, cleanup } from '@testing-library/react';
4
- import { JsonWidget } from '../components/JsonWidget.js';
5
-
6
- const defaultProps = {
7
- props: {},
8
- bind: { value: null },
9
- on: {},
10
- context: { record: {}, model: '', mode: 'view' as const },
11
- };
12
-
13
- afterEach(() => cleanup());
14
-
15
- describe('JsonWidget', () => {
16
- it('renders a textarea', () => {
17
- const { container } = render(<JsonWidget {...defaultProps} />);
18
- expect(container.querySelector('textarea')).toBeInTheDocument();
19
- });
20
-
21
- it('has monospace font', () => {
22
- const { container } = render(<JsonWidget {...defaultProps} />);
23
- expect(container.querySelector('textarea')?.className).toContain('font-mono');
24
- });
25
-
26
- it('displays label from props', () => {
27
- render(<JsonWidget {...defaultProps} props={{ label: 'Metadata' }} />);
28
- expect(screen.getByText('Metadata')).toBeInTheDocument();
29
- });
30
-
31
- it('renders object value as formatted JSON', () => {
32
- const { container } = render(<JsonWidget {...defaultProps} bind={{ value: { key: 'val' } }} />);
33
- expect(container.querySelector('textarea')?.value).toContain('"key": "val"');
34
- });
35
-
36
- it('formats JSON on blur', () => {
37
- const setValue = vi.fn();
38
- const { container } = render(<JsonWidget {...defaultProps} bind={{ value: null, setValue }} />);
39
- const textarea = container.querySelector('textarea')!;
40
- fireEvent.change(textarea, { target: { value: '{"a":1}' } });
41
- fireEvent.blur(textarea);
42
- expect(textarea.value).toContain('"a": 1');
43
- expect(setValue).toHaveBeenCalledWith({ a: 1 });
44
- });
45
-
46
- it('shows error for invalid JSON on blur', () => {
47
- const { container } = render(<JsonWidget {...defaultProps} bind={{ value: null }} />);
48
- const textarea = container.querySelector('textarea')!;
49
- fireEvent.change(textarea, { target: { value: '{invalid' } });
50
- fireEvent.blur(textarea);
51
- expect(screen.getByRole('alert').textContent).toBe('Invalid JSON');
52
- });
53
-
54
- it('handles disabled state', () => {
55
- const { container } = render(<JsonWidget {...defaultProps} props={{ disabled: true }} />);
56
- expect(container.querySelector('textarea')?.disabled).toBe(true);
57
- });
58
-
59
- it('handles null bind.value gracefully', () => {
60
- const { container } = render(<JsonWidget {...defaultProps} bind={{ value: null }} />);
61
- expect(container.querySelector('textarea')?.value).toBe('');
62
- });
63
-
64
- it('fires on.change when typing', () => {
65
- const onChange = vi.fn();
66
- const { container } = render(<JsonWidget {...defaultProps} on={{ change: onChange }} />);
67
- fireEvent.change(container.querySelector('textarea')!, { target: { value: '{}' } });
68
- expect(onChange).toHaveBeenCalledWith('{}');
69
- });
70
- });
@@ -1,92 +0,0 @@
1
- import { describe, it, expect, vi, afterEach } from 'vitest';
2
- import React from 'react';
3
- import { render, screen, fireEvent, cleanup } from '@testing-library/react';
4
- import userEvent from '@testing-library/user-event';
5
- import { LinkWidget } from '../components/LinkWidget.js';
6
-
7
- const defaultProps = {
8
- props: {},
9
- bind: {
10
- value: null,
11
- meta: {
12
- type: 'link',
13
- label: 'Customer',
14
- required: false,
15
- readOnly: false,
16
- options: [
17
- { label: 'Acme Corp', value: 'cust-1' },
18
- { label: 'Globex', value: 'cust-2' },
19
- ],
20
- },
21
- },
22
- on: {},
23
- context: { record: {}, model: '', mode: 'view' as const },
24
- };
25
-
26
- afterEach(() => cleanup());
27
-
28
- describe('LinkWidget', () => {
29
- it('renders a combobox button', () => {
30
- render(<LinkWidget {...defaultProps} />);
31
- expect(screen.getByRole('combobox')).toBeInTheDocument();
32
- });
33
-
34
- it('displays label from props', () => {
35
- render(<LinkWidget {...defaultProps} props={{ label: 'Supplier' }} />);
36
- expect(screen.getByText('Supplier')).toBeInTheDocument();
37
- });
38
-
39
- it('falls back to bind.meta.label', () => {
40
- render(<LinkWidget {...defaultProps} />);
41
- expect(screen.getByText('Customer')).toBeInTheDocument();
42
- });
43
-
44
- it('shows placeholder when no value', () => {
45
- render(<LinkWidget {...defaultProps} />);
46
- expect(screen.getByText('Select...')).toBeInTheDocument();
47
- });
48
-
49
- it('shows selected option label', () => {
50
- render(<LinkWidget {...defaultProps} bind={{ ...defaultProps.bind, value: 'cust-1' }} />);
51
- expect(screen.getByText('Acme Corp')).toBeInTheDocument();
52
- });
53
-
54
- it('handles disabled state', () => {
55
- render(<LinkWidget {...defaultProps} props={{ disabled: true }} />);
56
- expect(screen.getByRole('combobox')).toBeDisabled();
57
- });
58
-
59
- it.skip('opens dropdown on click', async () => {
60
- render(<LinkWidget {...defaultProps} />);
61
- await userEvent.click(screen.getByRole('combobox'));
62
- expect(screen.getByPlaceholderText('Search...')).toBeInTheDocument();
63
- });
64
-
65
- it.skip('shows options in dropdown', async () => {
66
- render(<LinkWidget {...defaultProps} />);
67
- await userEvent.click(screen.getByRole('combobox'));
68
- expect(screen.getByText('Acme Corp')).toBeInTheDocument();
69
- expect(screen.getByText('Globex')).toBeInTheDocument();
70
- });
71
-
72
- it.skip('fires on.change when selecting', async () => {
73
- const onChange = vi.fn();
74
- render(<LinkWidget {...defaultProps} on={{ change: onChange }} />);
75
- await userEvent.click(screen.getByRole('combobox'));
76
- await userEvent.click(screen.getByText('Globex'));
77
- expect(onChange).toHaveBeenCalledWith('cust-2');
78
- });
79
-
80
- it.skip('fires on.search when typing', async () => {
81
- const onSearch = vi.fn();
82
- render(<LinkWidget {...defaultProps} on={{ search: onSearch }} />);
83
- await userEvent.click(screen.getByRole('combobox'));
84
- fireEvent.change(screen.getByPlaceholderText('Search...'), { target: { value: 'acm' } });
85
- expect(onSearch).toHaveBeenCalledWith('acm');
86
- });
87
-
88
- it('handles null bind.value gracefully', () => {
89
- render(<LinkWidget {...defaultProps} bind={{ ...defaultProps.bind, value: null }} />);
90
- expect(screen.getByText('Select...')).toBeInTheDocument();
91
- });
92
- });
@@ -1,93 +0,0 @@
1
- import { describe, it, expect, vi, afterEach } from 'vitest';
2
- import React from 'react';
3
- import { render, screen, fireEvent, cleanup } from '@testing-library/react';
4
- import userEvent from '@testing-library/user-event';
5
- import { ManyToManyWidget } from '../components/ManyToManyWidget.js';
6
-
7
- const defaultProps = {
8
- props: {},
9
- bind: {
10
- value: [],
11
- meta: {
12
- type: 'manyToMany',
13
- label: 'Tags',
14
- required: false,
15
- readOnly: false,
16
- options: [
17
- { label: 'VIP', value: 'tag-1' },
18
- { label: 'Active', value: 'tag-2' },
19
- { label: 'New', value: 'tag-3' },
20
- ],
21
- },
22
- },
23
- on: {},
24
- context: { record: {}, model: '', mode: 'view' as const },
25
- };
26
-
27
- afterEach(() => cleanup());
28
-
29
- describe('ManyToManyWidget', () => {
30
- it('renders a combobox button', () => {
31
- render(<ManyToManyWidget {...defaultProps} />);
32
- expect(screen.getByRole('combobox')).toBeInTheDocument();
33
- });
34
-
35
- it('displays label', () => {
36
- render(<ManyToManyWidget {...defaultProps} />);
37
- expect(screen.getByText('Tags')).toBeInTheDocument();
38
- });
39
-
40
- it('shows placeholder when empty', () => {
41
- render(<ManyToManyWidget {...defaultProps} />);
42
- expect(screen.getByText('Select...')).toBeInTheDocument();
43
- });
44
-
45
- it('shows badges for selected values', () => {
46
- render(
47
- <ManyToManyWidget
48
- {...defaultProps}
49
- bind={{ ...defaultProps.bind, value: ['tag-1', 'tag-2'] }}
50
- />,
51
- );
52
- expect(screen.getByText('VIP')).toBeInTheDocument();
53
- expect(screen.getByText('Active')).toBeInTheDocument();
54
- });
55
-
56
- it('handles disabled state', () => {
57
- render(<ManyToManyWidget {...defaultProps} props={{ disabled: true }} />);
58
- expect(screen.getByRole('combobox')).toBeDisabled();
59
- });
60
-
61
- it.skip('fires on.change when toggling selection', async () => {
62
- const onChange = vi.fn();
63
- render(<ManyToManyWidget {...defaultProps} on={{ change: onChange }} />);
64
- await userEvent.click(screen.getByRole('combobox'));
65
- await userEvent.click(screen.getByText('VIP'));
66
- expect(onChange).toHaveBeenCalledWith(['tag-1']);
67
- });
68
-
69
- it('removes item via badge button', () => {
70
- const setValue = vi.fn();
71
- render(
72
- <ManyToManyWidget
73
- {...defaultProps}
74
- bind={{ ...defaultProps.bind, value: ['tag-1', 'tag-2'], setValue }}
75
- />,
76
- );
77
- fireEvent.click(screen.getByLabelText('Remove VIP'));
78
- expect(setValue).toHaveBeenCalledWith(['tag-2']);
79
- });
80
-
81
- it('handles null bind.value gracefully', () => {
82
- render(<ManyToManyWidget {...defaultProps} bind={{ ...defaultProps.bind, value: null }} />);
83
- expect(screen.getByText('Select...')).toBeInTheDocument();
84
- });
85
-
86
- it.skip('fires on.search when typing', async () => {
87
- const onSearch = vi.fn();
88
- render(<ManyToManyWidget {...defaultProps} on={{ search: onSearch }} />);
89
- await userEvent.click(screen.getByRole('combobox'));
90
- fireEvent.change(screen.getByPlaceholderText('Search...'), { target: { value: 'vi' } });
91
- expect(onSearch).toHaveBeenCalledWith('vi');
92
- });
93
- });
@@ -1,148 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import React from 'react';
3
- import { render, screen, fireEvent, cleanup } from '@testing-library/react';
4
- import { ModalWidget } from '../components/ModalWidget.js';
5
- import { PageStateProvider } from '../hooks/usePageState.js';
6
- import { StateStore } from '../state/store.js';
7
-
8
- function renderWithState(ui: React.ReactElement, state?: StateStore) {
9
- const store = state ?? new StateStore();
10
- return { ...render(<PageStateProvider value={store}>{ui}</PageStateProvider>), store };
11
- }
12
-
13
- beforeEach(() => {});
14
-
15
- afterEach(() => {
16
- cleanup();
17
- });
18
-
19
- describe('ModalWidget', () => {
20
- it('renders children', () => {
21
- renderWithState(
22
- <ModalWidget
23
- props={{ size: 'md' }}
24
- bind={{ value: undefined }}
25
- on={{}}
26
- context={{ record: {}, model: '', mode: 'view' }}
27
- >
28
- <div data-testid="child">Hello</div>
29
- </ModalWidget>,
30
- );
31
-
32
- expect(screen.getByTestId('child')).toBeInTheDocument();
33
- expect(screen.getByTestId('child')).toHaveTextContent('Hello');
34
- });
35
-
36
- it('renders with sm size', () => {
37
- renderWithState(
38
- <ModalWidget
39
- props={{ size: 'sm' }}
40
- bind={{ value: undefined }}
41
- on={{}}
42
- context={{ record: {}, model: '', mode: 'view' }}
43
- >
44
- <div>Content</div>
45
- </ModalWidget>,
46
- );
47
-
48
- const dialog = screen.getByRole('dialog');
49
- expect(dialog.className).toContain('sm:max-w-sm');
50
- });
51
-
52
- it('renders with md size by default', () => {
53
- renderWithState(
54
- <ModalWidget
55
- props={{}}
56
- bind={{ value: undefined }}
57
- on={{}}
58
- context={{ record: {}, model: '', mode: 'view' }}
59
- >
60
- <div>Content</div>
61
- </ModalWidget>,
62
- );
63
-
64
- const dialog = screen.getByRole('dialog');
65
- expect(dialog.className).toContain('sm:max-w-lg');
66
- });
67
-
68
- it('renders with lg size', () => {
69
- renderWithState(
70
- <ModalWidget
71
- props={{ size: 'lg' }}
72
- bind={{ value: undefined }}
73
- on={{}}
74
- context={{ record: {}, model: '', mode: 'view' }}
75
- >
76
- <div>Content</div>
77
- </ModalWidget>,
78
- );
79
-
80
- const dialog = screen.getByRole('dialog');
81
- expect(dialog.className).toContain('sm:max-w-3xl');
82
- });
83
-
84
- it('renders title', () => {
85
- renderWithState(
86
- <ModalWidget
87
- props={{ title: 'Confirm Action' }}
88
- bind={{ value: undefined }}
89
- on={{}}
90
- context={{ record: {}, model: '', mode: 'view' }}
91
- >
92
- <div>Content</div>
93
- </ModalWidget>,
94
- );
95
-
96
- expect(screen.getByText('Confirm Action')).toBeInTheDocument();
97
- });
98
-
99
- it('renders close button by default', () => {
100
- renderWithState(
101
- <ModalWidget
102
- props={{ title: 'Test' }}
103
- bind={{ value: undefined }}
104
- on={{}}
105
- context={{ record: {}, model: '', mode: 'view' }}
106
- >
107
- <div>Content</div>
108
- </ModalWidget>,
109
- );
110
-
111
- expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument();
112
- });
113
-
114
- it('close button sets $state key to false', () => {
115
- const store = new StateStore();
116
- store.set('showModal', true);
117
-
118
- renderWithState(
119
- <ModalWidget
120
- props={{ title: 'Test', _visibleField: '$state.showModal' }}
121
- bind={{ value: undefined }}
122
- on={{}}
123
- context={{ record: {}, model: '', mode: 'view' }}
124
- >
125
- <div>Content</div>
126
- </ModalWidget>,
127
- store,
128
- );
129
-
130
- fireEvent.click(screen.getByRole('button', { name: 'Close' }));
131
- expect(store.get('showModal')).toBe(false);
132
- });
133
-
134
- it('has correct aria role', () => {
135
- renderWithState(
136
- <ModalWidget
137
- props={{ title: 'My Modal' }}
138
- bind={{ value: undefined }}
139
- on={{}}
140
- context={{ record: {}, model: '', mode: 'view' }}
141
- >
142
- <div>Content</div>
143
- </ModalWidget>,
144
- );
145
-
146
- expect(screen.getByRole('dialog')).toBeInTheDocument();
147
- });
148
- });
@@ -1,97 +0,0 @@
1
- import { describe, it, expect, vi, afterEach } from 'vitest';
2
- import React from 'react';
3
- import { render, screen, fireEvent, cleanup } from '@testing-library/react';
4
- import { MoneyWidget } from '../components/MoneyWidget.js';
5
-
6
- const defaultProps = {
7
- props: {},
8
- bind: { value: null },
9
- on: {},
10
- context: { record: {}, model: '', mode: 'view' as const },
11
- };
12
-
13
- afterEach(() => cleanup());
14
-
15
- describe('MoneyWidget', () => {
16
- it('renders input element', () => {
17
- const { container } = render(<MoneyWidget {...defaultProps} />);
18
- expect(container.querySelector('input')).toBeInTheDocument();
19
- });
20
-
21
- it('displays label from props', () => {
22
- render(<MoneyWidget {...defaultProps} props={{ label: 'Amount' }} />);
23
- expect(screen.getByText('Amount')).toBeInTheDocument();
24
- });
25
-
26
- it('shows currency prefix', () => {
27
- render(<MoneyWidget {...defaultProps} props={{ currency: '€' }} />);
28
- expect(screen.getByText('€')).toBeInTheDocument();
29
- });
30
-
31
- it('shows default $ prefix', () => {
32
- render(<MoneyWidget {...defaultProps} />);
33
- expect(screen.getByText('$')).toBeInTheDocument();
34
- });
35
-
36
- it('displays value with 2 decimal places', () => {
37
- const { container } = render(<MoneyWidget {...defaultProps} bind={{ value: 1234.5 }} />);
38
- expect(container.querySelector('input')?.value).toBe('1234.50');
39
- });
40
-
41
- it('fires on.change with numeric value', () => {
42
- const onChange = vi.fn();
43
- const { container } = render(<MoneyWidget {...defaultProps} on={{ change: onChange }} />);
44
- fireEvent.focus(container.querySelector('input')!);
45
- fireEvent.change(container.querySelector('input')!, { target: { value: '99.99' } });
46
- expect(onChange).toHaveBeenCalledWith(99.99);
47
- });
48
-
49
- it('fires on.change with null for empty input', () => {
50
- const onChange = vi.fn();
51
- const { container } = render(
52
- <MoneyWidget {...defaultProps} bind={{ value: 100 }} on={{ change: onChange }} />,
53
- );
54
- fireEvent.focus(container.querySelector('input')!);
55
- fireEvent.change(container.querySelector('input')!, { target: { value: '' } });
56
- expect(onChange).toHaveBeenCalledWith(null);
57
- });
58
-
59
- it('formats value on blur', () => {
60
- const { container } = render(<MoneyWidget {...defaultProps} bind={{ value: null }} />);
61
- const input = container.querySelector('input')!;
62
- fireEvent.focus(input);
63
- fireEvent.change(input, { target: { value: '42.1' } });
64
- fireEvent.blur(input);
65
- expect(input.value).toBe('42.10');
66
- });
67
-
68
- it('handles disabled state', () => {
69
- const { container } = render(<MoneyWidget {...defaultProps} props={{ disabled: true }} />);
70
- expect(container.querySelector('input')?.disabled).toBe(true);
71
- });
72
-
73
- it('handles null bind.value gracefully', () => {
74
- const { container } = render(<MoneyWidget {...defaultProps} bind={{ value: null }} />);
75
- expect(container.querySelector('input')?.value).toBe('');
76
- });
77
-
78
- it('handles readOnly from bind.meta', () => {
79
- const { container } = render(
80
- <MoneyWidget
81
- {...defaultProps}
82
- bind={{ value: 0, meta: { type: 'money', label: 'X', required: false, readOnly: true } }}
83
- />,
84
- );
85
- expect(container.querySelector('input')?.readOnly).toBe(true);
86
- });
87
-
88
- it('calls bind.setValue on change', () => {
89
- const setValue = vi.fn();
90
- const { container } = render(
91
- <MoneyWidget {...defaultProps} bind={{ value: null, setValue }} />,
92
- );
93
- fireEvent.focus(container.querySelector('input')!);
94
- fireEvent.change(container.querySelector('input')!, { target: { value: '50' } });
95
- expect(setValue).toHaveBeenCalledWith(50);
96
- });
97
- });