@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,509 +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 { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5
- import type { WidgetNode } from '@rangka/shared';
6
- import { registerWidget, clearWidgetRegistry } from '../registry.js';
7
- import { SlotRenderer } from '../renderer/SlotRenderer.js';
8
- import { StateStore } from '../state/store.js';
9
- import { buildRowContext } from '../context/builder.js';
10
- import { TableWidget } from '../components/TableWidget.js';
11
- import { ColumnWidget } from '../components/ColumnWidget.js';
12
- import { MetaProvider } from '../../context/MetaContext.js';
13
- import type { WidgetProps } from '../types.js';
14
- import type { WidgetContext } from '../context/types.js';
15
- import type { ComponentType } from 'react';
16
-
17
- function TestText({ props, bind }: WidgetProps) {
18
- return <span data-testid="cell-text">{String(bind?.value ?? props?.text ?? '')}</span>;
19
- }
20
-
21
- function TestBadge({ bind }: WidgetProps) {
22
- return <span data-testid="cell-badge">{String(bind?.value ?? '')}</span>;
23
- }
24
-
25
- beforeEach(() => {
26
- clearWidgetRegistry();
27
- registerWidget(
28
- {
29
- name: 'table',
30
- label: 'Table',
31
- category: 'layout',
32
- schema: {},
33
- binding: 'model',
34
- triggers: ['rowClick', 'select', 'pageChange'],
35
- container: true,
36
- accepts: ['column'],
37
- },
38
- TableWidget as unknown as ComponentType<WidgetProps>,
39
- );
40
- registerWidget(
41
- {
42
- name: 'column',
43
- label: 'Column',
44
- category: 'layout',
45
- schema: {},
46
- binding: 'none',
47
- triggers: [],
48
- container: true,
49
- },
50
- ColumnWidget as unknown as ComponentType<WidgetProps>,
51
- );
52
- registerWidget(
53
- {
54
- name: 'text',
55
- label: 'Text',
56
- category: 'display',
57
- schema: {},
58
- binding: 'expression',
59
- triggers: [],
60
- container: false,
61
- },
62
- TestText,
63
- );
64
- registerWidget(
65
- {
66
- name: 'badge',
67
- label: 'Badge',
68
- category: 'display',
69
- schema: {},
70
- binding: 'field',
71
- triggers: [],
72
- container: false,
73
- },
74
- TestBadge,
75
- );
76
- });
77
-
78
- afterEach(() => {
79
- cleanup();
80
- clearWidgetRegistry();
81
- });
82
-
83
- function renderWithContext(
84
- nodes: WidgetNode[],
85
- records: Record<string, unknown>[] = [],
86
- model = 'sales.order',
87
- ) {
88
- const ctx: WidgetContext = {
89
- record: {},
90
- records,
91
- model,
92
- mode: 'view',
93
- };
94
- const state = new StateStore();
95
- const queryClient = new QueryClient({
96
- defaultOptions: { queries: { retry: false, gcTime: 0 } },
97
- });
98
- const meta = {
99
- navigation: [],
100
- pages: [],
101
- models: {
102
- [model]: {
103
- qualifiedName: model,
104
- fields: [
105
- { name: 'customer', type: 'string', label: 'Customer' },
106
- { name: 'total', type: 'money', label: 'Total' },
107
- { name: 'status', type: 'enum', label: 'Status', options: ['draft', 'confirmed'] },
108
- ],
109
- },
110
- },
111
- };
112
- return render(
113
- <MetaProvider meta={meta}>
114
- <QueryClientProvider client={queryClient}>
115
- <SlotRenderer nodes={nodes} context={ctx} state={state} />
116
- </QueryClientProvider>
117
- </MetaProvider>,
118
- );
119
- }
120
-
121
- describe('TableWidget', () => {
122
- const sampleOrders = [
123
- { id: '1', customer: 'Acme Corp', total: 1500, status: 'draft' },
124
- { id: '2', customer: 'Beta Inc', total: 2300, status: 'confirmed' },
125
- { id: '3', customer: 'Gamma Ltd', total: 800, status: 'draft' },
126
- ];
127
-
128
- describe('basic rendering', () => {
129
- it('renders table with column headers', () => {
130
- const nodes: WidgetNode[] = [
131
- {
132
- type: 'table',
133
- bind: { model: { name: 'sales.order' } },
134
- children: [
135
- { type: 'column', props: { label: 'Customer' } },
136
- { type: 'column', props: { label: 'Total' } },
137
- { type: 'column', props: { label: 'Status' } },
138
- ],
139
- },
140
- ];
141
-
142
- renderWithContext(nodes, sampleOrders);
143
-
144
- expect(screen.getByText('Customer')).toBeInTheDocument();
145
- expect(screen.getByText('Total')).toBeInTheDocument();
146
- expect(screen.getByText('Status')).toBeInTheDocument();
147
- });
148
-
149
- it('renders rows from records', () => {
150
- const nodes: WidgetNode[] = [
151
- {
152
- type: 'table',
153
- bind: { model: { name: 'sales.order' } },
154
- children: [
155
- {
156
- type: 'column',
157
- props: { label: 'Customer' },
158
- bind: { field: 'customer' },
159
- },
160
- ],
161
- },
162
- ];
163
-
164
- renderWithContext(nodes, sampleOrders);
165
-
166
- expect(screen.getByText('Acme Corp')).toBeInTheDocument();
167
- expect(screen.getByText('Beta Inc')).toBeInTheDocument();
168
- expect(screen.getByText('Gamma Ltd')).toBeInTheDocument();
169
- });
170
-
171
- it('renders "No data" when records are empty', () => {
172
- const nodes: WidgetNode[] = [
173
- {
174
- type: 'table',
175
- bind: { model: { name: 'sales.order' } },
176
- children: [{ type: 'column', props: { label: 'Customer' } }],
177
- },
178
- ];
179
-
180
- renderWithContext(nodes, []);
181
-
182
- expect(screen.getByText('No data')).toBeInTheDocument();
183
- });
184
-
185
- it('renders with bordered prop', () => {
186
- const nodes: WidgetNode[] = [
187
- {
188
- type: 'table',
189
- bind: { model: { name: 'sales.order' } },
190
- props: { bordered: true },
191
- children: [{ type: 'column', props: { label: 'Name' } }],
192
- },
193
- ];
194
-
195
- renderWithContext(nodes, sampleOrders);
196
- const table = screen.getByRole('table');
197
- expect(table.className).toContain('border');
198
- });
199
-
200
- it('renders with selectable prop', () => {
201
- const nodes: WidgetNode[] = [
202
- {
203
- type: 'table',
204
- bind: { model: { name: 'sales.order' } },
205
- props: { selectable: true },
206
- children: [{ type: 'column', props: { label: 'Name' } }],
207
- },
208
- ];
209
-
210
- renderWithContext(nodes, sampleOrders);
211
- const checkboxes = screen.getAllByRole('checkbox');
212
- // 1 header + 3 rows
213
- expect(checkboxes.length).toBe(4);
214
- });
215
- });
216
-
217
- describe('row-scoped context', () => {
218
- it('renders column children with row data', () => {
219
- const nodes: WidgetNode[] = [
220
- {
221
- type: 'table',
222
- bind: { model: { name: 'sales.order' } },
223
- children: [
224
- {
225
- type: 'column',
226
- props: { label: 'Customer' },
227
- children: [{ type: 'text', bind: { expression: '{{customer}}' } }],
228
- },
229
- ],
230
- },
231
- ];
232
-
233
- renderWithContext(nodes, sampleOrders);
234
-
235
- const cells = screen.getAllByTestId('cell-text');
236
- expect(cells).toHaveLength(3);
237
- expect(cells[0]).toHaveTextContent('Acme Corp');
238
- expect(cells[1]).toHaveTextContent('Beta Inc');
239
- expect(cells[2]).toHaveTextContent('Gamma Ltd');
240
- });
241
-
242
- it('renders badge widget bound to row field', () => {
243
- const nodes: WidgetNode[] = [
244
- {
245
- type: 'table',
246
- bind: { model: { name: 'sales.order' } },
247
- children: [
248
- {
249
- type: 'column',
250
- props: { label: 'Status' },
251
- children: [{ type: 'badge', bind: { field: 'status' } }],
252
- },
253
- ],
254
- },
255
- ];
256
-
257
- renderWithContext(nodes, sampleOrders);
258
-
259
- const badges = screen.getAllByTestId('cell-badge');
260
- expect(badges).toHaveLength(3);
261
- expect(badges[0]).toHaveTextContent('draft');
262
- expect(badges[1]).toHaveTextContent('confirmed');
263
- });
264
-
265
- it('renders expressions using row fields', () => {
266
- const nodes: WidgetNode[] = [
267
- {
268
- type: 'table',
269
- bind: { model: { name: 'sales.order' } },
270
- children: [
271
- {
272
- type: 'column',
273
- props: { label: 'Display' },
274
- children: [{ type: 'text', bind: { expression: '{{customer}}' } }],
275
- },
276
- ],
277
- },
278
- ];
279
-
280
- const records = [{ id: '1', customer: 'Test Co', total: 100 }];
281
-
282
- renderWithContext(nodes, records);
283
-
284
- expect(screen.getByTestId('cell-text')).toHaveTextContent('Test Co');
285
- });
286
- });
287
-
288
- describe('triggers', () => {
289
- it('fires rowClick trigger on row click', () => {
290
- const nodes: WidgetNode[] = [
291
- {
292
- type: 'table',
293
- bind: { model: { name: 'sales.order' } },
294
- on: { rowClick: { type: 'setValue', field: '$state.selected', value: '{{id}}' } },
295
- children: [
296
- {
297
- type: 'column',
298
- props: { label: 'Customer' },
299
- bind: { field: 'customer' },
300
- },
301
- ],
302
- },
303
- ];
304
-
305
- renderWithContext(nodes, sampleOrders);
306
-
307
- const rows = screen.getAllByRole('row');
308
- // rows[0] is header, click first data row
309
- fireEvent.click(rows[1]);
310
-
311
- // Row should have cursor-pointer class
312
- expect(rows[1].className).toContain('cursor-pointer');
313
- });
314
-
315
- it('fires select trigger on checkbox click', () => {
316
- const nodes: WidgetNode[] = [
317
- {
318
- type: 'table',
319
- bind: { model: { name: 'sales.order' } },
320
- props: { selectable: true },
321
- on: { select: { type: 'setValue', field: '$state.selected', value: '{{id}}' } },
322
- children: [{ type: 'column', props: { label: 'Customer' }, bind: { field: 'customer' } }],
323
- },
324
- ];
325
-
326
- renderWithContext(nodes, sampleOrders);
327
-
328
- const checkboxes = screen.getAllByRole('checkbox');
329
- // Click first row checkbox (index 1, since 0 is select all)
330
- fireEvent.click(checkboxes[1]);
331
-
332
- // Should not crash
333
- expect(checkboxes[1]).toBeInTheDocument();
334
- });
335
- });
336
-
337
- describe('column props', () => {
338
- it('applies column width', () => {
339
- const nodes: WidgetNode[] = [
340
- {
341
- type: 'table',
342
- bind: { model: { name: 'sales.order' } },
343
- children: [
344
- { type: 'column', props: { label: 'ID', width: '80px' } },
345
- { type: 'column', props: { label: 'Name' } },
346
- ],
347
- },
348
- ];
349
-
350
- renderWithContext(nodes, [{ id: '1' }]);
351
-
352
- const headers = screen.getAllByRole('columnheader');
353
- expect(headers[0].style.width).toBe('80px');
354
- });
355
-
356
- it('applies column alignment', () => {
357
- const nodes: WidgetNode[] = [
358
- {
359
- type: 'table',
360
- bind: { model: { name: 'sales.order' } },
361
- children: [
362
- { type: 'column', props: { label: 'Amount', align: 'right' } },
363
- { type: 'column', props: { label: 'Name', align: 'center' } },
364
- ],
365
- },
366
- ];
367
-
368
- renderWithContext(nodes, [{ id: '1' }]);
369
-
370
- const headers = screen.getAllByRole('columnheader');
371
- expect(headers[0].className).toContain('text-right');
372
- expect(headers[1].className).toContain('text-center');
373
- });
374
- });
375
-
376
- describe('edge cases', () => {
377
- it('handles records with missing fields gracefully', () => {
378
- const nodes: WidgetNode[] = [
379
- {
380
- type: 'table',
381
- bind: { model: { name: 'sales.order' } },
382
- children: [
383
- { type: 'column', props: { label: 'Name' }, bind: { field: 'name' } },
384
- { type: 'column', props: { label: 'Missing' }, bind: { field: 'nonexistent' } },
385
- ],
386
- },
387
- ];
388
-
389
- renderWithContext(nodes, [{ id: '1', name: 'Test' }]);
390
-
391
- expect(screen.getByText('Test')).toBeInTheDocument();
392
- });
393
-
394
- it('handles large dataset (100 rows)', () => {
395
- const records = Array.from({ length: 100 }, (_, i) => ({
396
- id: String(i),
397
- name: `Item ${i}`,
398
- }));
399
-
400
- const nodes: WidgetNode[] = [
401
- {
402
- type: 'table',
403
- bind: { model: { name: 'test.model' } },
404
- children: [{ type: 'column', props: { label: 'Name' }, bind: { field: 'name' } }],
405
- },
406
- ];
407
-
408
- renderWithContext(nodes, records);
409
-
410
- expect(screen.getByText('Item 0')).toBeInTheDocument();
411
- expect(screen.getByText('Item 99')).toBeInTheDocument();
412
- });
413
-
414
- it('handles table with no children columns', () => {
415
- const nodes: WidgetNode[] = [
416
- {
417
- type: 'table',
418
- bind: { model: { name: 'sales.order' } },
419
- },
420
- ];
421
-
422
- renderWithContext(nodes, sampleOrders);
423
-
424
- // Should render without crashing, no columns means no cells
425
- const table = screen.getByRole('table');
426
- expect(table).toBeInTheDocument();
427
- });
428
-
429
- it('handles null values in row data', () => {
430
- const records = [{ id: '1', name: null, amount: undefined }];
431
-
432
- const nodes: WidgetNode[] = [
433
- {
434
- type: 'table',
435
- bind: { model: { name: 'test.model' } },
436
- children: [
437
- { type: 'column', props: { label: 'Name' }, bind: { field: 'name' } },
438
- { type: 'column', props: { label: 'Amount' }, bind: { field: 'amount' } },
439
- ],
440
- },
441
- ];
442
-
443
- renderWithContext(nodes, records);
444
-
445
- // Should not crash
446
- const table = screen.getByRole('table');
447
- expect(table).toBeInTheDocument();
448
- });
449
-
450
- it('renders multiple columns with different child widgets', () => {
451
- const nodes: WidgetNode[] = [
452
- {
453
- type: 'table',
454
- bind: { model: { name: 'sales.order' } },
455
- children: [
456
- {
457
- type: 'column',
458
- props: { label: 'Name' },
459
- children: [{ type: 'text', bind: { expression: '{{customer}}' } }],
460
- },
461
- {
462
- type: 'column',
463
- props: { label: 'Status' },
464
- children: [{ type: 'badge', bind: { field: 'status' } }],
465
- },
466
- ],
467
- },
468
- ];
469
-
470
- renderWithContext(nodes, sampleOrders);
471
-
472
- expect(screen.getAllByTestId('cell-text')).toHaveLength(3);
473
- expect(screen.getAllByTestId('cell-badge')).toHaveLength(3);
474
- });
475
- });
476
- });
477
-
478
- describe('buildRowContext', () => {
479
- it('creates row context with correct record and index', () => {
480
- const tableCtx: WidgetContext = {
481
- record: {},
482
- records: [{ id: '1', name: 'A' }],
483
- model: 'sales.order',
484
- mode: 'view',
485
- parent: { record: { page: 1 }, model: 'root', mode: 'view' },
486
- };
487
-
488
- const rowCtx = buildRowContext({ id: '1', name: 'A' }, 0, tableCtx);
489
-
490
- expect(rowCtx.record).toEqual({ id: '1', name: 'A' });
491
- expect(rowCtx.index).toBe(0);
492
- expect(rowCtx.model).toBe('sales.order');
493
- expect(rowCtx.parent).toBe(tableCtx.parent);
494
- });
495
-
496
- it('row context parent points to table parent (not table)', () => {
497
- const rootCtx: WidgetContext = { record: { root: true }, model: 'root', mode: 'view' };
498
- const tableCtx: WidgetContext = {
499
- record: {},
500
- records: [],
501
- model: 'sales.order',
502
- mode: 'view',
503
- parent: rootCtx,
504
- };
505
-
506
- const rowCtx = buildRowContext({ id: '1' }, 0, tableCtx);
507
- expect(rowCtx.parent).toBe(rootCtx);
508
- });
509
- });
@@ -1,105 +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 { TextareaWidget } from '../components/TextareaWidget.js';
5
-
6
- const defaultProps = {
7
- props: {},
8
- bind: { value: '' },
9
- on: {},
10
- context: { record: {}, model: '', mode: 'view' as const },
11
- };
12
-
13
- afterEach(() => cleanup());
14
-
15
- describe('TextareaWidget', () => {
16
- it('renders textarea element', () => {
17
- const { container } = render(<TextareaWidget {...defaultProps} />);
18
- expect(container.querySelector('textarea')).toBeInTheDocument();
19
- });
20
-
21
- it('displays label from props', () => {
22
- render(<TextareaWidget {...defaultProps} props={{ label: 'Notes' }} />);
23
- expect(screen.getByText('Notes')).toBeInTheDocument();
24
- });
25
-
26
- it('falls back to bind.meta.label', () => {
27
- render(
28
- <TextareaWidget
29
- {...defaultProps}
30
- bind={{
31
- value: '',
32
- meta: { type: 'text', label: 'Description', required: false, readOnly: false },
33
- }}
34
- />,
35
- );
36
- expect(screen.getByText('Description')).toBeInTheDocument();
37
- });
38
-
39
- it('renders current value', () => {
40
- const { container } = render(
41
- <TextareaWidget {...defaultProps} bind={{ value: 'Hello world' }} />,
42
- );
43
- expect(container.querySelector('textarea')?.value).toBe('Hello world');
44
- });
45
-
46
- it('fires on.change when typing', () => {
47
- const onChange = vi.fn();
48
- const { container } = render(<TextareaWidget {...defaultProps} on={{ change: onChange }} />);
49
- fireEvent.change(container.querySelector('textarea')!, { target: { value: 'new text' } });
50
- expect(onChange).toHaveBeenCalledWith('new text');
51
- });
52
-
53
- it('fires on.focus and on.blur', () => {
54
- const onFocus = vi.fn();
55
- const onBlur = vi.fn();
56
- const { container } = render(
57
- <TextareaWidget {...defaultProps} on={{ focus: onFocus, blur: onBlur }} />,
58
- );
59
- fireEvent.focus(container.querySelector('textarea')!);
60
- expect(onFocus).toHaveBeenCalled();
61
- fireEvent.blur(container.querySelector('textarea')!);
62
- expect(onBlur).toHaveBeenCalled();
63
- });
64
-
65
- it('sets placeholder', () => {
66
- const { container } = render(
67
- <TextareaWidget {...defaultProps} props={{ placeholder: 'Enter notes...' }} />,
68
- );
69
- expect(container.querySelector('textarea')?.placeholder).toBe('Enter notes...');
70
- });
71
-
72
- it('sets rows from props', () => {
73
- const { container } = render(<TextareaWidget {...defaultProps} props={{ rows: 8 }} />);
74
- expect(container.querySelector('textarea')?.rows).toBe(8);
75
- });
76
-
77
- it('handles disabled state', () => {
78
- const { container } = render(<TextareaWidget {...defaultProps} props={{ disabled: true }} />);
79
- expect(container.querySelector('textarea')?.disabled).toBe(true);
80
- });
81
-
82
- it('handles readOnly from bind.meta', () => {
83
- const { container } = render(
84
- <TextareaWidget
85
- {...defaultProps}
86
- bind={{ value: '', meta: { type: 'text', label: 'X', required: false, readOnly: true } }}
87
- />,
88
- );
89
- expect(container.querySelector('textarea')?.readOnly).toBe(true);
90
- });
91
-
92
- it('handles null bind.value gracefully', () => {
93
- const { container } = render(<TextareaWidget {...defaultProps} bind={{ value: null }} />);
94
- expect(container.querySelector('textarea')?.value).toBe('');
95
- });
96
-
97
- it('calls bind.setValue on change', () => {
98
- const setValue = vi.fn();
99
- const { container } = render(
100
- <TextareaWidget {...defaultProps} bind={{ value: '', setValue }} />,
101
- );
102
- fireEvent.change(container.querySelector('textarea')!, { target: { value: 'updated' } });
103
- expect(setValue).toHaveBeenCalledWith('updated');
104
- });
105
- });