@rangka/client 0.1.1 → 0.1.3

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 (317) 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/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/main.d.ts.map +1 -1
  9. package/dist/main.js +20 -0
  10. package/dist/main.js.map +1 -1
  11. package/dist/shell/assets/index-63v1sBS3.css +1 -0
  12. package/dist/shell/assets/index-Dh7K40cQ.js +8634 -0
  13. package/dist/shell/assets/vendor-query-B2cydN5j.js +1 -0
  14. package/dist/shell/assets/vendor-radix-BJxYPxPb.js +69 -0
  15. package/dist/shell/assets/vendor-router-ET_myMt5.js +17 -0
  16. package/dist/shell/index.html +5 -2
  17. package/dist/theme.css +82 -0
  18. package/dist/widgets/components/lazy-manifest.d.ts +11 -0
  19. package/dist/widgets/components/lazy-manifest.d.ts.map +1 -0
  20. package/dist/widgets/components/lazy-manifest.js +32 -0
  21. package/dist/widgets/components/lazy-manifest.js.map +1 -0
  22. package/dist/widgets/components/register.js +12 -12
  23. package/dist/widgets/components/register.js.map +1 -1
  24. package/dist/widgets/loader.d.ts +3 -0
  25. package/dist/widgets/loader.d.ts.map +1 -0
  26. package/dist/widgets/loader.js +73 -0
  27. package/dist/widgets/loader.js.map +1 -0
  28. package/dist/widgets/renderer/LazyWidget.d.ts +8 -0
  29. package/dist/widgets/renderer/LazyWidget.d.ts.map +1 -0
  30. package/dist/widgets/renderer/LazyWidget.js +31 -0
  31. package/dist/widgets/renderer/LazyWidget.js.map +1 -0
  32. package/dist/widgets/renderer/WidgetErrorBoundary.d.ts +17 -0
  33. package/dist/widgets/renderer/WidgetErrorBoundary.d.ts.map +1 -0
  34. package/dist/widgets/renderer/WidgetErrorBoundary.js +18 -0
  35. package/dist/widgets/renderer/WidgetErrorBoundary.js.map +1 -0
  36. package/dist/widgets/renderer/WidgetRenderer.d.ts.map +1 -1
  37. package/dist/widgets/renderer/WidgetRenderer.js +8 -6
  38. package/dist/widgets/renderer/WidgetRenderer.js.map +1 -1
  39. package/package.json +7 -4
  40. package/.claude/skills/add-widget/SKILL.md +0 -101
  41. package/.turbo/turbo-build.log +0 -29
  42. package/CHANGELOG.md +0 -25
  43. package/CLAUDE.md +0 -236
  44. package/components.json +0 -25
  45. package/dist/components/ui/chart.d.ts +0 -45
  46. package/dist/components/ui/chart.d.ts.map +0 -1
  47. package/dist/components/ui/chart.js +0 -119
  48. package/dist/components/ui/chart.js.map +0 -1
  49. package/dist/shell/assets/index--35CAvcP.js +0 -8715
  50. package/dist/shell/assets/index-COLmoPYo.css +0 -1
  51. package/index.html +0 -12
  52. package/src/App.tsx +0 -44
  53. package/src/__tests__/setup.ts +0 -1
  54. package/src/api/auth.ts +0 -41
  55. package/src/api/boot.ts +0 -10
  56. package/src/api/client.ts +0 -26
  57. package/src/api/paths.ts +0 -3
  58. package/src/api/token.ts +0 -13
  59. package/src/auth/LoginForm.tsx +0 -67
  60. package/src/auth/SessionExpired.tsx +0 -24
  61. package/src/auth/SetupForm.tsx +0 -76
  62. package/src/boot/BootGate.tsx +0 -35
  63. package/src/boot/BootProvider.tsx +0 -28
  64. package/src/boot/types.ts +0 -9
  65. package/src/boot/useBoot.ts +0 -111
  66. package/src/components/Icon.tsx +0 -17
  67. package/src/components/ui/accordion.tsx +0 -82
  68. package/src/components/ui/alert-dialog.tsx +0 -180
  69. package/src/components/ui/alert.tsx +0 -76
  70. package/src/components/ui/aspect-ratio.tsx +0 -9
  71. package/src/components/ui/avatar.tsx +0 -94
  72. package/src/components/ui/badge.tsx +0 -45
  73. package/src/components/ui/breadcrumb.tsx +0 -104
  74. package/src/components/ui/button-group.tsx +0 -78
  75. package/src/components/ui/button.tsx +0 -65
  76. package/src/components/ui/calendar.tsx +0 -187
  77. package/src/components/ui/card.tsx +0 -85
  78. package/src/components/ui/carousel.tsx +0 -229
  79. package/src/components/ui/chart.tsx +0 -339
  80. package/src/components/ui/checkbox.tsx +0 -27
  81. package/src/components/ui/collapsible.tsx +0 -21
  82. package/src/components/ui/combobox.tsx +0 -275
  83. package/src/components/ui/command.tsx +0 -178
  84. package/src/components/ui/context-menu.tsx +0 -242
  85. package/src/components/ui/dialog.tsx +0 -146
  86. package/src/components/ui/direction.tsx +0 -20
  87. package/src/components/ui/drawer.tsx +0 -118
  88. package/src/components/ui/dropdown-menu.tsx +0 -247
  89. package/src/components/ui/empty.tsx +0 -94
  90. package/src/components/ui/field.tsx +0 -224
  91. package/src/components/ui/hover-card.tsx +0 -36
  92. package/src/components/ui/input-group.tsx +0 -142
  93. package/src/components/ui/input-otp.tsx +0 -86
  94. package/src/components/ui/input.tsx +0 -19
  95. package/src/components/ui/item.tsx +0 -182
  96. package/src/components/ui/kbd.tsx +0 -26
  97. package/src/components/ui/label.tsx +0 -19
  98. package/src/components/ui/menubar.tsx +0 -260
  99. package/src/components/ui/native-select.tsx +0 -55
  100. package/src/components/ui/navigation-menu.tsx +0 -160
  101. package/src/components/ui/pagination.tsx +0 -112
  102. package/src/components/ui/popover.tsx +0 -74
  103. package/src/components/ui/progress.tsx +0 -31
  104. package/src/components/ui/radio-group.tsx +0 -42
  105. package/src/components/ui/resizable.tsx +0 -42
  106. package/src/components/ui/scroll-area.tsx +0 -53
  107. package/src/components/ui/select.tsx +0 -185
  108. package/src/components/ui/separator.tsx +0 -26
  109. package/src/components/ui/sheet.tsx +0 -128
  110. package/src/components/ui/sidebar.tsx +0 -669
  111. package/src/components/ui/skeleton.tsx +0 -13
  112. package/src/components/ui/slider.tsx +0 -54
  113. package/src/components/ui/sonner.tsx +0 -43
  114. package/src/components/ui/spinner.tsx +0 -16
  115. package/src/components/ui/switch.tsx +0 -33
  116. package/src/components/ui/table.tsx +0 -87
  117. package/src/components/ui/tabs.tsx +0 -80
  118. package/src/components/ui/textarea.tsx +0 -18
  119. package/src/components/ui/toggle-group.tsx +0 -86
  120. package/src/components/ui/toggle.tsx +0 -44
  121. package/src/components/ui/tooltip.tsx +0 -53
  122. package/src/context/MetaContext.tsx +0 -22
  123. package/src/context/ModuleContext.tsx +0 -62
  124. package/src/context/PermissionsContext.tsx +0 -39
  125. package/src/context/ShellProviders.tsx +0 -33
  126. package/src/context/UserContext.tsx +0 -16
  127. package/src/data/QueryProvider.tsx +0 -7
  128. package/src/data/queryClient.ts +0 -18
  129. package/src/data/useModelMeta.ts +0 -17
  130. package/src/data/useMutation.ts +0 -60
  131. package/src/data/useRecord.ts +0 -29
  132. package/src/data/useSource.ts +0 -112
  133. package/src/hooks/use-mobile.ts +0 -19
  134. package/src/index.css +0 -260
  135. package/src/index.ts +0 -16
  136. package/src/lib/utils.ts +0 -6
  137. package/src/main.tsx +0 -17
  138. package/src/router/NotFound.tsx +0 -8
  139. package/src/router/RouterProvider.tsx +0 -7
  140. package/src/router/buildRouteTree.tsx +0 -63
  141. package/src/router/createShellRouter.ts +0 -9
  142. package/src/router/hooks.ts +0 -43
  143. package/src/shell/CommandPalette.tsx +0 -76
  144. package/src/shell/ConfirmDialog.tsx +0 -34
  145. package/src/shell/ConfirmProvider.tsx +0 -56
  146. package/src/shell/DrawerContext.tsx +0 -44
  147. package/src/shell/HeaderActions.tsx +0 -31
  148. package/src/shell/ModuleSelectorPage.tsx +0 -149
  149. package/src/shell/PageOutlet.tsx +0 -21
  150. package/src/shell/ShellContext.tsx +0 -45
  151. package/src/shell/ShellDevTools.tsx +0 -153
  152. package/src/shell/ShellLayout.tsx +0 -231
  153. package/src/shell/Toast.tsx +0 -58
  154. package/src/shell/ToastProvider.tsx +0 -60
  155. package/src/shell/app-sidebar/AppSidebar.tsx +0 -44
  156. package/src/shell/app-sidebar/ModuleSwitcher.tsx +0 -87
  157. package/src/shell/app-sidebar/NavMain.tsx +0 -64
  158. package/src/shell/app-sidebar/NavUser.tsx +0 -97
  159. package/src/shell/app-sidebar/SearchMenu.tsx +0 -22
  160. package/src/shell/app-sidebar/index.ts +0 -8
  161. package/src/shell/app-sidebar/types.ts +0 -38
  162. package/src/shell/types.ts +0 -6
  163. package/src/shell/useBreadcrumbs.ts +0 -42
  164. package/src/studio/bridge.ts +0 -125
  165. package/src/studio/index.ts +0 -3
  166. package/src/studio/overlay.ts +0 -47
  167. package/src/studio/types.ts +0 -32
  168. package/src/studio/walker.ts +0 -48
  169. package/src/vite-env.d.ts +0 -1
  170. package/src/widgets/__tests__/action-edge-cases.test.ts +0 -281
  171. package/src/widgets/__tests__/action.test.ts +0 -236
  172. package/src/widgets/__tests__/attachment-widget.test.tsx +0 -85
  173. package/src/widgets/__tests__/attachments-widget.test.tsx +0 -109
  174. package/src/widgets/__tests__/binding.test.ts +0 -76
  175. package/src/widgets/__tests__/button-widget.test.tsx +0 -145
  176. package/src/widgets/__tests__/checkbox-widget.test.tsx +0 -158
  177. package/src/widgets/__tests__/code-widget.test.tsx +0 -64
  178. package/src/widgets/__tests__/computed-widget.test.tsx +0 -62
  179. package/src/widgets/__tests__/condition-edge-cases.test.ts +0 -120
  180. package/src/widgets/__tests__/condition.test.ts +0 -221
  181. package/src/widgets/__tests__/context.test.ts +0 -99
  182. package/src/widgets/__tests__/data-widget.test.tsx +0 -204
  183. package/src/widgets/__tests__/datepicker-widget.test.tsx +0 -66
  184. package/src/widgets/__tests__/datetime-widget.test.tsx +0 -67
  185. package/src/widgets/__tests__/drawer-widget.test.tsx +0 -149
  186. package/src/widgets/__tests__/dynamic-link-widget.test.tsx +0 -52
  187. package/src/widgets/__tests__/edge-cases.test.ts +0 -232
  188. package/src/widgets/__tests__/evaluator.test.ts +0 -107
  189. package/src/widgets/__tests__/functions.test.ts +0 -147
  190. package/src/widgets/__tests__/grid-widget.test.tsx +0 -137
  191. package/src/widgets/__tests__/hooks.test.tsx +0 -249
  192. package/src/widgets/__tests__/icon-widget.test.tsx +0 -129
  193. package/src/widgets/__tests__/input-widget.test.tsx +0 -264
  194. package/src/widgets/__tests__/integration.test.ts +0 -116
  195. package/src/widgets/__tests__/json-widget.test.tsx +0 -70
  196. package/src/widgets/__tests__/link-widget.test.tsx +0 -92
  197. package/src/widgets/__tests__/many-to-many-widget.test.tsx +0 -93
  198. package/src/widgets/__tests__/modal-widget.test.tsx +0 -148
  199. package/src/widgets/__tests__/money-widget.test.tsx +0 -97
  200. package/src/widgets/__tests__/parser.test.ts +0 -171
  201. package/src/widgets/__tests__/reactive-variables.test.ts +0 -383
  202. package/src/widgets/__tests__/renderer.test.tsx +0 -300
  203. package/src/widgets/__tests__/repeat-widget.test.tsx +0 -229
  204. package/src/widgets/__tests__/select-widget.test.tsx +0 -231
  205. package/src/widgets/__tests__/sequence-widget.test.tsx +0 -58
  206. package/src/widgets/__tests__/shell-integration.test.tsx +0 -1343
  207. package/src/widgets/__tests__/split-widget.test.tsx +0 -133
  208. package/src/widgets/__tests__/state-edge-cases.test.ts +0 -118
  209. package/src/widgets/__tests__/state.test.ts +0 -106
  210. package/src/widgets/__tests__/table-data-binding.test.tsx +0 -482
  211. package/src/widgets/__tests__/table-filter-popover.test.tsx +0 -486
  212. package/src/widgets/__tests__/table-search.test.tsx +0 -305
  213. package/src/widgets/__tests__/table-widget.test.tsx +0 -509
  214. package/src/widgets/__tests__/textarea-widget.test.tsx +0 -105
  215. package/src/widgets/__tests__/tracker-validator-edge-cases.test.ts +0 -242
  216. package/src/widgets/__tests__/tracker.test.ts +0 -133
  217. package/src/widgets/__tests__/tree-widget.test.tsx +0 -97
  218. package/src/widgets/__tests__/use-model-source.test.ts +0 -67
  219. package/src/widgets/__tests__/validator.test.ts +0 -208
  220. package/src/widgets/action/dispatcher.ts +0 -334
  221. package/src/widgets/action/index.ts +0 -2
  222. package/src/widgets/binding/index.ts +0 -2
  223. package/src/widgets/binding/resolver.ts +0 -61
  224. package/src/widgets/components/AttachmentWidget.tsx +0 -111
  225. package/src/widgets/components/AttachmentsWidget.tsx +0 -121
  226. package/src/widgets/components/BadgeWidget.tsx +0 -35
  227. package/src/widgets/components/ButtonWidget.tsx +0 -43
  228. package/src/widgets/components/CardWidget.tsx +0 -68
  229. package/src/widgets/components/CheckboxWidget.tsx +0 -39
  230. package/src/widgets/components/CodeWidget.tsx +0 -44
  231. package/src/widgets/components/ColumnWidget.tsx +0 -22
  232. package/src/widgets/components/ComputedWidget.tsx +0 -49
  233. package/src/widgets/components/DataWidget.tsx +0 -189
  234. package/src/widgets/components/DatePickerWidget.tsx +0 -73
  235. package/src/widgets/components/DatetimeWidget.tsx +0 -160
  236. package/src/widgets/components/DividerWidget.tsx +0 -37
  237. package/src/widgets/components/DrawerWidget.tsx +0 -52
  238. package/src/widgets/components/DynamicLinkWidget.tsx +0 -130
  239. package/src/widgets/components/GridWidget.tsx +0 -134
  240. package/src/widgets/components/GroupWidget.tsx +0 -111
  241. package/src/widgets/components/IconWidget.tsx +0 -29
  242. package/src/widgets/components/ImageWidget.tsx +0 -28
  243. package/src/widgets/components/InputWidget.tsx +0 -70
  244. package/src/widgets/components/JsonWidget.tsx +0 -78
  245. package/src/widgets/components/LinkWidget.tsx +0 -99
  246. package/src/widgets/components/ManyToManyWidget.tsx +0 -125
  247. package/src/widgets/components/ModalWidget.tsx +0 -52
  248. package/src/widgets/components/MoneyWidget.tsx +0 -80
  249. package/src/widgets/components/RepeatWidget.tsx +0 -66
  250. package/src/widgets/components/ScrollAreaWidget.tsx +0 -40
  251. package/src/widgets/components/SectionWidget.tsx +0 -78
  252. package/src/widgets/components/SelectWidget.tsx +0 -63
  253. package/src/widgets/components/SequenceWidget.tsx +0 -32
  254. package/src/widgets/components/SpacerWidget.tsx +0 -29
  255. package/src/widgets/components/SplitWidget.tsx +0 -60
  256. package/src/widgets/components/StackWidget.tsx +0 -44
  257. package/src/widgets/components/TableWidget.tsx +0 -366
  258. package/src/widgets/components/TextWidget.tsx +0 -44
  259. package/src/widgets/components/TextareaWidget.tsx +0 -49
  260. package/src/widgets/components/TreeWidget.tsx +0 -109
  261. package/src/widgets/components/index.ts +0 -30
  262. package/src/widgets/components/register.ts +0 -93
  263. package/src/widgets/components/table/CellRenderers.tsx +0 -83
  264. package/src/widgets/components/table/TablePagination.tsx +0 -45
  265. package/src/widgets/components/table/TableToolbar.tsx +0 -285
  266. package/src/widgets/components/table/filter-operators.ts +0 -134
  267. package/src/widgets/components/table/index.ts +0 -11
  268. package/src/widgets/condition/evaluator.ts +0 -57
  269. package/src/widgets/condition/index.ts +0 -1
  270. package/src/widgets/context/builder.ts +0 -99
  271. package/src/widgets/context/index.ts +0 -8
  272. package/src/widgets/context/types.ts +0 -37
  273. package/src/widgets/data/index.ts +0 -5
  274. package/src/widgets/data/useModelQuery.ts +0 -116
  275. package/src/widgets/data/useModelRecord.ts +0 -37
  276. package/src/widgets/expression/evaluator.ts +0 -100
  277. package/src/widgets/expression/functions.ts +0 -131
  278. package/src/widgets/expression/index.ts +0 -13
  279. package/src/widgets/expression/parser.ts +0 -229
  280. package/src/widgets/expression/types.ts +0 -45
  281. package/src/widgets/form/FormContext.ts +0 -29
  282. package/src/widgets/form/FormProvider.tsx +0 -84
  283. package/src/widgets/form/FormWidget.tsx +0 -42
  284. package/src/widgets/form/index.ts +0 -4
  285. package/src/widgets/form/useFormState.ts +0 -127
  286. package/src/widgets/form/useFormSubmit.ts +0 -90
  287. package/src/widgets/form/useFormValidation.ts +0 -62
  288. package/src/widgets/hooks/index.ts +0 -8
  289. package/src/widgets/hooks/useAction.ts +0 -83
  290. package/src/widgets/hooks/useBind.ts +0 -34
  291. package/src/widgets/hooks/useCondition.ts +0 -21
  292. package/src/widgets/hooks/useDataQuery.ts +0 -48
  293. package/src/widgets/hooks/useExpression.ts +0 -14
  294. package/src/widgets/hooks/usePageState.ts +0 -21
  295. package/src/widgets/hooks/useSurfaceContext.ts +0 -11
  296. package/src/widgets/hooks/useWidgetContext.ts +0 -14
  297. package/src/widgets/index.ts +0 -80
  298. package/src/widgets/lib/layout-props.ts +0 -135
  299. package/src/widgets/reactivity/index.ts +0 -11
  300. package/src/widgets/reactivity/tracker.ts +0 -139
  301. package/src/widgets/reactivity/variables.ts +0 -213
  302. package/src/widgets/registry.ts +0 -41
  303. package/src/widgets/renderer/SlotRenderer.tsx +0 -47
  304. package/src/widgets/renderer/WidgetRenderer.tsx +0 -191
  305. package/src/widgets/renderer/index.ts +0 -4
  306. package/src/widgets/shell/WidgetSlotRenderer.tsx +0 -73
  307. package/src/widgets/shell/index.ts +0 -4
  308. package/src/widgets/shell/useActionHandlers.ts +0 -170
  309. package/src/widgets/state/index.ts +0 -2
  310. package/src/widgets/state/store.ts +0 -96
  311. package/src/widgets/types.ts +0 -28
  312. package/src/widgets/validation/index.ts +0 -2
  313. package/src/widgets/validation/validator.ts +0 -140
  314. package/tsconfig.json +0 -27
  315. package/tsconfig.tsbuildinfo +0 -1
  316. package/vite.config.ts +0 -21
  317. package/vitest.config.ts +0 -16
@@ -1,158 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import React from 'react';
3
- import { render, screen, fireEvent } from '@testing-library/react';
4
- import { CheckboxWidget } from '../components/CheckboxWidget.js';
5
-
6
- describe('CheckboxWidget', () => {
7
- it('renders label text from props', () => {
8
- render(
9
- <CheckboxWidget
10
- props={{ label: 'Accept terms' }}
11
- bind={{ value: false }}
12
- on={{}}
13
- context={{ record: {}, model: '', mode: 'view' }}
14
- />,
15
- );
16
-
17
- expect(screen.getByText('Accept terms')).toBeInTheDocument();
18
- });
19
-
20
- it('renders label from bind.meta.label as fallback', () => {
21
- render(
22
- <CheckboxWidget
23
- props={{}}
24
- bind={{
25
- value: false,
26
- meta: { type: 'boolean', label: 'Meta Label', required: false, readOnly: false },
27
- }}
28
- on={{}}
29
- context={{ record: {}, model: '', mode: 'view' }}
30
- />,
31
- );
32
-
33
- expect(screen.getByText('Meta Label')).toBeInTheDocument();
34
- });
35
-
36
- it('does not render label when label is empty', () => {
37
- const { container } = render(
38
- <CheckboxWidget
39
- props={{ label: '' }}
40
- bind={{ value: false }}
41
- on={{}}
42
- context={{ record: {}, model: '', mode: 'view' }}
43
- />,
44
- );
45
-
46
- expect(container.querySelector('span')).toBeNull();
47
- });
48
-
49
- it('renders checked state when bind.value is true', () => {
50
- render(
51
- <CheckboxWidget
52
- props={{}}
53
- bind={{ value: true }}
54
- on={{}}
55
- context={{ record: {}, model: '', mode: 'view' }}
56
- />,
57
- );
58
-
59
- const checkbox = screen.getByRole('checkbox');
60
- expect(checkbox).toHaveAttribute('aria-checked', 'true');
61
- });
62
-
63
- it('renders unchecked state when bind.value is false', () => {
64
- render(
65
- <CheckboxWidget
66
- props={{}}
67
- bind={{ value: false }}
68
- on={{}}
69
- context={{ record: {}, model: '', mode: 'view' }}
70
- />,
71
- );
72
-
73
- const checkbox = screen.getByRole('checkbox');
74
- expect(checkbox).toHaveAttribute('aria-checked', 'false');
75
- });
76
-
77
- it('defaults to unchecked when bind.value is undefined', () => {
78
- render(
79
- <CheckboxWidget
80
- props={{}}
81
- bind={{ value: undefined }}
82
- on={{}}
83
- context={{ record: {}, model: '', mode: 'view' }}
84
- />,
85
- );
86
-
87
- const checkbox = screen.getByRole('checkbox');
88
- expect(checkbox).toHaveAttribute('aria-checked', 'false');
89
- });
90
-
91
- it('disables checkbox when props.disabled is true', () => {
92
- render(
93
- <CheckboxWidget
94
- props={{ disabled: true }}
95
- bind={{ value: false }}
96
- on={{}}
97
- context={{ record: {}, model: '', mode: 'view' }}
98
- />,
99
- );
100
-
101
- const checkbox = screen.getByRole('checkbox');
102
- expect(checkbox).toBeDisabled();
103
- expect(checkbox).toHaveAttribute('data-disabled', '');
104
- });
105
-
106
- it('disables checkbox from bind.meta.readOnly as fallback', () => {
107
- render(
108
- <CheckboxWidget
109
- props={{}}
110
- bind={{
111
- value: false,
112
- meta: { type: 'boolean', label: 'Read Only', required: false, readOnly: true },
113
- }}
114
- on={{}}
115
- context={{ record: {}, model: '', mode: 'view' }}
116
- />,
117
- );
118
-
119
- const checkbox = screen.getByRole('checkbox');
120
- expect(checkbox).toBeDisabled();
121
- expect(checkbox).toHaveAttribute('data-disabled', '');
122
- });
123
-
124
- it('calls bind.setValue and on.change when clicked', () => {
125
- const setValue = vi.fn();
126
- const onChange = vi.fn();
127
-
128
- render(
129
- <CheckboxWidget
130
- props={{}}
131
- bind={{ value: false, setValue }}
132
- on={{ change: onChange }}
133
- context={{ record: {}, model: '', mode: 'edit' }}
134
- />,
135
- );
136
-
137
- const checkbox = screen.getByRole('checkbox');
138
- fireEvent.click(checkbox);
139
-
140
- expect(setValue).toHaveBeenCalledWith(true);
141
- expect(onChange).toHaveBeenCalledWith(true);
142
- });
143
-
144
- it('has correct widgetMeta', () => {
145
- expect(CheckboxWidget.widgetMeta).toEqual({
146
- name: 'checkbox',
147
- label: 'Checkbox',
148
- category: 'input',
149
- schema: {
150
- label: { type: 'string' },
151
- disabled: { type: 'boolean', default: false },
152
- },
153
- binding: 'field',
154
- triggers: ['change'],
155
- container: false,
156
- });
157
- });
158
- });
@@ -1,64 +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 { CodeWidget } from '../components/CodeWidget.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('CodeWidget', () => {
16
- it('renders a textarea', () => {
17
- const { container } = render(<CodeWidget {...defaultProps} />);
18
- expect(container.querySelector('textarea')).toBeInTheDocument();
19
- });
20
-
21
- it('has monospace font', () => {
22
- const { container } = render(<CodeWidget {...defaultProps} />);
23
- expect(container.querySelector('textarea')?.className).toContain('font-mono');
24
- });
25
-
26
- it('displays label from props', () => {
27
- render(<CodeWidget {...defaultProps} props={{ label: 'Expression' }} />);
28
- expect(screen.getByText('Expression')).toBeInTheDocument();
29
- });
30
-
31
- it('renders current value', () => {
32
- const { container } = render(
33
- <CodeWidget {...defaultProps} bind={{ value: 'return x + 1;' }} />,
34
- );
35
- expect(container.querySelector('textarea')?.value).toBe('return x + 1;');
36
- });
37
-
38
- it('fires on.change when typing', () => {
39
- const onChange = vi.fn();
40
- const { container } = render(<CodeWidget {...defaultProps} on={{ change: onChange }} />);
41
- fireEvent.change(container.querySelector('textarea')!, { target: { value: 'new code' } });
42
- expect(onChange).toHaveBeenCalledWith('new code');
43
- });
44
-
45
- it('sets rows from props', () => {
46
- const { container } = render(<CodeWidget {...defaultProps} props={{ rows: 10 }} />);
47
- expect(container.querySelector('textarea')?.rows).toBe(10);
48
- });
49
-
50
- it('handles disabled state', () => {
51
- const { container } = render(<CodeWidget {...defaultProps} props={{ disabled: true }} />);
52
- expect(container.querySelector('textarea')?.disabled).toBe(true);
53
- });
54
-
55
- it('handles null bind.value gracefully', () => {
56
- const { container } = render(<CodeWidget {...defaultProps} bind={{ value: null }} />);
57
- expect(container.querySelector('textarea')?.value).toBe('');
58
- });
59
-
60
- it('has spellCheck disabled', () => {
61
- const { container } = render(<CodeWidget {...defaultProps} />);
62
- expect(container.querySelector('textarea')?.getAttribute('spellcheck')).toBe('false');
63
- });
64
- });
@@ -1,62 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import React from 'react';
3
- import { render, screen, cleanup } from '@testing-library/react';
4
- import { ComputedWidget } from '../components/ComputedWidget.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('ComputedWidget', () => {
16
- it('renders dash for null value', () => {
17
- render(<ComputedWidget {...defaultProps} />);
18
- expect(screen.getByTestId('computed-value').textContent).toBe('—');
19
- });
20
-
21
- it('renders string value', () => {
22
- render(<ComputedWidget {...defaultProps} bind={{ value: 'hello' }} />);
23
- expect(screen.getByTestId('computed-value').textContent).toBe('hello');
24
- });
25
-
26
- it('renders number with locale formatting', () => {
27
- render(
28
- <ComputedWidget {...defaultProps} props={{ format: 'number' }} bind={{ value: 1234567 }} />,
29
- );
30
- expect(screen.getByTestId('computed-value').textContent).toContain('1,234,567');
31
- });
32
-
33
- it('renders currency with 2 decimals', () => {
34
- render(
35
- <ComputedWidget {...defaultProps} props={{ format: 'currency' }} bind={{ value: 99.5 }} />,
36
- );
37
- expect(screen.getByTestId('computed-value').textContent).toBe('99.50');
38
- });
39
-
40
- it('displays label from props', () => {
41
- render(<ComputedWidget {...defaultProps} props={{ label: 'Total' }} />);
42
- expect(screen.getByText('Total')).toBeInTheDocument();
43
- });
44
-
45
- it('falls back to bind.meta.label', () => {
46
- render(
47
- <ComputedWidget
48
- {...defaultProps}
49
- bind={{
50
- value: 0,
51
- meta: { type: 'computed', label: 'Outstanding', required: false, readOnly: true },
52
- }}
53
- />,
54
- );
55
- expect(screen.getByText('Outstanding')).toBeInTheDocument();
56
- });
57
-
58
- it('handles undefined value gracefully', () => {
59
- render(<ComputedWidget {...defaultProps} bind={{ value: undefined }} />);
60
- expect(screen.getByTestId('computed-value').textContent).toBe('—');
61
- });
62
- });
@@ -1,120 +0,0 @@
1
- import { describe, test, expect } from 'vitest';
2
- import { evaluateCondition, evaluateConditions } from '../condition/evaluator.js';
3
-
4
- describe('Condition Edge Cases', () => {
5
- test('eq with null value matches null field', () => {
6
- expect(evaluateCondition({ field: 'x', operator: 'eq', value: null }, { x: null })).toBe(true);
7
- });
8
-
9
- test('eq with undefined field matches null (loose)', () => {
10
- expect(evaluateCondition({ field: 'x', operator: 'eq', value: null }, {})).toBe(true);
11
- });
12
-
13
- test('neq null vs undefined is false (loose equality)', () => {
14
- expect(evaluateCondition({ field: 'x', operator: 'neq', value: null }, {})).toBe(false);
15
- });
16
-
17
- test('gt with string numbers coerces', () => {
18
- expect(evaluateCondition({ field: 'x', operator: 'gt', value: '5' }, { x: '10' })).toBe(true);
19
- });
20
-
21
- test('lt with null field (NaN comparison)', () => {
22
- expect(evaluateCondition({ field: 'x', operator: 'lt', value: 5 }, { x: null })).toBe(true);
23
- });
24
-
25
- test('in with empty array always false', () => {
26
- expect(evaluateCondition({ field: 'x', operator: 'in', value: [] }, { x: 'anything' })).toBe(
27
- false,
28
- );
29
- });
30
-
31
- test('notIn with empty array always true', () => {
32
- expect(evaluateCondition({ field: 'x', operator: 'notIn', value: [] }, { x: 'anything' })).toBe(
33
- true,
34
- );
35
- });
36
-
37
- test('empty with 0 is not empty', () => {
38
- expect(evaluateCondition({ field: 'x', operator: 'empty' }, { x: 0 })).toBe(false);
39
- });
40
-
41
- test('empty with false is not empty', () => {
42
- expect(evaluateCondition({ field: 'x', operator: 'empty' }, { x: false })).toBe(false);
43
- });
44
-
45
- test('notEmpty with 0 is true', () => {
46
- expect(evaluateCondition({ field: 'x', operator: 'notEmpty' }, { x: 0 })).toBe(true);
47
- });
48
-
49
- test('deeply nested field resolution', () => {
50
- expect(
51
- evaluateCondition(
52
- { field: '$state.wizard.currentStep', operator: 'eq', value: 3 },
53
- { $state: { wizard: { currentStep: 3 } } },
54
- ),
55
- ).toBe(true);
56
- });
57
-
58
- test('multiple conditions with all passing', () => {
59
- expect(
60
- evaluateConditions(
61
- [
62
- { field: 'a', operator: 'gt', value: 0 },
63
- { field: 'b', operator: 'notEmpty' },
64
- { field: 'c', operator: 'in', value: ['x', 'y'] },
65
- ],
66
- { a: 5, b: 'hello', c: 'x' },
67
- ),
68
- ).toBe(true);
69
- });
70
-
71
- test('multiple conditions short-circuits on first false', () => {
72
- expect(
73
- evaluateConditions(
74
- [
75
- { field: 'a', operator: 'eq', value: 'wrong' },
76
- { field: 'b', operator: 'notEmpty' },
77
- ],
78
- { a: 'right', b: 'hello' },
79
- ),
80
- ).toBe(false);
81
- });
82
-
83
- test('gte with equal values', () => {
84
- expect(evaluateCondition({ field: 'x', operator: 'gte', value: 10 }, { x: 10 })).toBe(true);
85
- });
86
-
87
- test('lte with equal values', () => {
88
- expect(evaluateCondition({ field: 'x', operator: 'lte', value: 10 }, { x: 10 })).toBe(true);
89
- });
90
-
91
- test('in operator with numeric values', () => {
92
- expect(evaluateCondition({ field: 'x', operator: 'in', value: [1, 2, 3] }, { x: 2 })).toBe(
93
- true,
94
- );
95
- });
96
-
97
- test('notIn operator with numeric values', () => {
98
- expect(evaluateCondition({ field: 'x', operator: 'notIn', value: [1, 2, 3] }, { x: 5 })).toBe(
99
- true,
100
- );
101
- });
102
-
103
- test('expression value resolves against context', () => {
104
- expect(
105
- evaluateCondition(
106
- { field: 'total', operator: 'gte', value: '{{min_total}}' },
107
- { total: 100, min_total: 50 },
108
- ),
109
- ).toBe(true);
110
- });
111
-
112
- test('expression value with function call', () => {
113
- expect(
114
- evaluateCondition(
115
- { field: 'count', operator: 'gt', value: '{{count(items)}}' },
116
- { count: 5, items: [1, 2, 3] },
117
- ),
118
- ).toBe(true);
119
- });
120
- });
@@ -1,221 +0,0 @@
1
- import { describe, test, expect } from 'vitest';
2
- import { evaluateCondition, evaluateConditions } from '../condition/evaluator.js';
3
-
4
- describe('Condition Evaluator', () => {
5
- describe('eq operator', () => {
6
- test('matches equal values', () => {
7
- expect(
8
- evaluateCondition({ field: 'status', operator: 'eq', value: 'draft' }, { status: 'draft' }),
9
- ).toBe(true);
10
- });
11
-
12
- test('fails on unequal values', () => {
13
- expect(
14
- evaluateCondition(
15
- { field: 'status', operator: 'eq', value: 'draft' },
16
- { status: 'submitted' },
17
- ),
18
- ).toBe(false);
19
- });
20
-
21
- test('loose equality (number vs string)', () => {
22
- expect(evaluateCondition({ field: 'count', operator: 'eq', value: '5' }, { count: 5 })).toBe(
23
- true,
24
- );
25
- });
26
- });
27
-
28
- describe('neq operator', () => {
29
- test('passes when not equal', () => {
30
- expect(
31
- evaluateCondition(
32
- { field: 'status', operator: 'neq', value: 'draft' },
33
- { status: 'submitted' },
34
- ),
35
- ).toBe(true);
36
- });
37
-
38
- test('fails when equal', () => {
39
- expect(
40
- evaluateCondition(
41
- { field: 'status', operator: 'neq', value: 'draft' },
42
- { status: 'draft' },
43
- ),
44
- ).toBe(false);
45
- });
46
- });
47
-
48
- describe('in operator', () => {
49
- test('passes when value in array', () => {
50
- expect(
51
- evaluateCondition(
52
- { field: 'status', operator: 'in', value: ['draft', 'pending'] },
53
- { status: 'draft' },
54
- ),
55
- ).toBe(true);
56
- });
57
-
58
- test('fails when value not in array', () => {
59
- expect(
60
- evaluateCondition(
61
- { field: 'status', operator: 'in', value: ['draft', 'pending'] },
62
- { status: 'submitted' },
63
- ),
64
- ).toBe(false);
65
- });
66
- });
67
-
68
- describe('notIn operator', () => {
69
- test('passes when value not in array', () => {
70
- expect(
71
- evaluateCondition(
72
- { field: 'status', operator: 'notIn', value: ['draft', 'pending'] },
73
- { status: 'submitted' },
74
- ),
75
- ).toBe(true);
76
- });
77
-
78
- test('fails when value in array', () => {
79
- expect(
80
- evaluateCondition(
81
- { field: 'status', operator: 'notIn', value: ['draft', 'pending'] },
82
- { status: 'draft' },
83
- ),
84
- ).toBe(false);
85
- });
86
- });
87
-
88
- describe('empty operator', () => {
89
- test('passes for null', () => {
90
- expect(evaluateCondition({ field: 'name', operator: 'empty' }, { name: null })).toBe(true);
91
- });
92
-
93
- test('passes for undefined', () => {
94
- expect(evaluateCondition({ field: 'name', operator: 'empty' }, {})).toBe(true);
95
- });
96
-
97
- test('passes for empty string', () => {
98
- expect(evaluateCondition({ field: 'name', operator: 'empty' }, { name: '' })).toBe(true);
99
- });
100
-
101
- test('fails for non-empty value', () => {
102
- expect(evaluateCondition({ field: 'name', operator: 'empty' }, { name: 'test' })).toBe(false);
103
- });
104
- });
105
-
106
- describe('notEmpty operator', () => {
107
- test('passes for non-empty value', () => {
108
- expect(evaluateCondition({ field: 'name', operator: 'notEmpty' }, { name: 'test' })).toBe(
109
- true,
110
- );
111
- });
112
-
113
- test('fails for null', () => {
114
- expect(evaluateCondition({ field: 'name', operator: 'notEmpty' }, { name: null })).toBe(
115
- false,
116
- );
117
- });
118
-
119
- test('fails for empty string', () => {
120
- expect(evaluateCondition({ field: 'name', operator: 'notEmpty' }, { name: '' })).toBe(false);
121
- });
122
- });
123
-
124
- describe('gt/lt/gte/lte operators', () => {
125
- test('gt passes', () => {
126
- expect(
127
- evaluateCondition({ field: 'total', operator: 'gt', value: 100 }, { total: 150 }),
128
- ).toBe(true);
129
- });
130
-
131
- test('gt fails', () => {
132
- expect(evaluateCondition({ field: 'total', operator: 'gt', value: 100 }, { total: 50 })).toBe(
133
- false,
134
- );
135
- });
136
-
137
- test('lt passes', () => {
138
- expect(evaluateCondition({ field: 'total', operator: 'lt', value: 100 }, { total: 50 })).toBe(
139
- true,
140
- );
141
- });
142
-
143
- test('gte passes on equal', () => {
144
- expect(
145
- evaluateCondition({ field: 'total', operator: 'gte', value: 100 }, { total: 100 }),
146
- ).toBe(true);
147
- });
148
-
149
- test('lte passes on equal', () => {
150
- expect(
151
- evaluateCondition({ field: 'total', operator: 'lte', value: 100 }, { total: 100 }),
152
- ).toBe(true);
153
- });
154
- });
155
-
156
- describe('scope references', () => {
157
- test('resolves $state field', () => {
158
- expect(
159
- evaluateCondition(
160
- { field: '$state.step', operator: 'eq', value: 2 },
161
- { $state: { step: 2 } },
162
- ),
163
- ).toBe(true);
164
- });
165
-
166
- test('resolves $parent field', () => {
167
- expect(
168
- evaluateCondition(
169
- { field: '$parent.status', operator: 'eq', value: 'active' },
170
- { $parent: { status: 'active' } },
171
- ),
172
- ).toBe(true);
173
- });
174
- });
175
-
176
- describe('expression values', () => {
177
- test('resolves expression in value field', () => {
178
- expect(
179
- evaluateCondition(
180
- { field: 'total', operator: 'gt', value: '{{$state.threshold}}' },
181
- { total: 150, $state: { threshold: 100 } },
182
- ),
183
- ).toBe(true);
184
- });
185
- });
186
-
187
- describe('evaluateConditions (AND logic)', () => {
188
- test('all conditions pass', () => {
189
- expect(
190
- evaluateConditions(
191
- [
192
- { field: 'status', operator: 'eq', value: 'draft' },
193
- { field: 'total', operator: 'gt', value: 0 },
194
- ],
195
- { status: 'draft', total: 100 },
196
- ),
197
- ).toBe(true);
198
- });
199
-
200
- test('one condition fails', () => {
201
- expect(
202
- evaluateConditions(
203
- [
204
- { field: 'status', operator: 'eq', value: 'draft' },
205
- { field: 'total', operator: 'gt', value: 0 },
206
- ],
207
- { status: 'draft', total: 0 },
208
- ),
209
- ).toBe(false);
210
- });
211
-
212
- test('single condition (not array)', () => {
213
- expect(
214
- evaluateConditions(
215
- { field: 'status', operator: 'eq', value: 'draft' },
216
- { status: 'draft' },
217
- ),
218
- ).toBe(true);
219
- });
220
- });
221
- });