@simplysm/solid 13.0.76 → 13.0.77

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 (291) hide show
  1. package/README.md +9 -10
  2. package/dist/components/data/kanban/Kanban.d.ts +31 -38
  3. package/dist/components/data/kanban/Kanban.d.ts.map +1 -1
  4. package/dist/components/data/kanban/Kanban.js.map +1 -1
  5. package/dist/components/data/list/List.d.ts +7 -28
  6. package/dist/components/data/list/List.d.ts.map +1 -1
  7. package/dist/components/data/list/List.js +3 -2
  8. package/dist/components/data/list/List.js.map +2 -2
  9. package/dist/components/data/sheet/DataSheet.d.ts +3 -8
  10. package/dist/components/data/sheet/DataSheet.d.ts.map +1 -1
  11. package/dist/components/data/sheet/DataSheet.js +305 -418
  12. package/dist/components/data/sheet/DataSheet.js.map +2 -2
  13. package/dist/components/data/sheet/{types.d.ts → DataSheet.types.d.ts} +9 -3
  14. package/dist/components/data/sheet/DataSheet.types.d.ts.map +1 -0
  15. package/dist/components/data/sheet/DataSheet.types.js +1 -0
  16. package/dist/components/data/sheet/{sheetUtils.d.ts → DataSheet.utils.d.ts} +2 -2
  17. package/dist/components/data/sheet/DataSheet.utils.d.ts.map +1 -0
  18. package/dist/components/data/sheet/{sheetUtils.js → DataSheet.utils.js} +3 -3
  19. package/dist/components/data/sheet/DataSheet.utils.js.map +6 -0
  20. package/dist/components/data/sheet/DataSheetColumn.d.ts +1 -1
  21. package/dist/components/data/sheet/DataSheetColumn.d.ts.map +1 -1
  22. package/dist/components/data/sheet/DataSheetConfigDialog.d.ts +1 -1
  23. package/dist/components/data/sheet/DataSheetConfigDialog.d.ts.map +1 -1
  24. package/dist/components/data/sheet/hooks/useDataSheetExpansion.d.ts +1 -1
  25. package/dist/components/data/sheet/hooks/useDataSheetExpansion.d.ts.map +1 -1
  26. package/dist/components/data/sheet/hooks/useDataSheetExpansion.js +1 -1
  27. package/dist/components/data/sheet/hooks/useDataSheetFixedColumns.d.ts +2 -2
  28. package/dist/components/data/sheet/hooks/useDataSheetFixedColumns.d.ts.map +1 -1
  29. package/dist/components/data/sheet/hooks/useDataSheetHeaderCell.d.ts +27 -0
  30. package/dist/components/data/sheet/hooks/useDataSheetHeaderCell.d.ts.map +1 -0
  31. package/dist/components/data/sheet/hooks/useDataSheetHeaderCell.js +173 -0
  32. package/dist/components/data/sheet/hooks/useDataSheetHeaderCell.js.map +6 -0
  33. package/dist/components/data/sheet/hooks/useDataSheetReorder.d.ts +1 -2
  34. package/dist/components/data/sheet/hooks/useDataSheetReorder.d.ts.map +1 -1
  35. package/dist/components/data/sheet/hooks/useDataSheetReorder.js.map +1 -1
  36. package/dist/components/data/sheet/hooks/useDataSheetSelection.d.ts +2 -2
  37. package/dist/components/data/sheet/hooks/useDataSheetSelection.d.ts.map +1 -1
  38. package/dist/components/data/sheet/hooks/useDataSheetSelection.js +2 -2
  39. package/dist/components/data/sheet/hooks/useDataSheetSelection.js.map +1 -1
  40. package/dist/components/data/sheet/hooks/useDataSheetSorting.d.ts +1 -1
  41. package/dist/components/data/sheet/hooks/useDataSheetSorting.d.ts.map +1 -1
  42. package/dist/components/data/sheet/hooks/useDataSheetSorting.js +1 -1
  43. package/dist/components/disclosure/Dialog.d.ts +6 -10
  44. package/dist/components/disclosure/Dialog.d.ts.map +1 -1
  45. package/dist/components/disclosure/Dialog.js +11 -14
  46. package/dist/components/disclosure/Dialog.js.map +2 -2
  47. package/dist/components/disclosure/Dropdown.d.ts +8 -12
  48. package/dist/components/disclosure/Dropdown.d.ts.map +1 -1
  49. package/dist/components/disclosure/Dropdown.js.map +1 -1
  50. package/dist/components/disclosure/Tabs.d.ts +3 -5
  51. package/dist/components/disclosure/Tabs.d.ts.map +1 -1
  52. package/dist/components/disclosure/Tabs.js.map +1 -1
  53. package/dist/components/display/Barcode.d.ts +1 -1
  54. package/dist/components/display/Barcode.d.ts.map +1 -1
  55. package/dist/components/display/Barcode.js +2 -1
  56. package/dist/components/display/Barcode.js.map +2 -2
  57. package/dist/components/display/Barcode.types.d.ts +2 -0
  58. package/dist/components/display/Barcode.types.d.ts.map +1 -0
  59. package/dist/components/display/Barcode.types.js +1 -0
  60. package/dist/components/display/Echarts.d.ts +2 -2
  61. package/dist/components/display/Echarts.d.ts.map +1 -1
  62. package/dist/components/display/Echarts.js +7 -8
  63. package/dist/components/display/Echarts.js.map +2 -2
  64. package/dist/components/features/address/AddressSearch.d.ts.map +1 -1
  65. package/dist/components/features/address/AddressSearch.js +4 -1
  66. package/dist/components/features/address/AddressSearch.js.map +2 -2
  67. package/dist/components/features/crud-detail/CrudDetail.d.ts +12 -13
  68. package/dist/components/features/crud-detail/CrudDetail.d.ts.map +1 -1
  69. package/dist/components/features/crud-detail/CrudDetail.js +18 -15
  70. package/dist/components/features/crud-detail/CrudDetail.js.map +2 -2
  71. package/dist/components/features/crud-detail/{types.d.ts → CrudDetail.types.d.ts} +1 -1
  72. package/dist/components/features/crud-detail/CrudDetail.types.d.ts.map +1 -0
  73. package/dist/components/features/crud-detail/CrudDetail.types.js +1 -0
  74. package/dist/components/features/crud-sheet/CrudSheet.d.ts +3 -13
  75. package/dist/components/features/crud-sheet/CrudSheet.d.ts.map +1 -1
  76. package/dist/components/features/crud-sheet/CrudSheet.js +62 -73
  77. package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
  78. package/dist/components/features/crud-sheet/{types.d.ts → CrudSheet.types.d.ts} +10 -10
  79. package/dist/components/features/crud-sheet/CrudSheet.types.d.ts.map +1 -0
  80. package/dist/components/features/crud-sheet/CrudSheet.types.js +1 -0
  81. package/dist/components/features/crud-sheet/CrudSheet.types.js.map +6 -0
  82. package/dist/components/features/crud-sheet/CrudSheetColumn.d.ts +1 -1
  83. package/dist/components/features/crud-sheet/CrudSheetColumn.d.ts.map +1 -1
  84. package/dist/components/features/crud-sheet/CrudSheetTools.d.ts +1 -1
  85. package/dist/components/features/crud-sheet/CrudSheetTools.d.ts.map +1 -1
  86. package/dist/components/features/data-select-button/DataSelectButton.d.ts +2 -2
  87. package/dist/components/features/data-select-button/DataSelectButton.d.ts.map +1 -1
  88. package/dist/components/features/data-select-button/DataSelectButton.js +3 -3
  89. package/dist/components/features/data-select-button/DataSelectButton.js.map +2 -2
  90. package/dist/components/features/shared-data/SharedDataSelect.d.ts +7 -11
  91. package/dist/components/features/shared-data/SharedDataSelect.d.ts.map +1 -1
  92. package/dist/components/features/shared-data/SharedDataSelect.js +5 -4
  93. package/dist/components/features/shared-data/SharedDataSelect.js.map +2 -2
  94. package/dist/components/features/shared-data/SharedDataSelectButton.d.ts.map +1 -1
  95. package/dist/components/features/shared-data/SharedDataSelectButton.js.map +1 -1
  96. package/dist/components/features/shared-data/SharedDataSelectList.d.ts +8 -15
  97. package/dist/components/features/shared-data/SharedDataSelectList.d.ts.map +1 -1
  98. package/dist/components/features/shared-data/SharedDataSelectList.js +5 -3
  99. package/dist/components/features/shared-data/SharedDataSelectList.js.map +2 -2
  100. package/dist/components/feedback/notification/NotificationProvider.d.ts.map +1 -1
  101. package/dist/components/feedback/notification/NotificationProvider.js +9 -1
  102. package/dist/components/feedback/notification/NotificationProvider.js.map +2 -2
  103. package/dist/components/feedback/print/Print.d.ts +4 -6
  104. package/dist/components/feedback/print/Print.d.ts.map +1 -1
  105. package/dist/components/feedback/print/Print.js +3 -2
  106. package/dist/components/feedback/print/Print.js.map +2 -2
  107. package/dist/components/feedback/print/PrintProvider.d.ts.map +1 -1
  108. package/dist/components/feedback/print/PrintProvider.js +1 -0
  109. package/dist/components/feedback/print/PrintProvider.js.map +1 -1
  110. package/dist/components/form-control/Invalid.d.ts +1 -1
  111. package/dist/components/form-control/Invalid.d.ts.map +1 -1
  112. package/dist/components/form-control/Invalid.js +27 -26
  113. package/dist/components/form-control/Invalid.js.map +2 -2
  114. package/dist/components/form-control/checkbox/Checkbox.d.ts +1 -1
  115. package/dist/components/form-control/checkbox/Checkbox.d.ts.map +1 -1
  116. package/dist/components/form-control/checkbox/CheckboxGroup.d.ts +10 -10
  117. package/dist/components/form-control/checkbox/CheckboxGroup.d.ts.map +1 -1
  118. package/dist/components/form-control/checkbox/CheckboxGroup.js +2 -2
  119. package/dist/components/form-control/checkbox/CheckboxGroup.js.map +2 -2
  120. package/dist/components/form-control/checkbox/Radio.d.ts +1 -1
  121. package/dist/components/form-control/checkbox/Radio.d.ts.map +1 -1
  122. package/dist/components/form-control/checkbox/RadioGroup.d.ts +10 -10
  123. package/dist/components/form-control/checkbox/RadioGroup.d.ts.map +1 -1
  124. package/dist/components/form-control/checkbox/RadioGroup.js +2 -2
  125. package/dist/components/form-control/checkbox/RadioGroup.js.map +2 -2
  126. package/dist/components/form-control/checkbox/SelectableBase.d.ts +1 -1
  127. package/dist/components/form-control/checkbox/SelectableBase.d.ts.map +1 -1
  128. package/dist/components/form-control/checkbox/SelectableBase.js +3 -3
  129. package/dist/components/form-control/checkbox/SelectableBase.js.map +2 -2
  130. package/dist/components/form-control/checkbox/SelectionGroupBase.d.ts +1 -1
  131. package/dist/components/form-control/checkbox/SelectionGroupBase.d.ts.map +1 -1
  132. package/dist/components/form-control/checkbox/SelectionGroupBase.js +3 -3
  133. package/dist/components/form-control/checkbox/SelectionGroupBase.js.map +2 -2
  134. package/dist/components/form-control/color-picker/ColorPicker.d.ts +2 -2
  135. package/dist/components/form-control/color-picker/ColorPicker.d.ts.map +1 -1
  136. package/dist/components/form-control/color-picker/ColorPicker.js +3 -3
  137. package/dist/components/form-control/color-picker/ColorPicker.js.map +2 -2
  138. package/dist/components/form-control/combobox/Combobox.d.ts +3 -3
  139. package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
  140. package/dist/components/form-control/combobox/Combobox.js +10 -5
  141. package/dist/components/form-control/combobox/Combobox.js.map +2 -2
  142. package/dist/components/form-control/date-range-picker/DateRangePicker.js +4 -4
  143. package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
  144. package/dist/components/form-control/editor/EditorToolbar.d.ts.map +1 -1
  145. package/dist/components/form-control/editor/EditorToolbar.js +176 -312
  146. package/dist/components/form-control/editor/EditorToolbar.js.map +2 -2
  147. package/dist/components/form-control/field/DatePicker.d.ts +2 -2
  148. package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
  149. package/dist/components/form-control/field/DatePicker.js +3 -3
  150. package/dist/components/form-control/field/DatePicker.js.map +2 -2
  151. package/dist/components/form-control/field/DateTimePicker.d.ts +2 -2
  152. package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
  153. package/dist/components/form-control/field/DateTimePicker.js +3 -3
  154. package/dist/components/form-control/field/DateTimePicker.js.map +2 -2
  155. package/dist/components/form-control/field/FieldShell.d.ts +1 -1
  156. package/dist/components/form-control/field/FieldShell.d.ts.map +1 -1
  157. package/dist/components/form-control/field/FieldShell.js +2 -2
  158. package/dist/components/form-control/field/FieldShell.js.map +2 -2
  159. package/dist/components/form-control/field/NumberInput.d.ts +7 -28
  160. package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
  161. package/dist/components/form-control/field/NumberInput.js +7 -5
  162. package/dist/components/form-control/field/NumberInput.js.map +2 -2
  163. package/dist/components/form-control/field/TextInput.d.ts +7 -23
  164. package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
  165. package/dist/components/form-control/field/TextInput.js +6 -5
  166. package/dist/components/form-control/field/TextInput.js.map +2 -2
  167. package/dist/components/form-control/field/Textarea.d.ts +2 -2
  168. package/dist/components/form-control/field/Textarea.d.ts.map +1 -1
  169. package/dist/components/form-control/field/Textarea.js +3 -3
  170. package/dist/components/form-control/field/Textarea.js.map +2 -2
  171. package/dist/components/form-control/field/TimePicker.d.ts +2 -2
  172. package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
  173. package/dist/components/form-control/field/TimePicker.js +3 -3
  174. package/dist/components/form-control/field/TimePicker.js.map +2 -2
  175. package/dist/components/form-control/select/Select.d.ts +22 -44
  176. package/dist/components/form-control/select/Select.d.ts.map +1 -1
  177. package/dist/components/form-control/select/Select.js +8 -6
  178. package/dist/components/form-control/select/Select.js.map +2 -2
  179. package/dist/components/form-control/state-preset/StatePreset.js +5 -5
  180. package/dist/components/form-control/state-preset/StatePreset.js.map +2 -2
  181. package/dist/components/layout/sidebar/Sidebar.d.ts +5 -102
  182. package/dist/components/layout/sidebar/Sidebar.d.ts.map +1 -1
  183. package/dist/components/layout/sidebar/Sidebar.js.map +1 -1
  184. package/dist/components/layout/topbar/Topbar.d.ts +1 -1
  185. package/dist/components/layout/topbar/Topbar.d.ts.map +1 -1
  186. package/dist/components/layout/topbar/Topbar.js +3 -3
  187. package/dist/components/layout/topbar/Topbar.js.map +2 -2
  188. package/dist/helpers/createAppStructure.d.ts.map +1 -1
  189. package/dist/helpers/createAppStructure.js.map +1 -1
  190. package/dist/hooks/createControllableStore.js +5 -5
  191. package/dist/hooks/createControllableStore.js.map +1 -1
  192. package/dist/index.d.ts +4 -3
  193. package/dist/index.d.ts.map +1 -1
  194. package/dist/index.js +4 -3
  195. package/dist/index.js.map +1 -1
  196. package/dist/providers/ServiceClientProvider.d.ts +2 -2
  197. package/dist/providers/ServiceClientProvider.d.ts.map +1 -1
  198. package/dist/providers/ServiceClientProvider.js.map +1 -1
  199. package/dist/providers/shared-data/SharedDataProvider.d.ts +1 -1
  200. package/dist/providers/shared-data/SharedDataProvider.d.ts.map +1 -1
  201. package/dist/providers/shared-data/SharedDataProvider.js +98 -86
  202. package/dist/providers/shared-data/SharedDataProvider.js.map +2 -2
  203. package/package.json +5 -5
  204. package/src/components/data/kanban/Kanban.tsx +37 -32
  205. package/src/components/data/list/List.tsx +3 -6
  206. package/src/components/data/sheet/DataSheet.tsx +224 -334
  207. package/src/components/data/sheet/{types.ts → DataSheet.types.ts} +8 -2
  208. package/src/components/data/sheet/{sheetUtils.ts → DataSheet.utils.ts} +3 -3
  209. package/src/components/data/sheet/DataSheetColumn.tsx +1 -1
  210. package/src/components/data/sheet/DataSheetConfigDialog.tsx +1 -1
  211. package/src/components/data/sheet/hooks/useDataSheetExpansion.ts +2 -2
  212. package/src/components/data/sheet/hooks/useDataSheetFixedColumns.ts +2 -2
  213. package/src/components/data/sheet/hooks/useDataSheetHeaderCell.tsx +168 -0
  214. package/src/components/data/sheet/hooks/useDataSheetReorder.ts +1 -2
  215. package/src/components/data/sheet/hooks/useDataSheetSelection.ts +4 -4
  216. package/src/components/data/sheet/hooks/useDataSheetSorting.ts +2 -2
  217. package/src/components/disclosure/Dialog.tsx +21 -27
  218. package/src/components/disclosure/Dropdown.tsx +1 -6
  219. package/src/components/disclosure/Tabs.tsx +1 -6
  220. package/src/components/display/Barcode.tsx +4 -113
  221. package/src/components/display/Barcode.types.ts +111 -0
  222. package/src/components/display/Echarts.tsx +10 -11
  223. package/src/components/features/address/AddressSearch.tsx +21 -3
  224. package/src/components/features/crud-detail/CrudDetail.tsx +21 -38
  225. package/src/components/features/crud-sheet/CrudSheet.tsx +77 -93
  226. package/src/components/features/crud-sheet/{types.ts → CrudSheet.types.ts} +9 -9
  227. package/src/components/features/crud-sheet/CrudSheetColumn.tsx +1 -1
  228. package/src/components/features/crud-sheet/CrudSheetTools.tsx +1 -1
  229. package/src/components/features/data-select-button/DataSelectButton.tsx +8 -8
  230. package/src/components/features/shared-data/SharedDataSelect.tsx +23 -34
  231. package/src/components/features/shared-data/SharedDataSelectButton.tsx +1 -4
  232. package/src/components/features/shared-data/SharedDataSelectList.tsx +6 -10
  233. package/src/components/feedback/notification/NotificationProvider.tsx +9 -1
  234. package/src/components/feedback/print/Print.tsx +4 -8
  235. package/src/components/feedback/print/PrintProvider.tsx +2 -1
  236. package/src/components/form-control/Invalid.tsx +33 -29
  237. package/src/components/form-control/checkbox/Checkbox.tsx +1 -1
  238. package/src/components/form-control/checkbox/CheckboxGroup.tsx +3 -12
  239. package/src/components/form-control/checkbox/Radio.tsx +1 -1
  240. package/src/components/form-control/checkbox/RadioGroup.tsx +3 -12
  241. package/src/components/form-control/checkbox/SelectableBase.tsx +3 -3
  242. package/src/components/form-control/checkbox/SelectionGroupBase.tsx +3 -3
  243. package/src/components/form-control/color-picker/ColorPicker.tsx +4 -4
  244. package/src/components/form-control/combobox/Combobox.tsx +23 -18
  245. package/src/components/form-control/date-range-picker/DateRangePicker.tsx +5 -5
  246. package/src/components/form-control/editor/EditorToolbar.tsx +82 -202
  247. package/src/components/form-control/field/DatePicker.tsx +4 -4
  248. package/src/components/form-control/field/DateTimePicker.tsx +4 -4
  249. package/src/components/form-control/field/FieldShell.tsx +2 -2
  250. package/src/components/form-control/field/NumberInput.tsx +8 -11
  251. package/src/components/form-control/field/TextInput.tsx +7 -11
  252. package/src/components/form-control/field/Textarea.tsx +4 -4
  253. package/src/components/form-control/field/TimePicker.tsx +4 -4
  254. package/src/components/form-control/select/Select.tsx +33 -42
  255. package/src/components/form-control/state-preset/StatePreset.tsx +5 -5
  256. package/src/components/layout/sidebar/Sidebar.tsx +1 -7
  257. package/src/components/layout/topbar/Topbar.tsx +3 -3
  258. package/src/helpers/createAppStructure.ts +16 -0
  259. package/src/hooks/createControllableStore.ts +5 -5
  260. package/src/index.ts +4 -3
  261. package/src/providers/ServiceClientProvider.tsx +4 -4
  262. package/src/providers/shared-data/SharedDataProvider.tsx +136 -113
  263. package/tests/components/data/sheet/DataSheet.spec.tsx +2 -2
  264. package/tests/components/data/sheet/hooks/useDataSheetFixedColumns.spec.ts +1 -1
  265. package/tests/components/data/sheet/hooks/useDataSheetSelection.spec.ts +34 -17
  266. package/tests/components/data/sheet/hooks/useDataSheetSorting.spec.ts +1 -1
  267. package/tests/components/disclosure/Dialog.spec.tsx +7 -7
  268. package/tests/components/display/Barcode.spec.tsx +15 -1
  269. package/tests/components/features/crud-sheet/CrudSheet.spec.tsx +6 -6
  270. package/tests/components/form-control/Invalid.spec.tsx +5 -5
  271. package/tests/components/form-control/combobox/Combobox.spec.tsx +24 -1
  272. package/tests/components/layout/topbar/TopbarActions.spec.tsx +4 -4
  273. package/tests/components/layout/topbar/{createTopbarActions.spec.tsx → useTopbarActions.spec.tsx} +4 -4
  274. package/tests/providers/shared-data/SharedDataProvider.spec.tsx +130 -8
  275. package/dist/components/data/sheet/sheetUtils.d.ts.map +0 -1
  276. package/dist/components/data/sheet/sheetUtils.js.map +0 -6
  277. package/dist/components/data/sheet/types.d.ts.map +0 -1
  278. package/dist/components/data/sheet/types.js +0 -1
  279. package/dist/components/features/crud-detail/types.d.ts.map +0 -1
  280. package/dist/components/features/crud-detail/types.js +0 -1
  281. package/dist/components/features/crud-sheet/types.d.ts.map +0 -1
  282. package/dist/components/features/crud-sheet/types.js +0 -1
  283. package/dist/components/feedback/notification/index.d.ts +0 -4
  284. package/dist/components/feedback/notification/index.d.ts.map +0 -1
  285. package/dist/components/feedback/notification/index.js +0 -4
  286. package/dist/components/feedback/notification/index.js.map +0 -6
  287. package/src/components/feedback/notification/index.ts +0 -3
  288. /package/dist/components/data/sheet/{types.js.map → DataSheet.types.js.map} +0 -0
  289. /package/dist/components/{features/crud-detail/types.js.map → display/Barcode.types.js.map} +0 -0
  290. /package/dist/components/features/{crud-sheet/types.js.map → crud-detail/CrudDetail.types.js.map} +0 -0
  291. /package/src/components/features/crud-detail/{types.ts → CrudDetail.types.ts} +0 -0
@@ -1,9 +1,10 @@
1
1
  import { type Accessor, type JSX, createContext, createMemo, createSignal, onCleanup, useContext } from "solid-js";
2
- import { objEqual, waitUntil } from "@simplysm/core-common";
2
+ import { obj, wait as waitU } from "@simplysm/core-common";
3
3
  import { SharedDataChangeEvent } from "./SharedDataChangeEvent";
4
4
  import { useServiceClient } from "../ServiceClientProvider";
5
5
  import { useNotification } from "../../components/feedback/notification/NotificationProvider";
6
6
  import { useLogger } from "../../hooks/useLogger";
7
+ import type { ServiceClient } from "@simplysm/service-client";
7
8
 
8
9
  /**
9
10
  * Shared data definition.
@@ -92,9 +93,11 @@ export function useSharedData<
92
93
  if (!context) {
93
94
  throw new Error("useSharedData can only be used inside SharedDataProvider");
94
95
  }
95
- return context as unknown as SharedDataValue<TSharedData>;
96
+ return context as SharedDataValue<TSharedData>;
96
97
  }
97
98
 
99
+ type EntryState = "idle" | "initializing" | "ready" | "error";
100
+
98
101
  /**
99
102
  * Shared data Provider.
100
103
  *
@@ -102,7 +105,7 @@ export function useSharedData<
102
105
  * - Must be used inside ServiceClientProvider and NotificationProvider
103
106
  * - Logs fetch failures to logger if LoggerProvider is present
104
107
  * - Before configure(): only wait, busy, configure are accessible. Data access throws
105
- * - After configure(): registers definitions. Registers server event listeners + fetch on first items()/get() access per key (lazy)
108
+ * - After configure(): registers definitions and immediately starts fetching data for all entries
106
109
  * - Prevents data inversion on concurrent fetch calls via version counter
107
110
  * - Displays danger notification to the user on fetch failure
108
111
  * - Automatically releases all event listeners on cleanup
@@ -132,17 +135,14 @@ export function SharedDataProvider(props: { children: JSX.Element }): JSX.Elemen
132
135
  const [busyCount, setBusyCount] = createSignal(0);
133
136
  const busy: Accessor<boolean> = () => busyCount() > 0;
134
137
 
135
- const signalMap = new Map<string, ReturnType<typeof createSignal<unknown[]>>>();
136
- const memoMap = new Map<string, Accessor<Map<string | number, unknown>>>();
137
- const listenerKeyMap = new Map<string, string>();
138
- const versionMap = new Map<string, number>();
139
138
  const accessors: Record<string, SharedDataAccessor<unknown>> = {};
140
- let currentDefinitions: Record<string, SharedDataDefinition<unknown>> | undefined;
141
139
 
142
- function ordering<TT>(data: TT[], orderByList: [(item: TT) => unknown, "asc" | "desc"][]): TT[] {
140
+ let disposed = false;
141
+
142
+ function ordering<TItem>(data: TItem[], orderByList: [(item: TItem) => unknown, "asc" | "desc"][]): TItem[] {
143
143
  let result = [...data];
144
144
  for (const orderBy of [...orderByList].reverse()) {
145
- const selector = (item: TT) => orderBy[0](item) as string | number | undefined;
145
+ const selector = (item: TItem) => orderBy[0](item) as string | number | undefined;
146
146
  if (orderBy[1] === "desc") {
147
147
  result = result.orderByDesc(selector);
148
148
  } else {
@@ -152,49 +152,137 @@ export function SharedDataProvider(props: { children: JSX.Element }): JSX.Elemen
152
152
  return result;
153
153
  }
154
154
 
155
- async function loadData(
155
+ function createSharedDataEntry(
156
156
  name: string,
157
157
  def: SharedDataDefinition<unknown>,
158
- changeKeys?: Array<string | number>,
159
- ): Promise<void> {
160
- // CR-1: Prevent data inversion on concurrent calls via version counter
161
- const currentVersion = (versionMap.get(name) ?? 0) + 1;
162
- versionMap.set(name, currentVersion);
158
+ client: ServiceClient,
159
+ ): SharedDataAccessor<unknown> & { cleanup: () => void; initialize: () => Promise<void> } {
160
+ const [items, setItems] = createSignal<unknown[]>([]);
163
161
 
164
- setBusyCount((c) => c + 1);
165
- try {
166
- const signal = signalMap.get(name);
167
- if (!signal) throw new Error(`No shared data store found for '${name}'.`);
162
+ const itemMap = createMemo(() => {
163
+ const map = new Map<string | number, unknown>();
164
+ for (const item of items()) {
165
+ map.set(def.getKey(item as never), item);
166
+ }
167
+ return map;
168
+ });
168
169
 
169
- const [, setItems] = signal;
170
- const resData = await def.fetch(changeKeys);
170
+ let state: EntryState = "idle";
171
+ let listenerKey: string | undefined;
172
+ let version = 0;
171
173
 
172
- // CR-1: Ignore stale responses
173
- if (versionMap.get(name) !== currentVersion) return;
174
+ async function loadData(changeKeys?: Array<string | number>): Promise<void> {
175
+ // Prevent data inversion on concurrent calls via version counter
176
+ const currentVersion = ++version;
174
177
 
175
- if (!changeKeys) {
176
- setItems(ordering(resData, def.orderBy));
177
- } else {
178
- setItems((prev) => {
179
- const filtered = prev.filter((item) => !changeKeys.includes(def.getKey(item as never)));
180
- filtered.push(...resData);
181
- return ordering(filtered, def.orderBy);
182
- });
178
+ setBusyCount((c) => c + 1);
179
+ try {
180
+ const resData = await def.fetch(changeKeys);
181
+
182
+ // Ignore stale responses
183
+ if (version !== currentVersion) return;
184
+
185
+ if (!changeKeys) {
186
+ setItems(ordering(resData, def.orderBy));
187
+ } else {
188
+ setItems((prev) => {
189
+ const filtered = prev.filter((item) => !changeKeys.includes(def.getKey(item as never)));
190
+ filtered.push(...resData);
191
+ return ordering(filtered, def.orderBy);
192
+ });
193
+ }
194
+ } catch (err) {
195
+ logger.error(`SharedData '${name}' fetch failed:`, err);
196
+ notification.danger(
197
+ "Shared data load failed",
198
+ err instanceof Error ? err.message : `Error occurred while loading '${name}' data.`,
199
+ );
200
+ throw err;
201
+ } finally {
202
+ setBusyCount((c) => c - 1);
203
+ }
204
+ }
205
+
206
+ async function initialize(): Promise<void> {
207
+ if (state !== "idle" && state !== "error") return;
208
+
209
+ // Clean up leaked listener from previous failed attempt
210
+ if (listenerKey != null) {
211
+ void client.removeListener(listenerKey);
212
+ listenerKey = undefined;
213
+ }
214
+
215
+ state = "initializing";
216
+
217
+ // Increment busyCount synchronously so wait() sees busy state immediately
218
+ setBusyCount((c) => c + 1);
219
+
220
+ try {
221
+ const key = await client.addListener(
222
+ SharedDataChangeEvent,
223
+ { name, filter: def.filter },
224
+ async (changeKeys) => {
225
+ try {
226
+ await loadData(changeKeys);
227
+ } catch {
228
+ // Error already logged and notified in loadData
229
+ }
230
+ },
231
+ );
232
+
233
+ if (disposed) {
234
+ void client.removeListener(key);
235
+ return;
236
+ }
237
+
238
+ listenerKey = key;
239
+
240
+ await loadData();
241
+
242
+ state = "ready";
243
+ } catch {
244
+ state = "error";
245
+ } finally {
246
+ setBusyCount((c) => c - 1);
247
+ }
248
+ }
249
+
250
+ function cleanup(): void {
251
+ if (listenerKey != null) {
252
+ void client.removeListener(listenerKey);
183
253
  }
184
- } catch (err) {
185
- // CR-2: Notify user on fetch failure
186
- logger.error(`SharedData '${name}' fetch failed:`, err);
187
- notification.danger(
188
- "Shared data load failed",
189
- err instanceof Error ? err.message : `Error occurred while loading '${name}' data.`,
190
- );
191
- } finally {
192
- setBusyCount((c) => c - 1);
193
254
  }
255
+
256
+ return {
257
+ items: () => {
258
+ void initialize();
259
+ return items();
260
+ },
261
+ get: (key: string | number | undefined) => {
262
+ void initialize();
263
+ if (key === undefined) return undefined;
264
+ return itemMap().get(key);
265
+ },
266
+ emit: async (changeKeys?: Array<string | number>) => {
267
+ await client.emitEvent(
268
+ SharedDataChangeEvent,
269
+ (info) => info.name === name && obj.equal(info.filter, def.filter),
270
+ changeKeys,
271
+ );
272
+ },
273
+ getKey: def.getKey,
274
+ itemSearchText: def.itemSearchText,
275
+ isItemHidden: def.isItemHidden,
276
+ getParentKey: def.getParentKey,
277
+ cleanup,
278
+ initialize,
279
+ };
194
280
  }
195
281
 
282
+ const entries = new Map<string, ReturnType<typeof createSharedDataEntry>>();
283
+
196
284
  async function wait(): Promise<void> {
197
- await waitUntil(() => busyCount() <= 0);
285
+ await waitU.until(() => busyCount() <= 0);
198
286
  }
199
287
 
200
288
  function configure(
@@ -208,85 +296,20 @@ export function SharedDataProvider(props: { children: JSX.Element }): JSX.Elemen
208
296
  configured = true;
209
297
 
210
298
  const definitions = fn({});
211
- currentDefinitions = definitions;
212
299
 
213
300
  for (const [name, def] of Object.entries(definitions)) {
214
- const [items, setItems] = createSignal<unknown[]>([]);
215
- signalMap.set(name, [items, setItems]);
216
-
217
- const itemMap = createMemo(() => {
218
- const map = new Map<string | number, unknown>();
219
- for (const item of items()) {
220
- map.set(def.getKey(item as never), item);
221
- }
222
- return map;
223
- });
224
- memoMap.set(name, itemMap);
225
-
226
301
  const client = serviceClient.get(def.serviceKey ?? "default");
227
-
228
- let initialized = false;
229
-
230
- function ensureInitialized() {
231
- if (initialized) return;
232
- initialized = true;
233
-
234
- void client
235
- .addEventListener(
236
- SharedDataChangeEvent,
237
- { name, filter: def.filter },
238
- async (changeKeys) => {
239
- await loadData(name, def, changeKeys);
240
- },
241
- )
242
- .then((key) => {
243
- if (disposed) {
244
- void client.removeEventListener(key);
245
- } else {
246
- listenerKeyMap.set(name, key);
247
- }
248
- });
249
-
250
- void loadData(name, def);
251
- }
252
-
253
- accessors[name] = {
254
- items: () => {
255
- ensureInitialized();
256
- return items();
257
- },
258
- get: (key: string | number | undefined) => {
259
- ensureInitialized();
260
- if (key === undefined) return undefined;
261
- return itemMap().get(key);
262
- },
263
- emit: async (changeKeys?: Array<string | number>) => {
264
- await client.emitToServer(
265
- SharedDataChangeEvent,
266
- (info) => info.name === name && objEqual(info.filter, def.filter),
267
- changeKeys,
268
- );
269
- },
270
- getKey: def.getKey,
271
- itemSearchText: def.itemSearchText,
272
- isItemHidden: def.isItemHidden,
273
- getParentKey: def.getParentKey,
274
- };
302
+ const entry = createSharedDataEntry(name, def, client);
303
+ entries.set(name, entry);
304
+ accessors[name] = entry;
305
+ void entry.initialize();
275
306
  }
276
307
  }
277
308
 
278
- let disposed = false;
279
-
280
309
  onCleanup(() => {
281
310
  disposed = true;
282
- if (!currentDefinitions) return;
283
- for (const [name] of Object.entries(currentDefinitions)) {
284
- const listenerKey = listenerKeyMap.get(name);
285
- if (listenerKey != null) {
286
- const def = currentDefinitions[name];
287
- const client = serviceClient.get(def.serviceKey ?? "default");
288
- void client.removeEventListener(listenerKey);
289
- }
311
+ for (const entry of entries.values()) {
312
+ entry.cleanup();
290
313
  }
291
314
  });
292
315
 
@@ -5,8 +5,8 @@ import {
5
5
  applySorting,
6
6
  collectAllExpandable,
7
7
  flattenTree,
8
- } from "../../../../src/components/data/sheet/sheetUtils";
9
- import type { SortingDef } from "../../../../src/components/data/sheet/types";
8
+ } from "../../../../src/components/data/sheet/DataSheet.utils";
9
+ import type { SortingDef } from "../../../../src/components/data/sheet/DataSheet.types";
10
10
  import { ConfigContext, ConfigProvider } from "../../../../src/providers/ConfigContext";
11
11
  import type { JSX } from "solid-js";
12
12
  import { I18nProvider } from "../../../../src/providers/i18n/I18nProvider";
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { createRoot } from "solid-js";
3
3
  import { useDataSheetFixedColumns } from "../../../../../src/components/data/sheet/hooks/useDataSheetFixedColumns";
4
- import type { DataSheetColumnDef } from "../../../../../src/components/data/sheet/types";
4
+ import type { DataSheetColumnDef } from "../../../../../src/components/data/sheet/DataSheet.types";
5
5
 
6
6
  interface TestItem {
7
7
  id: number;
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { createRoot } from "solid-js";
3
3
  import { useDataSheetSelection } from "../../../../../src/components/data/sheet/hooks/useDataSheetSelection";
4
- import type { FlatItem } from "../../../../../src/components/data/sheet/types";
4
+ import type { FlatItem } from "../../../../../src/components/data/sheet/DataSheet.types";
5
5
 
6
6
  interface TestItem {
7
7
  id: string;
@@ -56,23 +56,23 @@ describe("useDataSheetSelection", () => {
56
56
  });
57
57
  });
58
58
 
59
- it("getItemSelectable should return false when item is not selectable", () => {
59
+ it("getItemSelectable should return disabled string when item is not selectable", () => {
60
60
  createRoot(() => {
61
- const itemSelectable = (item: TestItem) => item.selectable !== false;
61
+ const isItemSelectable = (item: TestItem): true | string => item.selectable !== false ? true : "disabled";
62
62
  const result = useDataSheetSelection(
63
- { itemSelectable },
63
+ { isItemSelectable },
64
64
  () => createTestFlatItems(testItems),
65
65
  );
66
66
 
67
- expect(result.getItemSelectable(testItems[2])).toBe(false);
67
+ expect(result.getItemSelectable(testItems[2])).toBe("disabled");
68
68
  });
69
69
  });
70
70
 
71
71
  it("getItemSelectable should return reason string when item is disabled", () => {
72
72
  createRoot(() => {
73
- const itemSelectable = (item: TestItem) => item.disabledReason != null && item.disabledReason !== "" ? item.disabledReason : true;
73
+ const isItemSelectable = (item: TestItem) => item.disabledReason != null && item.disabledReason !== "" ? item.disabledReason : true;
74
74
  const result = useDataSheetSelection(
75
- { itemSelectable },
75
+ { isItemSelectable },
76
76
  () => createTestFlatItems(testItems),
77
77
  );
78
78
 
@@ -134,9 +134,9 @@ describe("useDataSheetSelection", () => {
134
134
 
135
135
  it("toggleSelect should not select non-selectable items", () => {
136
136
  createRoot(() => {
137
- const itemSelectable = (item: TestItem) => item.selectable !== false;
137
+ const isItemSelectable = (item: TestItem): true | string => item.selectable !== false ? true : "disabled";
138
138
  const result = useDataSheetSelection(
139
- { itemSelectable },
139
+ { isItemSelectable },
140
140
  () => createTestFlatItems(testItems),
141
141
  );
142
142
 
@@ -174,9 +174,9 @@ describe("useDataSheetSelection", () => {
174
174
 
175
175
  it("toggleSelectAll should select all selectable items", () => {
176
176
  createRoot(() => {
177
- const itemSelectable = (item: TestItem) => item.selectable !== false;
177
+ const isItemSelectable = (item: TestItem): true | string => item.selectable !== false ? true : "disabled";
178
178
  const result = useDataSheetSelection(
179
- { itemSelectable },
179
+ { isItemSelectable },
180
180
  () => createTestFlatItems(testItems),
181
181
  );
182
182
 
@@ -189,10 +189,10 @@ describe("useDataSheetSelection", () => {
189
189
 
190
190
  it("toggleSelectAll should deselect all items when all are selected", () => {
191
191
  createRoot(() => {
192
- const itemSelectable = (item: TestItem) => item.selectable !== false;
192
+ const isItemSelectable = (item: TestItem): true | string => item.selectable !== false ? true : "disabled";
193
193
  const selectableItems = testItems.filter((i) => i.selectable !== false);
194
194
  const result = useDataSheetSelection(
195
- { itemSelectable, selection: selectableItems },
195
+ { isItemSelectable, selection: selectableItems },
196
196
  () => createTestFlatItems(testItems),
197
197
  );
198
198
 
@@ -204,9 +204,9 @@ describe("useDataSheetSelection", () => {
204
204
 
205
205
  it("toggleSelectAll should only select selectable items", () => {
206
206
  createRoot(() => {
207
- const itemSelectable = (item: TestItem) => item.selectable !== false;
207
+ const isItemSelectable = (item: TestItem): true | string => item.selectable !== false ? true : "disabled";
208
208
  const result = useDataSheetSelection(
209
- { itemSelectable },
209
+ { isItemSelectable },
210
210
  () => createTestFlatItems(testItems),
211
211
  );
212
212
 
@@ -284,9 +284,9 @@ describe("useDataSheetSelection", () => {
284
284
 
285
285
  it("rangeSelect should only select selectable items in range", () => {
286
286
  createRoot(() => {
287
- const itemSelectable = (item: TestItem) => item.selectable !== false;
287
+ const isItemSelectable = (item: TestItem): true | string => item.selectable !== false ? true : "disabled";
288
288
  const result = useDataSheetSelection(
289
- { selectionMode: "multiple", itemSelectable },
289
+ { selectionMode: "multiple", isItemSelectable },
290
290
  () => createTestFlatItems(testItems),
291
291
  );
292
292
 
@@ -342,4 +342,21 @@ describe("useDataSheetSelection", () => {
342
342
  expect(result.lastClickAction()).toBe("select");
343
343
  });
344
344
  });
345
+
346
+ it("treats false return from isItemSelectable as not selectable", () => {
347
+ createRoot((dispose) => {
348
+ const flatItems = () => createTestFlatItems(testItems);
349
+ const sel = useDataSheetSelection<TestItem>(
350
+ {
351
+ selectionMode: "multiple",
352
+ isItemSelectable: (item) => (item.selectable === false ? false : true),
353
+ },
354
+ flatItems,
355
+ );
356
+ expect(sel.getItemSelectable(testItems[2])).toBe(false);
357
+ sel.toggleSelect(testItems[2]);
358
+ expect(sel.selection()).toEqual([]);
359
+ dispose();
360
+ });
361
+ });
345
362
  });
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { createRoot, createSignal } from "solid-js";
3
3
  import { useDataSheetSorting } from "../../../../../src/components/data/sheet/hooks/useDataSheetSorting";
4
- import type { SortingDef } from "../../../../../src/components/data/sheet/types";
4
+ import type { SortingDef } from "../../../../../src/components/data/sheet/DataSheet.types";
5
5
 
6
6
  interface TestItem {
7
7
  id: number;
@@ -207,11 +207,11 @@ describe("Dialog", () => {
207
207
  expect(handleOpenChange).not.toHaveBeenCalled();
208
208
  });
209
209
 
210
- it("does not close when canDeactivate returns false", async () => {
210
+ it("does not close when beforeClose returns false", async () => {
211
211
  const handleOpenChange = vi.fn();
212
212
  render(() => (
213
213
  <ConfigProvider clientName="test"><I18nProvider>
214
- <Dialog open={true} onOpenChange={handleOpenChange} canDeactivate={() => false}>
214
+ <Dialog open={true} onOpenChange={handleOpenChange} beforeClose={() => false}>
215
215
  <Dialog.Header>테스트</Dialog.Header>
216
216
  <div>내용</div>
217
217
  </Dialog>
@@ -264,7 +264,7 @@ describe("Dialog", () => {
264
264
  it("does not set aria-modal in float mode", async () => {
265
265
  render(() => (
266
266
  <ConfigProvider clientName="test"><I18nProvider>
267
- <Dialog open={true} float>
267
+ <Dialog open={true} mode="float">
268
268
  <Dialog.Header>플로팅 다이얼로그</Dialog.Header>
269
269
  <div>내용</div>
270
270
  </Dialog>
@@ -280,10 +280,10 @@ describe("Dialog", () => {
280
280
  });
281
281
 
282
282
  describe("float mode", () => {
283
- it("has no backdrop when float=true", async () => {
283
+ it("has no backdrop when mode='float'", async () => {
284
284
  render(() => (
285
285
  <ConfigProvider clientName="test"><I18nProvider>
286
- <Dialog open={true} float>
286
+ <Dialog open={true} mode="float">
287
287
  <Dialog.Header>테스트</Dialog.Header>
288
288
  <div data-testid="content">내용</div>
289
289
  </Dialog>
@@ -298,10 +298,10 @@ describe("Dialog", () => {
298
298
  });
299
299
 
300
300
  describe("fill mode", () => {
301
- it("applies fill style to dialog when fill=true", async () => {
301
+ it("applies fill style to dialog when mode='fill'", async () => {
302
302
  render(() => (
303
303
  <ConfigProvider clientName="test"><I18nProvider>
304
- <Dialog open={true} fill>
304
+ <Dialog open={true} mode="fill">
305
305
  <Dialog.Header>테스트</Dialog.Header>
306
306
  <div>내용</div>
307
307
  </Dialog>
@@ -1,5 +1,5 @@
1
1
  import { render } from "@solidjs/testing-library";
2
- import { describe, it, expect } from "vitest";
2
+ import { describe, it, expect, vi } from "vitest";
3
3
  import { createSignal } from "solid-js";
4
4
  import { Barcode } from "../../../src/components/display/Barcode";
5
5
 
@@ -18,6 +18,20 @@ describe("Barcode", () => {
18
18
  });
19
19
  });
20
20
 
21
+ it("logs warning on render failure", () => {
22
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
23
+
24
+ // "code128" requires specific format; passing invalid chars triggers bwip-js error
25
+ render(() => <Barcode type={"invalid-type" as any} value="test" />);
26
+
27
+ expect(warnSpy).toHaveBeenCalledWith(
28
+ "Barcode render failed:",
29
+ expect.any(Error),
30
+ );
31
+
32
+ warnSpy.mockRestore();
33
+ });
34
+
21
35
  describe("reactivity", () => {
22
36
  it("updates SVG when value changes", () => {
23
37
  const [value, setValue] = createSignal("first");
@@ -25,7 +25,7 @@ function DialogWrapper(props: { children: JSX.Element }) {
25
25
  return (
26
26
  <ConfigContext.Provider value={{ clientName: "test" }}>
27
27
  <NotificationProvider>
28
- <Dialog open fill>
28
+ <Dialog open mode="fill">
29
29
  <Dialog.Header>Test Dialog</Dialog.Header>
30
30
  {props.children}
31
31
  </Dialog>
@@ -174,7 +174,7 @@ describe("CrudSheet inline edit", () => {
174
174
  <CrudSheet<TestItem, Record<string, never>>
175
175
  search={searchFn}
176
176
  getItemKey={(item) => item.id}
177
- itemDeleted={(item) => item.isDeleted}
177
+ isItemDeleted={(item) => item.isDeleted}
178
178
  inlineEdit={{
179
179
  submit: () => Promise.resolve(),
180
180
  newItem: () => ({ name: "", isDeleted: false }),
@@ -196,7 +196,7 @@ describe("CrudSheet inline edit", () => {
196
196
  });
197
197
  });
198
198
 
199
- describe("CrudSheet itemDeletable", () => {
199
+ describe("CrudSheet isItemDeletable", () => {
200
200
  beforeEach(() => {
201
201
  localStorage.setItem("test.i18n-locale", JSON.stringify("en"));
202
202
  });
@@ -205,7 +205,7 @@ describe("CrudSheet itemDeletable", () => {
205
205
  localStorage.removeItem("test.i18n-locale");
206
206
  });
207
207
 
208
- it("inline delete button is disabled for items where itemDeletable=false", async () => {
208
+ it("inline delete button is disabled for items where isItemDeletable=false", async () => {
209
209
  const searchFn = () =>
210
210
  Promise.resolve({
211
211
  items: [
@@ -221,8 +221,8 @@ describe("CrudSheet itemDeletable", () => {
221
221
  <CrudSheet<TestItem, Record<string, never>>
222
222
  search={searchFn}
223
223
  getItemKey={(item) => item.id}
224
- itemDeletable={(item) => item.id !== 1}
225
- itemDeleted={(item) => item.isDeleted}
224
+ isItemDeletable={(item) => item.id !== 1}
225
+ isItemDeleted={(item) => item.isDeleted}
226
226
  inlineEdit={{
227
227
  submit: () => Promise.resolve(),
228
228
  newItem: () => ({ name: "", isDeleted: false }),
@@ -90,10 +90,10 @@ describe("Invalid component", () => {
90
90
  });
91
91
  });
92
92
 
93
- describe("touchMode", () => {
94
- it("has no visual indication initially in touchMode", () => {
93
+ describe("lazyValidation", () => {
94
+ it("has no visual indication initially in lazyValidation", () => {
95
95
  const { container } = render(() => (
96
- <Invalid variant="border" message="error" touchMode>
96
+ <Invalid variant="border" message="error" lazyValidation>
97
97
  <div data-testid="target" class="border">
98
98
  Content
99
99
  </div>
@@ -103,9 +103,9 @@ describe("Invalid component", () => {
103
103
  expect(target.classList.contains("border-danger-500")).toBe(false);
104
104
  });
105
105
 
106
- it("setCustomValidity is always set in touchMode", () => {
106
+ it("setCustomValidity is always set in lazyValidation", () => {
107
107
  const { container } = render(() => (
108
- <Invalid variant="border" message="error" touchMode>
108
+ <Invalid variant="border" message="error" lazyValidation>
109
109
  <div>Content</div>
110
110
  </Invalid>
111
111
  ));
@@ -132,7 +132,7 @@ describe("Combobox component", () => {
132
132
  });
133
133
 
134
134
  describe("allowsCustomValue", () => {
135
- it("allows entering custom value with Enter when allowsCustomValue is true", () => {
135
+ it("allows entering custom value with Enter when allowsCustomValue and parseCustomValue are provided", () => {
136
136
  const handleChange = vi.fn();
137
137
  const loadItems = vi.fn(() => Promise.resolve([]));
138
138
 
@@ -142,6 +142,7 @@ describe("Combobox component", () => {
142
142
  loadItems={loadItems}
143
143
  onValueChange={handleChange}
144
144
  allowsCustomValue
145
+ parseCustomValue={(text) => text}
145
146
  renderValue={(v) => <>{v}</>}
146
147
  />
147
148
  </I18nProvider></ConfigProvider>
@@ -176,6 +177,28 @@ describe("Combobox component", () => {
176
177
 
177
178
  expect(handleChange).toHaveBeenCalledWith({ name: "테스트", custom: true });
178
179
  });
180
+
181
+ it("sets undefined when allowsCustomValue is true without parseCustomValue", async () => {
182
+ const onValueChange = vi.fn();
183
+ const { container, getByRole } = render(() => (
184
+ <ConfigProvider clientName="test"><I18nProvider>
185
+ <Combobox
186
+ loadItems={() => []}
187
+ renderValue={(v: string) => <>{v}</>}
188
+ allowsCustomValue
189
+ onValueChange={onValueChange}
190
+ />
191
+ </I18nProvider></ConfigProvider>
192
+ ));
193
+
194
+ const input = container.querySelector("input")!;
195
+ fireEvent.input(input, { target: { value: "custom text" } });
196
+ fireEvent.keyDown(getByRole("combobox"), { key: "Enter" });
197
+
198
+ await waitFor(() => {
199
+ expect(onValueChange).toHaveBeenCalledWith(undefined);
200
+ });
201
+ });
179
202
  });
180
203
 
181
204
  describe("validation", () => {