@multiplatform.one/table 7.7.6 → 7.10.0

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 (384) hide show
  1. package/dist/cjs/components/ColumnHeaderFilter/index.cjs +74 -38
  2. package/dist/cjs/components/ColumnHeaderFilter/index.native.js +97 -51
  3. package/dist/cjs/components/ColumnHeaderFilter/index.native.js.map +1 -1
  4. package/dist/cjs/components/DataTable/DataTableCardControls.cjs +0 -1
  5. package/dist/cjs/components/DataTable/DataTableCardControls.native.js +0 -1
  6. package/dist/cjs/components/DataTable/DataTableCardControls.native.js.map +1 -1
  7. package/dist/cjs/components/DataTable/DataTableMobileCards.cjs +100 -33
  8. package/dist/cjs/components/DataTable/DataTableMobileCards.native.js +139 -38
  9. package/dist/cjs/components/DataTable/DataTableMobileCards.native.js.map +1 -1
  10. package/dist/cjs/components/DataTable/DataTableRoot.cjs +33 -6
  11. package/dist/cjs/components/DataTable/DataTableRoot.native.js +29 -4
  12. package/dist/cjs/components/DataTable/DataTableRoot.native.js.map +1 -1
  13. package/dist/cjs/components/DataTable/DataTableStateContext.native.js.map +1 -1
  14. package/dist/cjs/components/DataTable/index.native.js +59 -93
  15. package/dist/cjs/components/DataTable/index.native.js.map +1 -1
  16. package/dist/cjs/components/DataTableBulkActions/index.cjs +0 -4
  17. package/dist/cjs/components/DataTableBulkActions/index.native.js +0 -4
  18. package/dist/cjs/components/DataTableBulkActions/index.native.js.map +1 -1
  19. package/dist/cjs/components/DataTableCell/index.cjs +16 -1
  20. package/dist/cjs/components/DataTableCell/index.native.js +16 -1
  21. package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
  22. package/dist/cjs/components/DataTableDateFilter/index.cjs +0 -7
  23. package/dist/cjs/components/DataTableDateFilter/index.native.js +0 -7
  24. package/dist/cjs/components/DataTableDateFilter/index.native.js.map +1 -1
  25. package/dist/cjs/components/DataTableFilterButton/index.cjs +0 -1
  26. package/dist/cjs/components/DataTableFilterButton/index.native.js +0 -1
  27. package/dist/cjs/components/DataTableFilterChips/FilterChip.cjs +2 -1
  28. package/dist/cjs/components/DataTableFilterChips/FilterChip.native.js +4 -1
  29. package/dist/cjs/components/DataTableFilterChips/FilterChip.native.js.map +1 -1
  30. package/dist/cjs/components/DataTableFilters/index.cjs +0 -1
  31. package/dist/cjs/components/DataTableFilters/index.native.js +0 -1
  32. package/dist/cjs/components/DataTableGrouping/index.cjs +0 -8
  33. package/dist/cjs/components/DataTableGrouping/index.native.js +0 -8
  34. package/dist/cjs/components/DataTableGrouping/index.native.js.map +1 -1
  35. package/dist/cjs/components/DataTablePagination/index.cjs +29 -13
  36. package/dist/cjs/components/DataTablePagination/index.native.js +30 -14
  37. package/dist/cjs/components/DataTablePagination/index.native.js.map +1 -1
  38. package/dist/cjs/components/DataTableSearch/index.cjs +0 -3
  39. package/dist/cjs/components/DataTableSearch/index.native.js +0 -3
  40. package/dist/cjs/components/DataTableSearch/index.native.js.map +1 -1
  41. package/dist/cjs/components/DataTableToolbar/index.cjs +0 -1
  42. package/dist/cjs/components/DataTableToolbar/index.native.js +0 -1
  43. package/dist/cjs/components/DataTableToolbar/index.native.js.map +1 -1
  44. package/dist/cjs/components/DataTableViewOptions/index.cjs +0 -5
  45. package/dist/cjs/components/DataTableViewOptions/index.native.js +0 -5
  46. package/dist/cjs/components/DataTableViewOptions/index.native.js.map +1 -1
  47. package/dist/cjs/components/FilterManager.cjs +0 -1
  48. package/dist/cjs/components/FilterManager.native.js +0 -1
  49. package/dist/cjs/components/FilterManager.native.js.map +1 -1
  50. package/dist/cjs/components/FilterOperatorValueEditor/index.cjs +2 -2
  51. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js +2 -2
  52. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  53. package/dist/cjs/components/TableInput/index.cjs +2 -1
  54. package/dist/cjs/components/TableInput/index.native.js +4 -2
  55. package/dist/cjs/components/TableInput/index.native.js.map +1 -1
  56. package/dist/cjs/hooks/useDataTable.cjs +24 -8
  57. package/dist/cjs/hooks/useDataTable.native.js +28 -8
  58. package/dist/cjs/hooks/useDataTable.native.js.map +1 -1
  59. package/dist/cjs/index.cjs +12 -0
  60. package/dist/cjs/index.native.js +12 -0
  61. package/dist/cjs/index.native.js.map +1 -1
  62. package/dist/cjs/shared/devWarn.cjs +70 -0
  63. package/dist/cjs/shared/devWarn.native.js +84 -0
  64. package/dist/cjs/shared/devWarn.native.js.map +1 -0
  65. package/dist/cjs/utils/columnAlign.cjs +4 -1
  66. package/dist/cjs/utils/columnAlign.native.js +4 -1
  67. package/dist/cjs/utils/columnAlign.native.js.map +1 -1
  68. package/dist/cjs/utils/filterFields.cjs +5 -0
  69. package/dist/cjs/utils/filterFields.native.js +5 -0
  70. package/dist/cjs/utils/filterFields.native.js.map +1 -1
  71. package/dist/cjs/utils/filterUtils.cjs +30 -0
  72. package/dist/cjs/utils/filterUtils.native.js +50 -0
  73. package/dist/cjs/utils/filterUtils.native.js.map +1 -1
  74. package/dist/cjs/utils/money.cjs +137 -0
  75. package/dist/cjs/utils/money.native.js +165 -0
  76. package/dist/cjs/utils/money.native.js.map +1 -0
  77. package/dist/cjs/utils/rowAccumulator.cjs +48 -0
  78. package/dist/cjs/utils/rowAccumulator.native.js +51 -0
  79. package/dist/cjs/utils/rowAccumulator.native.js.map +1 -0
  80. package/dist/esm/components/ColumnHeaderFilter/index.mjs +78 -42
  81. package/dist/esm/components/ColumnHeaderFilter/index.mjs.map +1 -1
  82. package/dist/esm/components/ColumnHeaderFilter/index.native.js +101 -55
  83. package/dist/esm/components/ColumnHeaderFilter/index.native.js.map +1 -1
  84. package/dist/esm/components/DataTable/DataTableCardControls.mjs +0 -1
  85. package/dist/esm/components/DataTable/DataTableCardControls.mjs.map +1 -1
  86. package/dist/esm/components/DataTable/DataTableCardControls.native.js +0 -1
  87. package/dist/esm/components/DataTable/DataTableCardControls.native.js.map +1 -1
  88. package/dist/esm/components/DataTable/DataTableMobileCards.mjs +99 -34
  89. package/dist/esm/components/DataTable/DataTableMobileCards.mjs.map +1 -1
  90. package/dist/esm/components/DataTable/DataTableMobileCards.native.js +138 -39
  91. package/dist/esm/components/DataTable/DataTableMobileCards.native.js.map +1 -1
  92. package/dist/esm/components/DataTable/DataTableRoot.mjs +33 -6
  93. package/dist/esm/components/DataTable/DataTableRoot.mjs.map +1 -1
  94. package/dist/esm/components/DataTable/DataTableRoot.native.js +29 -4
  95. package/dist/esm/components/DataTable/DataTableRoot.native.js.map +1 -1
  96. package/dist/esm/components/DataTable/DataTableStateContext.mjs.map +1 -1
  97. package/dist/esm/components/DataTable/DataTableStateContext.native.js.map +1 -1
  98. package/dist/esm/components/DataTable/index.native.js +60 -94
  99. package/dist/esm/components/DataTable/index.native.js.map +1 -1
  100. package/dist/esm/components/DataTableBulkActions/index.mjs +0 -4
  101. package/dist/esm/components/DataTableBulkActions/index.mjs.map +1 -1
  102. package/dist/esm/components/DataTableBulkActions/index.native.js +0 -4
  103. package/dist/esm/components/DataTableBulkActions/index.native.js.map +1 -1
  104. package/dist/esm/components/DataTableCell/index.mjs +17 -2
  105. package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
  106. package/dist/esm/components/DataTableCell/index.native.js +17 -2
  107. package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
  108. package/dist/esm/components/DataTableDateFilter/index.mjs +0 -7
  109. package/dist/esm/components/DataTableDateFilter/index.mjs.map +1 -1
  110. package/dist/esm/components/DataTableDateFilter/index.native.js +0 -7
  111. package/dist/esm/components/DataTableDateFilter/index.native.js.map +1 -1
  112. package/dist/esm/components/DataTableFilterButton/index.mjs +0 -1
  113. package/dist/esm/components/DataTableFilterButton/index.mjs.map +1 -1
  114. package/dist/esm/components/DataTableFilterButton/index.native.js +0 -1
  115. package/dist/esm/components/DataTableFilterChips/FilterChip.mjs +3 -2
  116. package/dist/esm/components/DataTableFilterChips/FilterChip.mjs.map +1 -1
  117. package/dist/esm/components/DataTableFilterChips/FilterChip.native.js +5 -2
  118. package/dist/esm/components/DataTableFilterChips/FilterChip.native.js.map +1 -1
  119. package/dist/esm/components/DataTableFilters/index.mjs +0 -1
  120. package/dist/esm/components/DataTableFilters/index.mjs.map +1 -1
  121. package/dist/esm/components/DataTableFilters/index.native.js +0 -1
  122. package/dist/esm/components/DataTableGrouping/index.mjs +0 -8
  123. package/dist/esm/components/DataTableGrouping/index.mjs.map +1 -1
  124. package/dist/esm/components/DataTableGrouping/index.native.js +0 -8
  125. package/dist/esm/components/DataTableGrouping/index.native.js.map +1 -1
  126. package/dist/esm/components/DataTablePagination/index.mjs +30 -14
  127. package/dist/esm/components/DataTablePagination/index.mjs.map +1 -1
  128. package/dist/esm/components/DataTablePagination/index.native.js +31 -15
  129. package/dist/esm/components/DataTablePagination/index.native.js.map +1 -1
  130. package/dist/esm/components/DataTableSearch/index.mjs +0 -3
  131. package/dist/esm/components/DataTableSearch/index.mjs.map +1 -1
  132. package/dist/esm/components/DataTableSearch/index.native.js +0 -3
  133. package/dist/esm/components/DataTableSearch/index.native.js.map +1 -1
  134. package/dist/esm/components/DataTableToolbar/index.mjs +0 -1
  135. package/dist/esm/components/DataTableToolbar/index.mjs.map +1 -1
  136. package/dist/esm/components/DataTableToolbar/index.native.js +0 -1
  137. package/dist/esm/components/DataTableToolbar/index.native.js.map +1 -1
  138. package/dist/esm/components/DataTableViewOptions/index.mjs +0 -5
  139. package/dist/esm/components/DataTableViewOptions/index.mjs.map +1 -1
  140. package/dist/esm/components/DataTableViewOptions/index.native.js +0 -5
  141. package/dist/esm/components/DataTableViewOptions/index.native.js.map +1 -1
  142. package/dist/esm/components/FilterManager.mjs +0 -1
  143. package/dist/esm/components/FilterManager.mjs.map +1 -1
  144. package/dist/esm/components/FilterManager.native.js +0 -1
  145. package/dist/esm/components/FilterManager.native.js.map +1 -1
  146. package/dist/esm/components/FilterOperatorValueEditor/index.mjs +2 -2
  147. package/dist/esm/components/FilterOperatorValueEditor/index.mjs.map +1 -1
  148. package/dist/esm/components/FilterOperatorValueEditor/index.native.js +2 -2
  149. package/dist/esm/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  150. package/dist/esm/components/TableInput/index.mjs +3 -2
  151. package/dist/esm/components/TableInput/index.mjs.map +1 -1
  152. package/dist/esm/components/TableInput/index.native.js +5 -3
  153. package/dist/esm/components/TableInput/index.native.js.map +1 -1
  154. package/dist/esm/hooks/useDataTable.mjs +24 -8
  155. package/dist/esm/hooks/useDataTable.mjs.map +1 -1
  156. package/dist/esm/hooks/useDataTable.native.js +28 -8
  157. package/dist/esm/hooks/useDataTable.native.js.map +1 -1
  158. package/dist/esm/index.mjs +3 -1
  159. package/dist/esm/index.mjs.map +1 -1
  160. package/dist/esm/index.native.js +3 -1
  161. package/dist/esm/index.native.js.map +1 -1
  162. package/dist/esm/shared/devWarn.mjs +43 -0
  163. package/dist/esm/shared/devWarn.mjs.map +1 -0
  164. package/dist/esm/shared/devWarn.native.js +54 -0
  165. package/dist/esm/shared/devWarn.native.js.map +1 -0
  166. package/dist/esm/utils/columnAlign.mjs +4 -1
  167. package/dist/esm/utils/columnAlign.mjs.map +1 -1
  168. package/dist/esm/utils/columnAlign.native.js +4 -1
  169. package/dist/esm/utils/columnAlign.native.js.map +1 -1
  170. package/dist/esm/utils/filterFields.mjs +5 -0
  171. package/dist/esm/utils/filterFields.mjs.map +1 -1
  172. package/dist/esm/utils/filterFields.native.js +5 -0
  173. package/dist/esm/utils/filterFields.native.js.map +1 -1
  174. package/dist/esm/utils/filterUtils.mjs +30 -0
  175. package/dist/esm/utils/filterUtils.mjs.map +1 -1
  176. package/dist/esm/utils/filterUtils.native.js +50 -0
  177. package/dist/esm/utils/filterUtils.native.js.map +1 -1
  178. package/dist/esm/utils/money.mjs +104 -0
  179. package/dist/esm/utils/money.mjs.map +1 -0
  180. package/dist/esm/utils/money.native.js +129 -0
  181. package/dist/esm/utils/money.native.js.map +1 -0
  182. package/dist/esm/utils/rowAccumulator.mjs +23 -0
  183. package/dist/esm/utils/rowAccumulator.mjs.map +1 -0
  184. package/dist/esm/utils/rowAccumulator.native.js +23 -0
  185. package/dist/esm/utils/rowAccumulator.native.js.map +1 -0
  186. package/dist/jsx/components/ColumnHeaderFilter/index.mjs +78 -42
  187. package/dist/jsx/components/ColumnHeaderFilter/index.mjs.map +1 -1
  188. package/dist/jsx/components/ColumnHeaderFilter/index.native.js +97 -51
  189. package/dist/jsx/components/ColumnHeaderFilter/index.native.js.map +1 -1
  190. package/dist/jsx/components/DataTable/DataTableCardControls.mjs +0 -1
  191. package/dist/jsx/components/DataTable/DataTableCardControls.mjs.map +1 -1
  192. package/dist/jsx/components/DataTable/DataTableCardControls.native.js +0 -1
  193. package/dist/jsx/components/DataTable/DataTableCardControls.native.js.map +1 -1
  194. package/dist/jsx/components/DataTable/DataTableMobileCards.mjs +99 -34
  195. package/dist/jsx/components/DataTable/DataTableMobileCards.mjs.map +1 -1
  196. package/dist/jsx/components/DataTable/DataTableMobileCards.native.js +139 -38
  197. package/dist/jsx/components/DataTable/DataTableMobileCards.native.js.map +1 -1
  198. package/dist/jsx/components/DataTable/DataTableRoot.mjs +33 -6
  199. package/dist/jsx/components/DataTable/DataTableRoot.mjs.map +1 -1
  200. package/dist/jsx/components/DataTable/DataTableRoot.native.js +29 -4
  201. package/dist/jsx/components/DataTable/DataTableRoot.native.js.map +1 -1
  202. package/dist/jsx/components/DataTable/DataTableStateContext.mjs.map +1 -1
  203. package/dist/jsx/components/DataTable/DataTableStateContext.native.js.map +1 -1
  204. package/dist/jsx/components/DataTable/index.native.js +59 -93
  205. package/dist/jsx/components/DataTable/index.native.js.map +1 -1
  206. package/dist/jsx/components/DataTableBulkActions/index.mjs +0 -4
  207. package/dist/jsx/components/DataTableBulkActions/index.mjs.map +1 -1
  208. package/dist/jsx/components/DataTableBulkActions/index.native.js +0 -4
  209. package/dist/jsx/components/DataTableBulkActions/index.native.js.map +1 -1
  210. package/dist/jsx/components/DataTableCell/index.mjs +17 -2
  211. package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
  212. package/dist/jsx/components/DataTableCell/index.native.js +16 -1
  213. package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
  214. package/dist/jsx/components/DataTableDateFilter/index.mjs +0 -7
  215. package/dist/jsx/components/DataTableDateFilter/index.mjs.map +1 -1
  216. package/dist/jsx/components/DataTableDateFilter/index.native.js +0 -7
  217. package/dist/jsx/components/DataTableDateFilter/index.native.js.map +1 -1
  218. package/dist/jsx/components/DataTableFilterButton/index.mjs +0 -1
  219. package/dist/jsx/components/DataTableFilterButton/index.mjs.map +1 -1
  220. package/dist/jsx/components/DataTableFilterButton/index.native.js +0 -1
  221. package/dist/jsx/components/DataTableFilterChips/FilterChip.mjs +3 -2
  222. package/dist/jsx/components/DataTableFilterChips/FilterChip.mjs.map +1 -1
  223. package/dist/jsx/components/DataTableFilterChips/FilterChip.native.js +4 -1
  224. package/dist/jsx/components/DataTableFilterChips/FilterChip.native.js.map +1 -1
  225. package/dist/jsx/components/DataTableFilters/index.mjs +0 -1
  226. package/dist/jsx/components/DataTableFilters/index.mjs.map +1 -1
  227. package/dist/jsx/components/DataTableFilters/index.native.js +0 -1
  228. package/dist/jsx/components/DataTableGrouping/index.mjs +0 -8
  229. package/dist/jsx/components/DataTableGrouping/index.mjs.map +1 -1
  230. package/dist/jsx/components/DataTableGrouping/index.native.js +0 -8
  231. package/dist/jsx/components/DataTableGrouping/index.native.js.map +1 -1
  232. package/dist/jsx/components/DataTablePagination/index.mjs +30 -14
  233. package/dist/jsx/components/DataTablePagination/index.mjs.map +1 -1
  234. package/dist/jsx/components/DataTablePagination/index.native.js +30 -14
  235. package/dist/jsx/components/DataTablePagination/index.native.js.map +1 -1
  236. package/dist/jsx/components/DataTableSearch/index.mjs +0 -3
  237. package/dist/jsx/components/DataTableSearch/index.mjs.map +1 -1
  238. package/dist/jsx/components/DataTableSearch/index.native.js +0 -3
  239. package/dist/jsx/components/DataTableSearch/index.native.js.map +1 -1
  240. package/dist/jsx/components/DataTableToolbar/index.mjs +0 -1
  241. package/dist/jsx/components/DataTableToolbar/index.mjs.map +1 -1
  242. package/dist/jsx/components/DataTableToolbar/index.native.js +0 -1
  243. package/dist/jsx/components/DataTableToolbar/index.native.js.map +1 -1
  244. package/dist/jsx/components/DataTableViewOptions/index.mjs +0 -5
  245. package/dist/jsx/components/DataTableViewOptions/index.mjs.map +1 -1
  246. package/dist/jsx/components/DataTableViewOptions/index.native.js +0 -5
  247. package/dist/jsx/components/DataTableViewOptions/index.native.js.map +1 -1
  248. package/dist/jsx/components/FilterManager.mjs +0 -1
  249. package/dist/jsx/components/FilterManager.mjs.map +1 -1
  250. package/dist/jsx/components/FilterManager.native.js +0 -1
  251. package/dist/jsx/components/FilterManager.native.js.map +1 -1
  252. package/dist/jsx/components/FilterOperatorValueEditor/index.mjs +2 -2
  253. package/dist/jsx/components/FilterOperatorValueEditor/index.mjs.map +1 -1
  254. package/dist/jsx/components/FilterOperatorValueEditor/index.native.js +2 -2
  255. package/dist/jsx/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  256. package/dist/jsx/components/TableInput/index.mjs +3 -2
  257. package/dist/jsx/components/TableInput/index.mjs.map +1 -1
  258. package/dist/jsx/components/TableInput/index.native.js +4 -2
  259. package/dist/jsx/components/TableInput/index.native.js.map +1 -1
  260. package/dist/jsx/hooks/useDataTable.mjs +24 -8
  261. package/dist/jsx/hooks/useDataTable.mjs.map +1 -1
  262. package/dist/jsx/hooks/useDataTable.native.js +28 -8
  263. package/dist/jsx/hooks/useDataTable.native.js.map +1 -1
  264. package/dist/jsx/index.js +3 -1
  265. package/dist/jsx/index.js.map +1 -1
  266. package/dist/jsx/index.mjs +3 -1
  267. package/dist/jsx/index.mjs.map +1 -1
  268. package/dist/jsx/index.native.js +12 -0
  269. package/dist/jsx/index.native.js.map +1 -1
  270. package/dist/jsx/shared/devWarn.mjs +43 -0
  271. package/dist/jsx/shared/devWarn.mjs.map +1 -0
  272. package/dist/jsx/shared/devWarn.native.js +84 -0
  273. package/dist/jsx/shared/devWarn.native.js.map +1 -0
  274. package/dist/jsx/utils/columnAlign.mjs +4 -1
  275. package/dist/jsx/utils/columnAlign.mjs.map +1 -1
  276. package/dist/jsx/utils/columnAlign.native.js +4 -1
  277. package/dist/jsx/utils/columnAlign.native.js.map +1 -1
  278. package/dist/jsx/utils/filterFields.mjs +5 -0
  279. package/dist/jsx/utils/filterFields.mjs.map +1 -1
  280. package/dist/jsx/utils/filterFields.native.js +5 -0
  281. package/dist/jsx/utils/filterFields.native.js.map +1 -1
  282. package/dist/jsx/utils/filterUtils.mjs +30 -0
  283. package/dist/jsx/utils/filterUtils.mjs.map +1 -1
  284. package/dist/jsx/utils/filterUtils.native.js +50 -0
  285. package/dist/jsx/utils/filterUtils.native.js.map +1 -1
  286. package/dist/jsx/utils/money.mjs +104 -0
  287. package/dist/jsx/utils/money.mjs.map +1 -0
  288. package/dist/jsx/utils/money.native.js +165 -0
  289. package/dist/jsx/utils/money.native.js.map +1 -0
  290. package/dist/jsx/utils/rowAccumulator.mjs +23 -0
  291. package/dist/jsx/utils/rowAccumulator.mjs.map +1 -0
  292. package/dist/jsx/utils/rowAccumulator.native.js +51 -0
  293. package/dist/jsx/utils/rowAccumulator.native.js.map +1 -0
  294. package/package.json +12 -12
  295. package/src/components/ColumnHeaderFilter/ColumnHeaderFilter.spec.tsx +142 -7
  296. package/src/components/ColumnHeaderFilter/ColumnHeaderFilter.stories.tsx +46 -9
  297. package/src/components/ColumnHeaderFilter/index.tsx +104 -47
  298. package/src/components/DataTable/DataTable.spec.tsx +5 -3
  299. package/src/components/DataTable/DataTable.stories.tsx +186 -14
  300. package/src/components/DataTable/DataTableCardControls.tsx +11 -13
  301. package/src/components/DataTable/DataTableMobileCards.tsx +162 -39
  302. package/src/components/DataTable/DataTableRoot.tsx +40 -14
  303. package/src/components/DataTable/DataTableStateContext.ts +7 -0
  304. package/src/components/DataTable/cardLayout.spec.tsx +19 -8
  305. package/src/components/DataTable/columnWidthLayout.spec.tsx +195 -0
  306. package/src/components/DataTable/dataTableMobileCards.spec.tsx +50 -2
  307. package/src/components/DataTable/headerSorting.spec.tsx +25 -0
  308. package/src/components/DataTable/index.native.tsx +80 -124
  309. package/src/components/DataTable/moneyColumn.spec.tsx +151 -0
  310. package/src/components/DataTable/nativeCardParity.spec.tsx +95 -0
  311. package/src/components/DataTable/rowAccumulator.spec.tsx +171 -0
  312. package/src/components/DataTableBulkActions/index.tsx +0 -4
  313. package/src/components/DataTableCell/index.tsx +28 -2
  314. package/src/components/DataTableColumnPinning/DataTableColumnPinning.stories.tsx +0 -4
  315. package/src/components/DataTableDateFilter/index.tsx +0 -7
  316. package/src/components/DataTableFilterButton/index.tsx +1 -1
  317. package/src/components/DataTableFilterChips/FilterChip.spec.tsx +6 -0
  318. package/src/components/DataTableFilterChips/FilterChip.tsx +3 -2
  319. package/src/components/DataTableFilters/index.tsx +1 -1
  320. package/src/components/DataTableGrouping/DataTableGrouping.stories.tsx +0 -4
  321. package/src/components/DataTableGrouping/index.tsx +1 -13
  322. package/src/components/DataTablePagination/DataTablePagination.stories.tsx +14 -1
  323. package/src/components/DataTablePagination/DataTablePagination.width.spec.tsx +51 -0
  324. package/src/components/DataTablePagination/index.tsx +60 -16
  325. package/src/components/DataTableSearch/index.tsx +2 -3
  326. package/src/components/DataTableToolbar/DataTableToolbar.stories.tsx +2 -4
  327. package/src/components/DataTableToolbar/index.tsx +1 -5
  328. package/src/components/DataTableViewOptions/index.tsx +2 -13
  329. package/src/components/FilterManager.tsx +1 -3
  330. package/src/components/FilterOperatorValueEditor/FilterOperatorValueEditor.spec.tsx +1 -1
  331. package/src/components/FilterOperatorValueEditor/FilterOperatorValueEditor.stories.tsx +1 -0
  332. package/src/components/FilterOperatorValueEditor/index.tsx +10 -2
  333. package/src/components/TableInput/index.native.tsx +3 -2
  334. package/src/components/TableInput/index.tsx +3 -2
  335. package/src/hooks/useDataTable.ts +43 -7
  336. package/src/index.ts +13 -0
  337. package/src/shared/devWarn.ts +97 -0
  338. package/src/types.ts +71 -2
  339. package/src/utils/columnAlign.ts +9 -1
  340. package/src/utils/filterFields.ts +23 -1
  341. package/src/utils/filterUtils.ts +50 -1
  342. package/src/utils/money.spec.ts +156 -0
  343. package/src/utils/money.ts +202 -0
  344. package/src/utils/rowAccumulator.ts +136 -0
  345. package/types/components/ColumnHeaderFilter/index.d.ts +9 -2
  346. package/types/components/ColumnHeaderFilter/index.d.ts.map +1 -1
  347. package/types/components/DataTable/DataTableCardControls.d.ts.map +1 -1
  348. package/types/components/DataTable/DataTableMobileCards.d.ts +42 -8
  349. package/types/components/DataTable/DataTableMobileCards.d.ts.map +1 -1
  350. package/types/components/DataTable/DataTableRoot.d.ts +1 -1
  351. package/types/components/DataTable/DataTableRoot.d.ts.map +1 -1
  352. package/types/components/DataTable/DataTableStateContext.d.ts +7 -0
  353. package/types/components/DataTable/DataTableStateContext.d.ts.map +1 -1
  354. package/types/components/DataTable/index.native.d.ts +1 -1
  355. package/types/components/DataTable/index.native.d.ts.map +1 -1
  356. package/types/components/DataTableBulkActions/index.d.ts.map +1 -1
  357. package/types/components/DataTableCell/index.d.ts.map +1 -1
  358. package/types/components/DataTableDateFilter/index.d.ts.map +1 -1
  359. package/types/components/DataTableFilterChips/FilterChip.d.ts.map +1 -1
  360. package/types/components/DataTableGrouping/index.d.ts.map +1 -1
  361. package/types/components/DataTablePagination/index.d.ts.map +1 -1
  362. package/types/components/DataTableSearch/index.d.ts.map +1 -1
  363. package/types/components/DataTableToolbar/index.d.ts.map +1 -1
  364. package/types/components/DataTableViewOptions/index.d.ts.map +1 -1
  365. package/types/components/FilterManager.d.ts.map +1 -1
  366. package/types/components/FilterOperatorValueEditor/index.d.ts.map +1 -1
  367. package/types/components/TableInput/index.d.ts.map +1 -1
  368. package/types/components/TableInput/index.native.d.ts.map +1 -1
  369. package/types/hooks/useDataTable.d.ts +1 -1
  370. package/types/hooks/useDataTable.d.ts.map +1 -1
  371. package/types/index.d.ts +3 -0
  372. package/types/index.d.ts.map +1 -1
  373. package/types/shared/devWarn.d.ts +50 -0
  374. package/types/shared/devWarn.d.ts.map +1 -0
  375. package/types/types.d.ts +71 -3
  376. package/types/types.d.ts.map +1 -1
  377. package/types/utils/columnAlign.d.ts.map +1 -1
  378. package/types/utils/filterFields.d.ts.map +1 -1
  379. package/types/utils/filterUtils.d.ts +1 -1
  380. package/types/utils/filterUtils.d.ts.map +1 -1
  381. package/types/utils/money.d.ts +94 -0
  382. package/types/utils/money.d.ts.map +1 -0
  383. package/types/utils/rowAccumulator.d.ts +107 -0
  384. package/types/utils/rowAccumulator.d.ts.map +1 -0
@@ -0,0 +1,151 @@
1
+ /**
2
+ * The money column type end to end (MPO-252).
3
+ *
4
+ * A Frappe Currency field arrives as a decimal STRING, and before this the
5
+ * kit had no type for that, so it fell to "text". Three consequences, all
6
+ * asserted here: sorting was silently wrong ("9.00" above "10.00"), the
7
+ * filter offered `contains` instead of `isGreaterThan`, and formatting could
8
+ * only read a column-level currency constant even though a ledger mixes
9
+ * currencies row by row.
10
+ */
11
+
12
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
13
+ import type React from "react";
14
+ import { screen, waitFor } from "@testing-library/react";
15
+ import userEvent from "@testing-library/user-event";
16
+ import { describe, expect, it } from "vitest";
17
+ import type { DataTableColumn } from "../../types";
18
+ import { applyFilterToValue } from "../../utils/filterUtils";
19
+ import { mapFieldToFilterType, operatorsForFilterType } from "../../utils/filterFields";
20
+ import { getDefaultColumnAlign } from "../../utils/columnAlign";
21
+ import { DataTable } from "./index";
22
+
23
+ interface Posting {
24
+ id: string;
25
+ payee: string;
26
+ amount: string;
27
+ currency: string;
28
+ }
29
+
30
+ const postings: Posting[] = [
31
+ { id: "P-1", payee: "Nine", amount: "9.00", currency: "USD" },
32
+ { id: "P-2", payee: "Ten", amount: "10.00", currency: "USD" },
33
+ { id: "P-3", payee: "Rupees", amount: "250.75", currency: "INR" },
34
+ { id: "P-4", payee: "Refund", amount: "-455.87", currency: "USD" },
35
+ ];
36
+
37
+ const columns: DataTableColumn<Posting>[] = [
38
+ { id: "payee", accessorKey: "payee", header: "Payee" },
39
+ {
40
+ id: "amount",
41
+ accessorKey: "amount",
42
+ header: "Amount",
43
+ field: "money",
44
+ // Safe to turn on now: the money type carries an exact comparator.
45
+ sortable: true,
46
+ fieldProps: { currencyField: "currency", locale: "en-US" },
47
+ },
48
+ ];
49
+
50
+ describe("money column type", () => {
51
+ it("maps field money → filter type money, right-aligned by default", () => {
52
+ expect(mapFieldToFilterType("money")).toBe("money");
53
+ expect(
54
+ getDefaultColumnAlign({ id: "amount", field: "money" }),
55
+ "a money column right-aligns without a column-level currency constant",
56
+ ).toBe("right");
57
+ });
58
+
59
+ it("offers the numeric filter operators, range included", () => {
60
+ const operators = operatorsForFilterType("money");
61
+ expect(operators).toContain("isLessThan");
62
+ expect(operators).toContain("isGreaterThan");
63
+ expect(operators).toContain("isBetween");
64
+ expect(operators, "text operators are what a money column used to get").not.toContain(
65
+ "contains",
66
+ );
67
+ });
68
+
69
+ it("filters numerically on decimal strings", () => {
70
+ const gt = { operator: "isGreaterThan" as const, value: "9.5" };
71
+ expect(applyFilterToValue("10.00", gt, "money")).toBe(true);
72
+ expect(applyFilterToValue("9.00", gt, "money")).toBe(false);
73
+ // The same rows through the "text" fallback are the defect: "10.00" is
74
+ // greater than "9.5" as a number and LESS as a string, so the row that
75
+ // should match is the one that gets dropped.
76
+ expect(applyFilterToValue("10.00", gt, "text")).toBe(false);
77
+
78
+ const between = { operator: "isBetween" as const, value: null, values: ["0", "100"] };
79
+ expect(applyFilterToValue("10.00", between, "money")).toBe(true);
80
+ expect(applyFilterToValue("250.75", between, "money")).toBe(false);
81
+ expect(applyFilterToValue("-455.87", between, "money")).toBe(false);
82
+
83
+ const is = { operator: "is" as const, value: "10" };
84
+ expect(applyFilterToValue("10.00", is, "money"), "scale does not change equality").toBe(true);
85
+ });
86
+
87
+ it("sorts numerically in the table, not lexically", async () => {
88
+ const user = userEvent.setup();
89
+ const { container } = renderWithProviders(
90
+ <DataTable
91
+ data={postings}
92
+ columns={columns}
93
+ layout="table"
94
+ enablePagination={false}
95
+ readOnly
96
+ />,
97
+ );
98
+ await waitFor(() => expect(screen.getByText("$9.00")).toBeTruthy());
99
+
100
+ await user.click(screen.getByText("Amount"));
101
+ await waitFor(() => {
102
+ const payees = Array.from(container.querySelectorAll("tbody tr"))
103
+ .map((row) => row.querySelector("td")?.textContent)
104
+ .filter((text) => !!text);
105
+ // Ascending: -455.87, 9.00, 10.00, 250.75. Lexically it would have been
106
+ // -455.87, 10.00, 250.75, 9.00 — with 9.00 LAST.
107
+ expect(payees).toEqual(["Refund", "Nine", "Ten", "Rupees"]);
108
+ });
109
+ });
110
+
111
+ it("formats from the ROW's currency, not only a column-level constant", async () => {
112
+ renderWithProviders(
113
+ <DataTable
114
+ data={postings}
115
+ columns={columns}
116
+ layout="table"
117
+ enablePagination={false}
118
+ readOnly
119
+ />,
120
+ );
121
+ await waitFor(() => expect(screen.getByText("$9.00")).toBeTruthy());
122
+ expect(screen.getByText("$10.00")).toBeTruthy();
123
+ expect(screen.getByText("-$455.87")).toBeTruthy();
124
+ // One row carries INR and renders in INR, in the same column, at the same
125
+ // time — two currencies are two figures and neither converts.
126
+ expect(screen.getByText("₹250.75")).toBeTruthy();
127
+ });
128
+ });
129
+
130
+ describe("the MoneyColumn story", () => {
131
+ it("renders mixed currencies and a per-currency footer total", async () => {
132
+ // The story is the documented example. Rendering it here means it cannot
133
+ // rot into something that formats one currency for every row.
134
+ const { MoneyColumn } = await import("./DataTable.stories");
135
+ renderWithProviders(<>{(MoneyColumn.render as () => React.ReactNode)()}</>);
136
+
137
+ await waitFor(() => expect(screen.getByText("Coffee roaster")).toBeTruthy());
138
+ expect(screen.getByText("$9.00")).toBeTruthy();
139
+ expect(screen.getByText("₹250.75")).toBeTruthy();
140
+ // Two totals, not one: USD 9.00 + 10.00 - 455.87 + 9.99, INR 250.75 +
141
+ // 1200.00. Nothing converts between them.
142
+ expect(screen.getByText("-$426.88")).toBeTruthy();
143
+ expect(screen.getByText("₹1,450.75")).toBeTruthy();
144
+ for (const total of ["-$426.88", "₹1,450.75"]) {
145
+ expect(screen.getByText(total).closest("td")?.getAttribute("data-align")).toBe("right");
146
+ const content = screen.getByText(total).closest('[data-cell-align="right"]') as HTMLElement;
147
+ expect(content).toBeTruthy();
148
+ expect(content.style.textAlign).toBe("right");
149
+ }
150
+ });
151
+ });
@@ -0,0 +1,95 @@
1
+ /**
2
+ * One card renderer, two platforms.
3
+ *
4
+ * DataTableRoot.tsx (web) and index.native.tsx (native) are separate roots
5
+ * because native virtualizes through FlashList. The CARD LIST is not part
6
+ * of that split — a card has no platform-specific anatomy — so both roots
7
+ * render the same DataTableMobileCards rather than each drawing a card.
8
+ *
9
+ * MPO-285 shared the field RULE (classifyCardField, getColumnLabel) while
10
+ * leaving two copies of the JSX, and the two had already drifted: the
11
+ * native copy hardcoded its surface, and shipped without zebra striping,
12
+ * the selection checkbox, the empty states, and the TableCellContext that
13
+ * makes a field render chromeless. Sharing the rule alone does not stop
14
+ * that, because a new slot still has to be added twice.
15
+ *
16
+ * These assertions read source rather than render: the native root pulls in
17
+ * FlashList and the suite runs under happy-dom, so index.native.tsx is
18
+ * never the module a spec resolves. What the card DOES on native is covered
19
+ * by dataTableMobileCards.spec.tsx, which now describes both platforms.
20
+ */
21
+
22
+ import { readdirSync, readFileSync } from "node:fs";
23
+ import { resolve } from "node:path";
24
+ import { describe, expect, it } from "vitest";
25
+
26
+ const DIR = resolve(__dirname);
27
+
28
+ function read(file: string): string {
29
+ return readFileSync(resolve(DIR, file), "utf-8");
30
+ }
31
+
32
+ /** A `<Component ... />` element at a call site, as text. */
33
+ function callSite(source: string, component: string): string {
34
+ const start = source.indexOf(`<${component}`);
35
+ if (start === -1) return "";
36
+ const end = source.indexOf("/>", start);
37
+ return end === -1 ? "" : source.slice(start, end);
38
+ }
39
+
40
+ /** Prop names that call site passes. */
41
+ function propNames(source: string, component: string): Set<string> {
42
+ const names = new Set<string>();
43
+ for (const match of callSite(source, component).matchAll(/(?:^|\s)([A-Za-z][A-Za-z0-9]*)=\{/g)) {
44
+ names.add(match[1]!);
45
+ }
46
+ return names;
47
+ }
48
+
49
+ const cardCallSite = (source: string) => callSite(source, "DataTableMobileCards");
50
+
51
+ describe("DataTable card renderer parity", () => {
52
+ it("the native root renders DataTableMobileCards and carries no card of its own", () => {
53
+ const native = read("index.native.tsx");
54
+ expect(native).toMatch(
55
+ /import \{[^}]*DataTableMobileCards[^}]*\} from "\.\/DataTableMobileCards"/,
56
+ );
57
+ expect(cardCallSite(native)).not.toBe("");
58
+ expect(native).not.toMatch(/renderMobileCard/);
59
+ });
60
+
61
+ it("the native root reaches the card field rule through the component, not directly", () => {
62
+ // MPO-285 left index.native.tsx importing classifyCardField/getColumnLabel
63
+ // to feed its own JSX. With the JSX shared there is nothing left to feed:
64
+ // an import here again would mean a second card had grown back.
65
+ const native = read("index.native.tsx");
66
+ expect(native).not.toMatch(/\bclassifyCardField\b/);
67
+ expect(native).not.toMatch(/\bgetColumnLabel\b/);
68
+ });
69
+
70
+ it("exactly one file in the component draws a card", () => {
71
+ const drawers = readdirSync(DIR)
72
+ .filter(
73
+ (file) => file.endsWith(".tsx") && !file.includes(".spec.") && !file.includes(".stories."),
74
+ )
75
+ .filter((file) => read(file).includes('testID="data-table-card"'));
76
+ expect(drawers).toEqual(["DataTableMobileCards.tsx"]);
77
+ });
78
+
79
+ // Both roots hand their row views the same contract. A prop web passes and
80
+ // native does not is how the card lost selection and the empty states in
81
+ // the first place, so it is the drift worth failing a build over.
82
+ it.each(["DataTableMobileCards", "DataTableDocumentList"])(
83
+ "native passes every %s prop web passes",
84
+ (component) => {
85
+ const web = propNames(read("DataTableRoot.tsx"), component);
86
+ const native = propNames(read("index.native.tsx"), component);
87
+ expect(web.size).toBeGreaterThan(0);
88
+ expect([...web].filter((prop) => !native.has(prop)).sort()).toEqual([]);
89
+ },
90
+ );
91
+
92
+ it("native hands the card list its press props, so a card is pressable there too", () => {
93
+ expect(cardCallSite(read("index.native.tsx"))).toMatch(/getRowPressProps/);
94
+ });
95
+ });
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Row accumulator — a running balance across pages (MPO-250).
3
+ *
4
+ * The claim under test: a column can be a function of the row AND every row
5
+ * before it, the fold runs BEFORE pagination, and page two therefore
6
+ * continues rather than restarting from zero. That is what no table in the
7
+ * kit could do — `data` is the only channel and a `cell` renderer sees one
8
+ * row — and it is the default column in every register, statement and ledger.
9
+ */
10
+
11
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
12
+ import { screen, waitFor } from "@testing-library/react";
13
+ import userEvent from "@testing-library/user-event";
14
+ import { describe, expect, it } from "vitest";
15
+ import type { DataTableColumn, RowAccumulator } from "../../types";
16
+ import { accumulateRows } from "../../utils/rowAccumulator";
17
+ import { DataTable } from "./index";
18
+
19
+ interface Posting {
20
+ id: string;
21
+ date: string;
22
+ payee: string;
23
+ /** Cents — integers, so this spec asserts the FOLD, not decimal arithmetic. */
24
+ amount: number;
25
+ /** Derived by the accumulator. Declared here so a column can name it. */
26
+ running?: number;
27
+ }
28
+
29
+ /** Twelve postings of +100 each: the running balance is 100, 200, … 1200. */
30
+ const postings: Posting[] = Array.from({ length: 12 }, (_, i) => ({
31
+ id: `P-${String(i + 1).padStart(2, "0")}`,
32
+ date: `2026-01-${String(i + 1).padStart(2, "0")}`,
33
+ payee: `Payee ${String.fromCharCode(90 - i)}`, // Z, Y, X … — reverse of date order
34
+ amount: 100,
35
+ }));
36
+
37
+ const runningBalance: RowAccumulator<Posting, number> = {
38
+ initial: 0,
39
+ reduce: (carried, row) => carried + row.amount,
40
+ derive: (carried) => ({ running: carried }),
41
+ order: (a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0),
42
+ total: (carried) => carried,
43
+ };
44
+
45
+ const columns: DataTableColumn<Posting>[] = [
46
+ { id: "id", accessorKey: "id", header: "ID" },
47
+ { id: "payee", accessorKey: "payee", header: "Payee", sortable: true },
48
+ { id: "amount", accessorKey: "amount", header: "Amount" },
49
+ // Not sortable: sorting a fold column reorders it without refolding, so the
50
+ // figures stop reading down the column as a progression. Rendered with a
51
+ // marker so an assertion can tell a BALANCE of 100 from an AMOUNT of 100.
52
+ {
53
+ id: "running",
54
+ accessorKey: "running",
55
+ header: "Balance",
56
+ sortable: false,
57
+ cell: ({ row }) => `bal ${row.original.running}`,
58
+ },
59
+ ];
60
+
61
+ describe("accumulateRows", () => {
62
+ it("folds in the declared order and returns the rows in it", () => {
63
+ const shuffled = [...postings].reverse();
64
+ const { rows, total } = accumulateRows(shuffled, runningBalance);
65
+ expect(rows.map((r) => r.id)).toEqual(postings.map((p) => p.id));
66
+ expect(rows.map((r) => r.running)).toEqual([
67
+ 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200,
68
+ ]);
69
+ expect(total, "the fold read once at the end is the footer total").toBe(1200);
70
+ });
71
+
72
+ it("keeps the input array identity when there is no accumulator", () => {
73
+ const { rows, total } = accumulateRows(postings);
74
+ expect(rows).toBe(postings);
75
+ expect(total).toBeUndefined();
76
+ });
77
+
78
+ it("calls a factory seed once per fold, so a mutable seed cannot leak", () => {
79
+ const perCurrency: RowAccumulator<Posting, Record<string, number>> = {
80
+ initial: () => ({}),
81
+ reduce: (carried, row) => {
82
+ carried["USD"] = (carried["USD"] ?? 0) + row.amount;
83
+ return carried;
84
+ },
85
+ derive: (carried) => ({ running: carried["USD"] }),
86
+ };
87
+ const first = accumulateRows(postings, perCurrency);
88
+ const second = accumulateRows(postings, perCurrency);
89
+ expect(first.rows[11]!.running).toBe(1200);
90
+ expect(second.rows[11]!.running, "the second fold starts from a fresh seed").toBe(1200);
91
+ });
92
+ });
93
+
94
+ describe("DataTable rowAccumulator", () => {
95
+ it("continues the running balance onto page two instead of restarting", async () => {
96
+ const user = userEvent.setup();
97
+ renderWithProviders(
98
+ <DataTable
99
+ data={postings}
100
+ columns={columns}
101
+ rowAccumulator={runningBalance}
102
+ layout="table"
103
+ initialState={{ pagination: { pageIndex: 0, pageSize: 5 } }}
104
+ />,
105
+ );
106
+
107
+ // Page one: the fold's first five values.
108
+ await waitFor(() => expect(screen.getByText("P-01")).toBeTruthy());
109
+ expect(screen.getByText("bal 100")).toBeTruthy();
110
+ expect(screen.getByText("bal 500")).toBeTruthy();
111
+ expect(screen.queryByText("bal 600")).toBeNull();
112
+
113
+ // Page two CONTINUES. Before MPO-250 the only way to get a balance column
114
+ // was a per-row cell renderer, which would have restarted at 100 here.
115
+ await user.click(screen.getByLabelText("Go to next page"));
116
+ await waitFor(() => expect(screen.getByText("P-06")).toBeTruthy());
117
+ expect(screen.getByText("bal 600"), "page two starts where page one ended").toBeTruthy();
118
+ expect(screen.getByText("bal 1000")).toBeTruthy();
119
+ expect(screen.queryByText("bal 100"), "page one's first balance is gone").toBeNull();
120
+ });
121
+
122
+ it("does not refold when the user sorts on another column", async () => {
123
+ const user = userEvent.setup();
124
+ renderWithProviders(
125
+ <DataTable
126
+ data={postings}
127
+ columns={columns}
128
+ rowAccumulator={runningBalance}
129
+ layout="table"
130
+ initialState={{ pagination: { pageIndex: 0, pageSize: 12 } }}
131
+ />,
132
+ );
133
+ await waitFor(() => expect(screen.getByText("P-01")).toBeTruthy());
134
+
135
+ // Payee runs backwards against date, so a payee sort puts the LAST
136
+ // posting first. Its balance stays 1200 — the fold order is the date
137
+ // order it was computed in, and sorting reorders rows for display only.
138
+ await user.click(screen.getByText("Payee"));
139
+ await waitFor(() => expect(screen.getByText("bal 1200")).toBeTruthy());
140
+ const rendered = screen.getAllByText(/^bal \d+$/).map((el) => el.textContent);
141
+ expect(rendered, "every folded figure is still on screen, attached to its own row").toContain(
142
+ "bal 1200",
143
+ );
144
+ expect(rendered).toContain("bal 100");
145
+ });
146
+ });
147
+
148
+ describe("the RunningBalance story", () => {
149
+ it("renders the register across two pages with figures that continue", async () => {
150
+ // The story is the documented example (AC: MPO-250). Rendering it here
151
+ // means it cannot rot into something that does not paginate.
152
+ const { RunningBalance } = await import("./DataTable.stories");
153
+ const user = userEvent.setup();
154
+ renderWithProviders(<>{(RunningBalance.render as () => React.ReactNode)()}</>);
155
+
156
+ await waitFor(() => expect(screen.getByText("Opening deposit")).toBeTruthy());
157
+ // Page one is P-01..P-05 and ends on Utilities: 2500.00 - 1800.00 - 84.50
158
+ // + 4123.00 - 146.00.
159
+ expect(screen.getByText("$4,592.50")).toBeTruthy();
160
+ expect(screen.queryByText("$4,573.75"), "page two's first balance is not here yet").toBeNull();
161
+
162
+ await user.click(screen.getByLabelText("Go to next page"));
163
+ await waitFor(() => expect(screen.getByText("Coffee roaster")).toBeTruthy());
164
+ // Page two picks the fold up rather than restarting: -18.75 on 4592.50.
165
+ expect(screen.getByText("$4,573.75"), "page two continues the fold").toBeTruthy();
166
+ expect(screen.getByText("$5,005.85")).toBeTruthy();
167
+ // The footer total is the same fold, read once at the end — every posting,
168
+ // not just this page.
169
+ expect(screen.getAllByText("$6,078.85").length).toBeGreaterThan(0);
170
+ });
171
+ });
@@ -159,7 +159,6 @@ export function DataTableBulkActions<T = any>(props: DataTableBulkActionsProps<T
159
159
  {showSelectAllMatching && (
160
160
  <Button
161
161
  size={knobProps.sizeToken}
162
- variant="outlined"
163
162
  onPress={() => ctx?.table.toggleAllRowsSelected(true)}
164
163
  aria-label={`Select all ${matchingCount} matching rows`}
165
164
  {...knobProps.borderRadius}
@@ -173,7 +172,6 @@ export function DataTableBulkActions<T = any>(props: DataTableBulkActionsProps<T
173
172
  <Button
174
173
  key={action.id}
175
174
  size={knobProps.sizeToken}
176
- variant="outlined"
177
175
  theme={action.variant === "destructive" ? "red" : undefined}
178
176
  disabled={action.disabled}
179
177
  // DG-ST-03: reason renders via the Button explain affordance;
@@ -199,7 +197,6 @@ export function DataTableBulkActions<T = any>(props: DataTableBulkActionsProps<T
199
197
 
200
198
  <Button
201
199
  size={knobProps.sizeToken}
202
- variant="outlined"
203
200
  onPress={onClearSelection}
204
201
  aria-label="Clear selection"
205
202
  {...knobProps.borderRadius}
@@ -265,7 +262,6 @@ function BulkActionsOverflowMenu<T = any>({
265
262
  >
266
263
  <Button
267
264
  size={knobProps.sizeToken}
268
- variant="outlined"
269
265
  icon={<DotsThreeIcon size={iconPx} />}
270
266
  aria-label="More bulk actions"
271
267
  {...knobProps.borderRadius}
@@ -53,9 +53,10 @@ import { Star } from "@phosphor-icons/react";
53
53
  import { RichTextEditor } from "@multiplatform.one/rich-text";
54
54
  import { useResolvedKnobs, ensureCompositeFocusRing } from "@multiplatform.one/theme";
55
55
  import React, { useCallback, useEffect, useRef, useState } from "react";
56
- import { View, isWeb } from "tamagui";
56
+ import { Text, View, isWeb } from "tamagui";
57
57
  import type { DataTableFieldProps, DataTableField } from "../../types";
58
58
  import { alignToFlex, getDefaultColumnAlign } from "../../utils/columnAlign";
59
+ import { formatMoney } from "../../utils/money";
59
60
  import { t } from "../../shared/t";
60
61
 
61
62
  export interface DataTableCellProps {
@@ -125,7 +126,9 @@ export function DataTableCell({
125
126
  max,
126
127
  step,
127
128
  precision,
128
- currency: _currency,
129
+ currency,
130
+ currencyField,
131
+ locale,
129
132
  defaultCountry,
130
133
  otpLength,
131
134
  mentions,
@@ -171,6 +174,29 @@ export function DataTableCell({
171
174
  );
172
175
  }
173
176
 
177
+ // A money cell is DISPLAY, in both modes. A Currency field is a decimal
178
+ // STRING (a float would round it) and a grid row is not where a ledger
179
+ // figure gets retyped — money is edited through the form surfaces (MPO-252).
180
+ //
181
+ // Formatting reads the ROW's own currency first, because a ledger genuinely
182
+ // mixes currencies row by row; the column-level `currency` constant is the
183
+ // fallback for rows carrying none. Nothing here converts between codes —
184
+ // two currencies are two figures. See utils/money.ts.
185
+ if (field === "money") {
186
+ const rowCurrency = currencyField
187
+ ? (rowData as Record<string, unknown> | undefined)?.[currencyField]
188
+ : undefined;
189
+ const resolvedCurrency =
190
+ typeof rowCurrency === "string" && rowCurrency ? rowCurrency : currency;
191
+ return (
192
+ <CellWrapper justify={cellJustify}>
193
+ <Text numberOfLines={1} aria-label={cellName}>
194
+ {formatMoney(value, resolvedCurrency, locale)}
195
+ </Text>
196
+ </CellWrapper>
197
+ );
198
+ }
199
+
174
200
  // Read-only cells route through FieldDisplay (lightweight, view-only) rather
175
201
  // than mounting the full Edit component with disabled={true}. This keeps
176
202
  // large tables performant and gives heavy fields a purpose-built compact
@@ -227,7 +227,6 @@ function BasicPinningExample() {
227
227
  onPress={() => pinning.togglePin(header.column.id, "left")}
228
228
  disabled={!isPinned && position !== "left" && !pinning.canPin("left")}
229
229
  circular
230
- variant={position === "left" ? "outlined" : undefined}
231
230
  aria-label={
232
231
  isPinned && position === "left"
233
232
  ? `Unpin ${headerLabel} column`
@@ -240,7 +239,6 @@ function BasicPinningExample() {
240
239
  onPress={() => pinning.togglePin(header.column.id, "right")}
241
240
  disabled={!isPinned && position !== "right" && !pinning.canPin("right")}
242
241
  circular
243
- variant={position === "right" ? "outlined" : undefined}
244
242
  aria-label={
245
243
  isPinned && position === "right"
246
244
  ? `Unpin ${headerLabel} column`
@@ -483,7 +481,6 @@ export const MaxPinnedColumns: Story = {
483
481
  size="$2"
484
482
  onPress={() => pinning.togglePin(column.id, "left")}
485
483
  disabled={!pinning.isPinned(column.id) && !pinning.canPin("left")}
486
- variant={pinning.getPinPosition(column.id) === "left" ? "outlined" : undefined}
487
484
  >
488
485
  {pinning.getPinPosition(column.id) === "left" ? "Unpin" : "Pin Left"}
489
486
  </Button>
@@ -491,7 +488,6 @@ export const MaxPinnedColumns: Story = {
491
488
  size="$2"
492
489
  onPress={() => pinning.togglePin(column.id, "right")}
493
490
  disabled={!pinning.isPinned(column.id) && !pinning.canPin("right")}
494
- variant={pinning.getPinPosition(column.id) === "right" ? "outlined" : undefined}
495
491
  >
496
492
  {pinning.getPinPosition(column.id) === "right" ? "Unpin" : "Pin Right"}
497
493
  </Button>
@@ -326,7 +326,6 @@ export function DataTableDateFilter({
326
326
  <Popover.Trigger asChild>
327
327
  <Button
328
328
  size={knobProps.sizeToken}
329
- variant="outlined"
330
329
  disabled={disabled}
331
330
  icon={<CalendarIcon size={iconPx} />}
332
331
  justifyContent="flex-start"
@@ -370,7 +369,6 @@ export function DataTableDateFilter({
370
369
  {value && (
371
370
  <Button
372
371
  size={knobProps.sizeToken}
373
- variant="outlined"
374
372
  onPress={handleClear}
375
373
  icon={<XIcon size={iconPx} />}
376
374
  >
@@ -391,7 +389,6 @@ export function DataTableDateFilter({
391
389
  <Button
392
390
  key={preset.id}
393
391
  size={knobProps.sizeToken}
394
- variant="outlined"
395
392
  justifyContent="flex-start"
396
393
  onPress={() => handlePresetSelect(preset)}
397
394
  {...(activePresetId === preset.id
@@ -418,7 +415,6 @@ export function DataTableDateFilter({
418
415
  <Button
419
416
  flex={1}
420
417
  size={knobProps.sizeToken}
421
- variant="outlined"
422
418
  onPress={() => handleCustomDateSelect(new Date().toISOString(), "is")}
423
419
  >
424
420
  {t("Today")}
@@ -426,7 +422,6 @@ export function DataTableDateFilter({
426
422
  <Button
427
423
  flex={1}
428
424
  size={knobProps.sizeToken}
429
- variant="outlined"
430
425
  onPress={() =>
431
426
  handleCustomDateSelect(
432
427
  new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
@@ -447,7 +442,6 @@ export function DataTableDateFilter({
447
442
  <Button
448
443
  flex={1}
449
444
  size={knobProps.sizeToken}
450
- variant="outlined"
451
445
  onPress={() =>
452
446
  handleDateRangeSelect(
453
447
  new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
@@ -460,7 +454,6 @@ export function DataTableDateFilter({
460
454
  <Button
461
455
  flex={1}
462
456
  size={knobProps.sizeToken}
463
- variant="outlined"
464
457
  onPress={() =>
465
458
  handleDateRangeSelect(
466
459
  new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(),
@@ -348,7 +348,7 @@ export function DataTableFilterButton() {
348
348
 
349
349
  {editingExisting && (
350
350
  <XStack justifyContent="flex-end">
351
- <Button size="$2" variant="outlined" onPress={handleRemoveCurrent}>
351
+ <Button size="$2" onPress={handleRemoveCurrent}>
352
352
  {t("Remove filter")}
353
353
  </Button>
354
354
  </XStack>
@@ -18,6 +18,12 @@ describe("FilterChip", () => {
18
18
  renderWithProviders(<FilterChip label="Email: contains corp" onRemove={vi.fn()} />);
19
19
  expect(screen.getByText("Email: contains corp")).toBeTruthy();
20
20
  expect(screen.getByRole("button", { name: "Remove" })).toBeTruthy();
21
+ const fill = screen
22
+ .getByRole("button", { name: "Remove" })
23
+ .querySelector("svg")
24
+ ?.getAttribute("fill");
25
+ expect(fill).toBeTruthy();
26
+ expect(fill?.startsWith("$")).toBe(false);
21
27
  });
22
28
 
23
29
  it("with onPress the pill is an edit button; both focusables are tab stops (LC-71)", () => {
@@ -16,7 +16,7 @@ import {
16
16
  pressTargetStyle,
17
17
  useResolvedKnobs,
18
18
  } from "@multiplatform.one/theme";
19
- import { View, styled } from "tamagui";
19
+ import { View, styled, useTheme } from "tamagui";
20
20
  import { t } from "../../shared/t";
21
21
 
22
22
  export interface FilterChipProps {
@@ -49,6 +49,7 @@ const DismissRing = styled(View, {
49
49
 
50
50
  export function FilterChip({ label, onRemove, onPress }: FilterChipProps) {
51
51
  const { knobProps } = useResolvedKnobs();
52
+ const theme = useTheme();
52
53
  if (typeof document !== "undefined") ensureCompositeFocusRing();
53
54
 
54
55
  const resolvedSize = (knobProps.sizeToken as ChipSize) || "$3";
@@ -114,7 +115,7 @@ export function FilterChip({ label, onRemove, onPress }: FilterChipProps) {
114
115
  }
115
116
  >
116
117
  <DismissRing className="mp-chip-dismiss-ring" borderRadius={knobProps.pointy ? 0 : 1000}>
117
- <XIcon size={iconSize} color="$color12" />
118
+ <XIcon size={iconSize} color={theme.color12?.val ?? "currentColor"} />
118
119
  </DismissRing>
119
120
  </DismissButton>
120
121
  </ChipFrame>
@@ -255,7 +255,7 @@ function FilterBuilder({
255
255
  ) : null}
256
256
 
257
257
  <XStack gap="$2" justifyContent="flex-end">
258
- <Button size="$3" compact variant="outlined" onPress={onCancel}>
258
+ <Button size="$3" compact onPress={onCancel}>
259
259
  {t("Cancel")}
260
260
  </Button>
261
261
  <Button