@multiplatform.one/table 6.4.2 → 6.6.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 (295) hide show
  1. package/dist/cjs/components/ChildTable/index.native.js +1 -1
  2. package/dist/cjs/components/ChildTable/index.native.js.map +1 -1
  3. package/dist/cjs/components/ColumnHeaderFilter/index.cjs +1 -0
  4. package/dist/cjs/components/ColumnHeaderFilter/index.native.js +2 -1
  5. package/dist/cjs/components/ColumnHeaderFilter/index.native.js.map +1 -1
  6. package/dist/cjs/components/DataTable/DataTableCardControls.native.js +1 -1
  7. package/dist/cjs/components/DataTable/DataTableCardControls.native.js.map +1 -1
  8. package/dist/cjs/components/DataTable/DataTableContext.native.js.map +1 -1
  9. package/dist/cjs/components/DataTable/DataTableMobileCards.cjs +9 -3
  10. package/dist/cjs/components/DataTable/DataTableMobileCards.native.js +12 -3
  11. package/dist/cjs/components/DataTable/DataTableMobileCards.native.js.map +1 -1
  12. package/dist/cjs/components/DataTable/DataTableRoot.cjs +133 -13
  13. package/dist/cjs/components/DataTable/DataTableRoot.native.js +166 -20
  14. package/dist/cjs/components/DataTable/DataTableRoot.native.js.map +1 -1
  15. package/dist/cjs/components/DataTable/DataTableStateContext.native.js.map +1 -1
  16. package/dist/cjs/components/DataTable/DataTableVirtualized.cjs +3 -1
  17. package/dist/cjs/components/DataTable/DataTableVirtualized.native.js +3 -1
  18. package/dist/cjs/components/DataTable/DataTableVirtualized.native.js.map +1 -1
  19. package/dist/cjs/components/DataTable/index.native.js +50 -13
  20. package/dist/cjs/components/DataTable/index.native.js.map +1 -1
  21. package/dist/cjs/components/DataTableBulkActions/index.cjs +16 -1
  22. package/dist/cjs/components/DataTableBulkActions/index.native.js +20 -1
  23. package/dist/cjs/components/DataTableBulkActions/index.native.js.map +1 -1
  24. package/dist/cjs/components/DataTableCell/CellModalEditor.cjs +4 -8
  25. package/dist/cjs/components/DataTableCell/CellOptionsEditor.native.js.map +1 -1
  26. package/dist/cjs/components/DataTableCell/index.cjs +1 -33
  27. package/dist/cjs/components/DataTableCell/index.native.js +1 -34
  28. package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
  29. package/dist/cjs/components/DataTableColumnPinning/index.cjs +2 -2
  30. package/dist/cjs/components/DataTableColumnPinning/index.native.js +2 -2
  31. package/dist/cjs/components/DataTableColumnPinning/index.native.js.map +1 -1
  32. package/dist/cjs/components/DataTableDateFilter/index.native.js.map +1 -1
  33. package/dist/cjs/components/DataTableExport/index.cjs +1 -0
  34. package/dist/cjs/components/DataTableExport/index.native.js +1 -0
  35. package/dist/cjs/components/DataTableExport/index.native.js.map +1 -1
  36. package/dist/cjs/components/DataTableFilterButton/index.cjs +1 -1
  37. package/dist/cjs/components/DataTableFilterButton/index.native.js +5 -2
  38. package/dist/cjs/components/DataTableFilterButton/index.native.js.map +1 -1
  39. package/dist/cjs/components/DataTableFilterChips/index.cjs +1 -1
  40. package/dist/cjs/components/DataTableFilterChips/index.native.js +1 -1
  41. package/dist/cjs/components/DataTableFilterChips/index.native.js.map +1 -1
  42. package/dist/cjs/components/DataTableFilters/index.native.js.map +1 -1
  43. package/dist/cjs/components/DataTableGrouping/index.cjs +17 -1
  44. package/dist/cjs/components/DataTableGrouping/index.native.js +17 -1
  45. package/dist/cjs/components/DataTableGrouping/index.native.js.map +1 -1
  46. package/dist/cjs/components/DataTablePagination/index.native.js.map +1 -1
  47. package/dist/cjs/components/DataTableSearch/index.native.js.map +1 -1
  48. package/dist/cjs/components/DataTableToolbar/index.cjs +1 -0
  49. package/dist/cjs/components/DataTableToolbar/index.native.js +2 -1
  50. package/dist/cjs/components/DataTableToolbar/index.native.js.map +1 -1
  51. package/dist/cjs/components/DataTableViewOptions/index.native.js.map +1 -1
  52. package/dist/cjs/components/FilterManager.cjs +18 -14
  53. package/dist/cjs/components/FilterManager.native.js +18 -14
  54. package/dist/cjs/components/FilterManager.native.js.map +1 -1
  55. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  56. package/dist/cjs/components/KnobDialog.native.js.map +1 -1
  57. package/dist/cjs/components/SavedFilterDropdown.native.js.map +1 -1
  58. package/dist/cjs/components/SimpleTable/index.cjs +1 -1
  59. package/dist/cjs/components/SimpleTable/index.native.js +1 -1
  60. package/dist/cjs/components/SimpleTable/index.native.js.map +1 -1
  61. package/dist/cjs/components/TableInput/index.native.js +4 -1
  62. package/dist/cjs/components/TableInput/index.native.js.map +1 -1
  63. package/dist/cjs/components/TableInput/shared.native.js.map +1 -1
  64. package/dist/cjs/hooks/useDataTable.cjs +17 -1
  65. package/dist/cjs/hooks/useDataTable.native.js +19 -1
  66. package/dist/cjs/hooks/useDataTable.native.js.map +1 -1
  67. package/dist/cjs/hooks/useDataTableKeyboard.native.js.map +1 -1
  68. package/dist/cjs/hooks/useDragScroll.native.js.map +1 -1
  69. package/dist/cjs/hooks/useInfiniteRows.native.js.map +1 -1
  70. package/dist/cjs/hooks/useSavedFilters.native.js.map +1 -1
  71. package/dist/cjs/hooks/useTableColumnPinning.native.js.map +1 -1
  72. package/dist/cjs/hooks/useTableFiltering.native.js.map +1 -1
  73. package/dist/cjs/hooks/useTableGrouping.native.js.map +1 -1
  74. package/dist/cjs/hooks/useTableKeyboard.native.js.map +1 -1
  75. package/dist/cjs/hooks/useTablePagination.native.js.map +1 -1
  76. package/dist/cjs/hooks/useTableSearch.native.js.map +1 -1
  77. package/dist/cjs/hooks/useTableSelection.native.js.map +1 -1
  78. package/dist/cjs/hooks/useTableSorting.native.js.map +1 -1
  79. package/dist/cjs/hooks/useTableUrlSync.native.js.map +1 -1
  80. package/dist/cjs/hooks/useTableViews.native.js.map +1 -1
  81. package/dist/cjs/shared/t.native.js.map +1 -1
  82. package/dist/cjs/shims/primitives.native.js.map +1 -1
  83. package/dist/cjs/shims/tableCellContext.native.js.map +1 -1
  84. package/dist/cjs/types.native.js.map +1 -1
  85. package/dist/cjs/utils/columnAlign.native.js.map +1 -1
  86. package/dist/cjs/utils/filterFields.native.js.map +1 -1
  87. package/dist/cjs/utils/filterUtils.native.js.map +1 -1
  88. package/dist/cjs/utils/paginationDefaults.native.js.map +1 -1
  89. package/dist/cjs/utils/rowPress.cjs +44 -0
  90. package/dist/cjs/utils/rowPress.native.js +56 -0
  91. package/dist/cjs/utils/rowPress.native.js.map +1 -0
  92. package/dist/cjs/utils/urlStateUtils.native.js.map +1 -1
  93. package/dist/cjs/utils/viewData.native.js.map +1 -1
  94. package/dist/cjs/utils/withInterp.native.js.map +1 -1
  95. package/dist/esm/components/ColumnHeaderFilter/index.mjs +1 -0
  96. package/dist/esm/components/ColumnHeaderFilter/index.mjs.map +1 -1
  97. package/dist/esm/components/ColumnHeaderFilter/index.native.js +1 -0
  98. package/dist/esm/components/ColumnHeaderFilter/index.native.js.map +1 -1
  99. package/dist/esm/components/DataTable/DataTableMobileCards.mjs +9 -3
  100. package/dist/esm/components/DataTable/DataTableMobileCards.mjs.map +1 -1
  101. package/dist/esm/components/DataTable/DataTableMobileCards.native.js +12 -3
  102. package/dist/esm/components/DataTable/DataTableMobileCards.native.js.map +1 -1
  103. package/dist/esm/components/DataTable/DataTableRoot.mjs +133 -13
  104. package/dist/esm/components/DataTable/DataTableRoot.mjs.map +1 -1
  105. package/dist/esm/components/DataTable/DataTableRoot.native.js +159 -13
  106. package/dist/esm/components/DataTable/DataTableRoot.native.js.map +1 -1
  107. package/dist/esm/components/DataTable/DataTableVirtualized.mjs +3 -1
  108. package/dist/esm/components/DataTable/DataTableVirtualized.mjs.map +1 -1
  109. package/dist/esm/components/DataTable/DataTableVirtualized.native.js +3 -1
  110. package/dist/esm/components/DataTable/DataTableVirtualized.native.js.map +1 -1
  111. package/dist/esm/components/DataTable/index.native.js +40 -3
  112. package/dist/esm/components/DataTable/index.native.js.map +1 -1
  113. package/dist/esm/components/DataTableBulkActions/index.mjs +16 -1
  114. package/dist/esm/components/DataTableBulkActions/index.mjs.map +1 -1
  115. package/dist/esm/components/DataTableBulkActions/index.native.js +20 -1
  116. package/dist/esm/components/DataTableBulkActions/index.native.js.map +1 -1
  117. package/dist/esm/components/DataTableCell/CellModalEditor.mjs +4 -8
  118. package/dist/esm/components/DataTableCell/CellModalEditor.mjs.map +1 -1
  119. package/dist/esm/components/DataTableCell/index.mjs +1 -33
  120. package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
  121. package/dist/esm/components/DataTableCell/index.native.js +1 -34
  122. package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
  123. package/dist/esm/components/DataTableColumnPinning/index.mjs +2 -2
  124. package/dist/esm/components/DataTableColumnPinning/index.mjs.map +1 -1
  125. package/dist/esm/components/DataTableColumnPinning/index.native.js +2 -2
  126. package/dist/esm/components/DataTableColumnPinning/index.native.js.map +1 -1
  127. package/dist/esm/components/DataTableExport/index.mjs +1 -0
  128. package/dist/esm/components/DataTableExport/index.mjs.map +1 -1
  129. package/dist/esm/components/DataTableExport/index.native.js +1 -0
  130. package/dist/esm/components/DataTableExport/index.native.js.map +1 -1
  131. package/dist/esm/components/DataTableFilterButton/index.mjs +1 -1
  132. package/dist/esm/components/DataTableFilterButton/index.mjs.map +1 -1
  133. package/dist/esm/components/DataTableFilterButton/index.native.js +4 -1
  134. package/dist/esm/components/DataTableFilterButton/index.native.js.map +1 -1
  135. package/dist/esm/components/DataTableFilterChips/index.mjs +1 -1
  136. package/dist/esm/components/DataTableFilterChips/index.mjs.map +1 -1
  137. package/dist/esm/components/DataTableFilterChips/index.native.js +1 -1
  138. package/dist/esm/components/DataTableGrouping/index.mjs +18 -3
  139. package/dist/esm/components/DataTableGrouping/index.mjs.map +1 -1
  140. package/dist/esm/components/DataTableGrouping/index.native.js +18 -3
  141. package/dist/esm/components/DataTableGrouping/index.native.js.map +1 -1
  142. package/dist/esm/components/DataTableToolbar/index.mjs +1 -0
  143. package/dist/esm/components/DataTableToolbar/index.mjs.map +1 -1
  144. package/dist/esm/components/DataTableToolbar/index.native.js +1 -0
  145. package/dist/esm/components/DataTableToolbar/index.native.js.map +1 -1
  146. package/dist/esm/components/FilterManager.mjs +18 -14
  147. package/dist/esm/components/FilterManager.mjs.map +1 -1
  148. package/dist/esm/components/FilterManager.native.js +18 -14
  149. package/dist/esm/components/FilterManager.native.js.map +1 -1
  150. package/dist/esm/components/SimpleTable/index.mjs +1 -1
  151. package/dist/esm/components/SimpleTable/index.mjs.map +1 -1
  152. package/dist/esm/components/SimpleTable/index.native.js +1 -1
  153. package/dist/esm/components/TableInput/index.native.js +3 -0
  154. package/dist/esm/components/TableInput/index.native.js.map +1 -1
  155. package/dist/esm/hooks/useDataTable.mjs +17 -1
  156. package/dist/esm/hooks/useDataTable.mjs.map +1 -1
  157. package/dist/esm/hooks/useDataTable.native.js +19 -1
  158. package/dist/esm/hooks/useDataTable.native.js.map +1 -1
  159. package/dist/esm/hooks/useTableGrouping.mjs.map +1 -1
  160. package/dist/esm/utils/rowPress.mjs +19 -0
  161. package/dist/esm/utils/rowPress.mjs.map +1 -0
  162. package/dist/esm/utils/rowPress.native.js +28 -0
  163. package/dist/esm/utils/rowPress.native.js.map +1 -0
  164. package/dist/jsx/components/ColumnHeaderFilter/index.mjs +1 -0
  165. package/dist/jsx/components/ColumnHeaderFilter/index.mjs.map +1 -1
  166. package/dist/jsx/components/ColumnHeaderFilter/index.native.js +1 -0
  167. package/dist/jsx/components/ColumnHeaderFilter/index.native.js.map +1 -1
  168. package/dist/jsx/components/DataTable/DataTableMobileCards.mjs +9 -3
  169. package/dist/jsx/components/DataTable/DataTableMobileCards.mjs.map +1 -1
  170. package/dist/jsx/components/DataTable/DataTableMobileCards.native.js +12 -3
  171. package/dist/jsx/components/DataTable/DataTableMobileCards.native.js.map +1 -1
  172. package/dist/jsx/components/DataTable/DataTableRoot.mjs +133 -13
  173. package/dist/jsx/components/DataTable/DataTableRoot.mjs.map +1 -1
  174. package/dist/jsx/components/DataTable/DataTableRoot.native.js +159 -13
  175. package/dist/jsx/components/DataTable/DataTableRoot.native.js.map +1 -1
  176. package/dist/jsx/components/DataTable/DataTableVirtualized.mjs +3 -1
  177. package/dist/jsx/components/DataTable/DataTableVirtualized.mjs.map +1 -1
  178. package/dist/jsx/components/DataTable/DataTableVirtualized.native.js +3 -1
  179. package/dist/jsx/components/DataTable/DataTableVirtualized.native.js.map +1 -1
  180. package/dist/jsx/components/DataTable/index.native.js +40 -3
  181. package/dist/jsx/components/DataTable/index.native.js.map +1 -1
  182. package/dist/jsx/components/DataTableBulkActions/index.mjs +16 -1
  183. package/dist/jsx/components/DataTableBulkActions/index.mjs.map +1 -1
  184. package/dist/jsx/components/DataTableBulkActions/index.native.js +20 -1
  185. package/dist/jsx/components/DataTableBulkActions/index.native.js.map +1 -1
  186. package/dist/jsx/components/DataTableCell/CellModalEditor.mjs +4 -8
  187. package/dist/jsx/components/DataTableCell/CellModalEditor.mjs.map +1 -1
  188. package/dist/jsx/components/DataTableCell/index.mjs +1 -33
  189. package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
  190. package/dist/jsx/components/DataTableCell/index.native.js +1 -34
  191. package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
  192. package/dist/jsx/components/DataTableColumnPinning/index.mjs +2 -2
  193. package/dist/jsx/components/DataTableColumnPinning/index.mjs.map +1 -1
  194. package/dist/jsx/components/DataTableColumnPinning/index.native.js +2 -2
  195. package/dist/jsx/components/DataTableColumnPinning/index.native.js.map +1 -1
  196. package/dist/jsx/components/DataTableExport/index.mjs +1 -0
  197. package/dist/jsx/components/DataTableExport/index.mjs.map +1 -1
  198. package/dist/jsx/components/DataTableExport/index.native.js +1 -0
  199. package/dist/jsx/components/DataTableExport/index.native.js.map +1 -1
  200. package/dist/jsx/components/DataTableFilterButton/index.mjs +1 -1
  201. package/dist/jsx/components/DataTableFilterButton/index.mjs.map +1 -1
  202. package/dist/jsx/components/DataTableFilterButton/index.native.js +4 -1
  203. package/dist/jsx/components/DataTableFilterButton/index.native.js.map +1 -1
  204. package/dist/jsx/components/DataTableFilterChips/index.mjs +1 -1
  205. package/dist/jsx/components/DataTableFilterChips/index.mjs.map +1 -1
  206. package/dist/jsx/components/DataTableFilterChips/index.native.js +1 -1
  207. package/dist/jsx/components/DataTableGrouping/index.mjs +18 -3
  208. package/dist/jsx/components/DataTableGrouping/index.mjs.map +1 -1
  209. package/dist/jsx/components/DataTableGrouping/index.native.js +17 -1
  210. package/dist/jsx/components/DataTableGrouping/index.native.js.map +1 -1
  211. package/dist/jsx/components/DataTableToolbar/index.mjs +1 -0
  212. package/dist/jsx/components/DataTableToolbar/index.mjs.map +1 -1
  213. package/dist/jsx/components/DataTableToolbar/index.native.js +1 -0
  214. package/dist/jsx/components/DataTableToolbar/index.native.js.map +1 -1
  215. package/dist/jsx/components/FilterManager.mjs +18 -14
  216. package/dist/jsx/components/FilterManager.mjs.map +1 -1
  217. package/dist/jsx/components/FilterManager.native.js +18 -14
  218. package/dist/jsx/components/FilterManager.native.js.map +1 -1
  219. package/dist/jsx/components/SimpleTable/index.mjs +1 -1
  220. package/dist/jsx/components/SimpleTable/index.mjs.map +1 -1
  221. package/dist/jsx/components/SimpleTable/index.native.js +1 -1
  222. package/dist/jsx/components/SimpleTable/index.native.js.map +1 -1
  223. package/dist/jsx/components/TableInput/index.native.js +3 -0
  224. package/dist/jsx/components/TableInput/index.native.js.map +1 -1
  225. package/dist/jsx/hooks/useDataTable.mjs +17 -1
  226. package/dist/jsx/hooks/useDataTable.mjs.map +1 -1
  227. package/dist/jsx/hooks/useDataTable.native.js +19 -1
  228. package/dist/jsx/hooks/useDataTable.native.js.map +1 -1
  229. package/dist/jsx/hooks/useTableGrouping.mjs.map +1 -1
  230. package/dist/jsx/utils/rowPress.mjs +19 -0
  231. package/dist/jsx/utils/rowPress.mjs.map +1 -0
  232. package/dist/jsx/utils/rowPress.native.js +56 -0
  233. package/dist/jsx/utils/rowPress.native.js.map +1 -0
  234. package/package.json +12 -12
  235. package/src/components/ColumnHeaderFilter/index.tsx +1 -0
  236. package/src/components/DataTable/DataTable.spec.tsx +4 -1
  237. package/src/components/DataTable/DataTable.stories.tsx +190 -5
  238. package/src/components/DataTable/DataTableMobileCards.tsx +30 -4
  239. package/src/components/DataTable/DataTableRoot.tsx +195 -8
  240. package/src/components/DataTable/DataTableVirtualized.tsx +8 -0
  241. package/src/components/DataTable/cardLayout.spec.tsx +4 -1
  242. package/src/components/DataTable/customCellAlign.spec.tsx +124 -0
  243. package/src/components/DataTable/disabledContract.spec.tsx +4 -3
  244. package/src/components/DataTable/index.native.tsx +47 -1
  245. package/src/components/DataTable/rowPress.spec.tsx +229 -0
  246. package/src/components/DataTable/selectionSemantics.spec.tsx +156 -0
  247. package/src/components/DataTableBulkActions/index.tsx +16 -2
  248. package/src/components/DataTableBulkActions/selectionCountI18n.spec.tsx +94 -0
  249. package/src/components/DataTableCell/CellModalEditor.tsx +3 -1
  250. package/src/components/DataTableCell/DataTableCell.stories.tsx +1 -5
  251. package/src/components/DataTableCell/cellDraftPolicy.spec.tsx +10 -2
  252. package/src/components/DataTableCell/dataTableCell.spec.tsx +10 -2
  253. package/src/components/DataTableCell/index.tsx +5 -36
  254. package/src/components/DataTableColumnPinning/DataTableColumnPinning.stories.tsx +49 -17
  255. package/src/components/DataTableDateFilter/DataTableDateFilter.stories.tsx +4 -3
  256. package/src/components/DataTableExport/DataTableExport.stories.tsx +3 -2
  257. package/src/components/DataTableExport/index.tsx +1 -0
  258. package/src/components/DataTableFilterButton/index.tsx +4 -1
  259. package/src/components/DataTableGrouping/DataTableGrouping.spec.tsx +59 -2
  260. package/src/components/DataTableGrouping/DataTableGrouping.stories.tsx +17 -5
  261. package/src/components/DataTableGrouping/index.tsx +1 -0
  262. package/src/components/DataTablePagination/DataTablePagination.stories.tsx +34 -20
  263. package/src/components/DataTableToolbar/index.tsx +1 -0
  264. package/src/components/DataTableViewOptions/DataTableViewOptions.stories.tsx +2 -1
  265. package/src/components/FilterManager.tsx +21 -13
  266. package/src/components/TableInput/index.native.tsx +3 -0
  267. package/src/components/__scratch__/scratch.spec.tsx +46 -0
  268. package/src/hooks/useDataTable.ts +27 -1
  269. package/src/types.ts +25 -0
  270. package/src/utils/rowPress.ts +58 -0
  271. package/types/components/ColumnHeaderFilter/index.d.ts.map +1 -1
  272. package/types/components/DataTable/DataTableMobileCards.d.ts +13 -1
  273. package/types/components/DataTable/DataTableMobileCards.d.ts.map +1 -1
  274. package/types/components/DataTable/DataTableRoot.d.ts +1 -1
  275. package/types/components/DataTable/DataTableRoot.d.ts.map +1 -1
  276. package/types/components/DataTable/DataTableVirtualized.d.ts +7 -1
  277. package/types/components/DataTable/DataTableVirtualized.d.ts.map +1 -1
  278. package/types/components/DataTable/index.native.d.ts +1 -1
  279. package/types/components/DataTable/index.native.d.ts.map +1 -1
  280. package/types/components/DataTableBulkActions/index.d.ts.map +1 -1
  281. package/types/components/DataTableCell/CellModalEditor.d.ts.map +1 -1
  282. package/types/components/DataTableCell/index.d.ts.map +1 -1
  283. package/types/components/DataTableColumnPinning/index.d.ts.map +1 -1
  284. package/types/components/DataTableFilterButton/index.d.ts.map +1 -1
  285. package/types/components/DataTableGrouping/index.d.ts +8 -0
  286. package/types/components/DataTableGrouping/index.d.ts.map +1 -1
  287. package/types/components/DataTableToolbar/index.d.ts.map +1 -1
  288. package/types/components/FilterManager.d.ts +7 -1
  289. package/types/components/FilterManager.d.ts.map +1 -1
  290. package/types/components/TableInput/index.native.d.ts.map +1 -1
  291. package/types/hooks/useDataTable.d.ts.map +1 -1
  292. package/types/types.d.ts +23 -0
  293. package/types/types.d.ts.map +1 -1
  294. package/types/utils/rowPress.d.ts +20 -0
  295. package/types/utils/rowPress.d.ts.map +1 -0
@@ -0,0 +1,94 @@
1
+ // D-15: the selection-count label is i18n-safe — each variant is ONE
2
+ // whole-sentence t() key ("{{n}} selected", "{{n}} selected (+{{hidden}}
3
+ // filtered out)"), never concatenated English fragments, so locales own the
4
+ // word order. This spec runs with an initialized i18next instance whose
5
+ // translations deliberately differ from the keys (reordered words), proving
6
+ // the label routes through t() — the sibling DataTableBulkActions.spec.tsx
7
+ // covers the untranslated fallback (withInterp fills the raw key).
8
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
9
+ import { cleanup, screen } from "@testing-library/react";
10
+ import i18next from "i18next";
11
+ import { initReactI18next } from "react-i18next";
12
+ import React from "react";
13
+ import {
14
+ getCoreRowModel,
15
+ getFilteredRowModel,
16
+ getPaginationRowModel,
17
+ useReactTable,
18
+ } from "@tanstack/react-table";
19
+ import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
20
+ import { DataTableStateContext } from "../DataTable/DataTableStateContext";
21
+ import { type BulkAction, DataTableBulkActions } from "./index";
22
+
23
+ beforeAll(async () => {
24
+ // Same natural-language keys registered in packages/i18n/en/common.json,
25
+ // mapped to a pseudo-locale that reorders the sentence.
26
+ await i18next.use(initReactI18next).init({
27
+ lng: "en",
28
+ interpolation: { escapeValue: false },
29
+ resources: {
30
+ en: {
31
+ translation: {
32
+ "{{n}} selected": "selected: {{n}}",
33
+ "{{n}} selected (+{{hidden}} filtered out)":
34
+ "selected: {{n}} (hidden by filter: {{hidden}})",
35
+ },
36
+ },
37
+ },
38
+ });
39
+ });
40
+
41
+ afterEach(cleanup);
42
+
43
+ type Row = { id: string; name: string; status: string };
44
+
45
+ const rows: Row[] = [
46
+ { id: "a", name: "Alpha", status: "ready" },
47
+ { id: "b", name: "Bravo", status: "blocked" },
48
+ ];
49
+
50
+ const actions: BulkAction<Row>[] = [{ id: "x", label: "X", onExecute: vi.fn() }];
51
+
52
+ function Harness({ columnFilters = [] }: { columnFilters?: Array<{ id: string; value: any }> }) {
53
+ const [rowSelection, setRowSelection] = React.useState<Record<string, boolean>>({
54
+ a: true,
55
+ b: true,
56
+ });
57
+ const table = useReactTable({
58
+ data: rows,
59
+ columns: [
60
+ { id: "name", accessorKey: "name", header: "Name" },
61
+ { id: "status", accessorKey: "status", header: "Status" },
62
+ ] as any,
63
+ state: { rowSelection, columnFilters, pagination: { pageIndex: 0, pageSize: 10 } },
64
+ onRowSelectionChange: setRowSelection,
65
+ getCoreRowModel: getCoreRowModel(),
66
+ getFilteredRowModel: getFilteredRowModel(),
67
+ getPaginationRowModel: getPaginationRowModel(),
68
+ getRowId: (row) => row.id,
69
+ enableRowSelection: true,
70
+ });
71
+ const ctxValue = React.useMemo(
72
+ () => ({ table, state: {} as any, actions: {} as any, computed: {} as any }) as any,
73
+ [table, rowSelection, columnFilters],
74
+ );
75
+ return (
76
+ <DataTableStateContext.Provider value={ctxValue}>
77
+ <DataTableBulkActions actions={actions} />
78
+ </DataTableStateContext.Provider>
79
+ );
80
+ }
81
+
82
+ describe("selection count label i18n (D-15)", () => {
83
+ it("base count renders through the {{n}} selected key", () => {
84
+ renderWithProviders(<Harness />);
85
+ expect(screen.getByText("selected: 2")).toBeInTheDocument();
86
+ expect(screen.queryByText(/\d selected/)).not.toBeInTheDocument();
87
+ });
88
+
89
+ it("filtered-out annotation renders through its own whole-sentence key", () => {
90
+ renderWithProviders(<Harness columnFilters={[{ id: "status", value: "ready" }]} />);
91
+ expect(screen.getByText("selected: 2 (hidden by filter: 1)")).toBeInTheDocument();
92
+ expect(screen.queryByText(/filtered out/)).not.toBeInTheDocument();
93
+ });
94
+ });
@@ -12,6 +12,7 @@
12
12
  * footer stays within the two-action dialog budget (LC-31).
13
13
  */
14
14
 
15
+ import { DialogOverlay } from "@multiplatform.one/components";
15
16
  import { FieldDisplay, type FieldDisplayType } from "@multiplatform.one/forms";
16
17
  import { TableCellContext } from "@multiplatform.one/table-primitives";
17
18
  import { useResolvedKnobs } from "@multiplatform.one/theme";
@@ -118,11 +119,12 @@ export function CellModalEditor({
118
119
  </View>
119
120
  <Dialog open={open} onOpenChange={handleOpenChange} modal>
120
121
  <Dialog.Portal>
121
- <Dialog.Overlay opacity={0.5} enterStyle={{ opacity: 0 }} exitStyle={{ opacity: 0 }} />
122
+ <DialogOverlay opacity={0.5} />
122
123
  <Dialog.Content
123
124
  borderWidth={1}
124
125
  borderColor="$borderColor"
125
126
  elevation={knobProps.elevation}
127
+ transition={knobProps.transition}
126
128
  enterStyle={{ y: -10, opacity: 0, scale: 0.96 }}
127
129
  exitStyle={{ y: 10, opacity: 0, scale: 0.96 }}
128
130
  maxWidth={720}
@@ -16,11 +16,7 @@ import { fn } from "storybook/test";
16
16
  import { Table } from "@multiplatform.one/table-primitives";
17
17
  import React from "react";
18
18
  import { Text, View, XStack, YStack } from "tamagui";
19
- import type {
20
- DataTableCellRenderContext,
21
- DataTableField,
22
- DataTableFieldProps,
23
- } from "../../types";
19
+ import type { DataTableCellRenderContext, DataTableField, DataTableFieldProps } from "../../types";
24
20
  import { DataTableCell } from "./index";
25
21
 
26
22
  // Custom renderers receive the cell's honest state as the 4th argument and
@@ -188,7 +188,11 @@ function MockMultiDatePicker({ value, onChange }: any) {
188
188
  );
189
189
  }
190
190
 
191
- vi.mock("@multiplatform.one/forms", () => ({
191
+ // Partial mock: real module first (module-scope exports like `zIndex` keep
192
+ // resolving for transitive imports), field components faked on top — the
193
+ // spec asserts draft/commit policy per field, not the fields themselves.
194
+ vi.mock("@multiplatform.one/forms", async (importOriginal) => ({
195
+ ...(await importOriginal<typeof import("@multiplatform.one/forms")>()),
192
196
  Checkbox: createMock("Checkbox"),
193
197
  Switch: createMock("Switch"),
194
198
  Select: MockSelect,
@@ -231,7 +235,11 @@ vi.mock("@multiplatform.one/rich-text", () => ({
231
235
  RichTextEditor: createMock("RichTextEditor"),
232
236
  }));
233
237
 
234
- vi.mock("@multiplatform.one/theme", () => ({
238
+ // Partial mock (importOriginal): only the knobs hook is stubbed — real
239
+ // module-scope exports (MIN_PRESS_TARGET, press-target helpers, formatters)
240
+ // keep resolving for every component this spec transitively imports.
241
+ vi.mock("@multiplatform.one/theme", async (importOriginal) => ({
242
+ ...(await importOriginal<typeof import("@multiplatform.one/theme")>()),
235
243
  useResolvedKnobs: () => ({
236
244
  knobProps: {
237
245
  sizeToken: "$3",
@@ -30,7 +30,11 @@ function createMock(name: string) {
30
30
  return Comp;
31
31
  }
32
32
 
33
- vi.mock("@multiplatform.one/forms", () => ({
33
+ // Partial mock: real module first (module-scope exports like `zIndex` keep
34
+ // resolving for transitive imports), field components faked on top — the
35
+ // spec asserts ROUTING to fields, not the fields themselves.
36
+ vi.mock("@multiplatform.one/forms", async (importOriginal) => ({
37
+ ...(await importOriginal<typeof import("@multiplatform.one/forms")>()),
34
38
  Checkbox: createMock("Checkbox"),
35
39
  Switch: createMock("Switch"),
36
40
  Select: createMock("Select"),
@@ -94,7 +98,11 @@ vi.mock("@multiplatform.one/rich-text", () => ({
94
98
  RichTextEditor: createMock("RichTextEditor"),
95
99
  }));
96
100
 
97
- vi.mock("@multiplatform.one/theme", () => ({
101
+ // Partial mock (importOriginal): only the knobs hook is stubbed — real
102
+ // module-scope exports (MIN_PRESS_TARGET, press-target helpers, formatters)
103
+ // keep resolving for every component this spec transitively imports.
104
+ vi.mock("@multiplatform.one/theme", async (importOriginal) => ({
105
+ ...(await importOriginal<typeof import("@multiplatform.one/theme")>()),
98
106
  useResolvedKnobs: () => ({
99
107
  knobProps: {
100
108
  sizeToken: "$3",
@@ -175,19 +175,16 @@ export function DataTableCell({
175
175
  // large tables performant and gives heavy fields a purpose-built compact
176
176
  // representation. Edit-mode (below) still mounts the full field component.
177
177
  if (readOnly) {
178
- // FieldDisplay formats currency via Intl.NumberFormat, which requires an
179
- // ISO-4217 code — a raw symbol ("$") throws RangeError and crashes the
180
- // whole table. Normalize symbols to codes and drop anything still invalid.
181
- const displayFieldProps =
182
- fieldProps.currency != null
183
- ? { ...fieldProps, currency: normalizeCurrencyCode(fieldProps.currency) }
184
- : fieldProps;
178
+ // Currency symbols ride through untouched: the money register in
179
+ // `@multiplatform.one/theme` owns symbol→ISO-code resolution (LC-12 one
180
+ // channel per data class) and prefixes a symbol it has no code for, so a
181
+ // cell never has to sanitize a value on the way to the formatter.
185
182
  return (
186
183
  <CellWrapper justify={cellJustify}>
187
184
  <FieldDisplay
188
185
  field={field}
189
186
  value={value}
190
- fieldProps={displayFieldProps}
187
+ fieldProps={fieldProps}
191
188
  linkedValue={_linkedValue}
192
189
  />
193
190
  </CellWrapper>
@@ -1182,34 +1179,6 @@ function DraftFieldEditor({
1182
1179
  );
1183
1180
  }
1184
1181
 
1185
- const CURRENCY_SYMBOL_TO_CODE: Record<string, string> = {
1186
- $: "USD",
1187
- "€": "EUR",
1188
- "£": "GBP",
1189
- "¥": "JPY",
1190
- "₹": "INR",
1191
- "₩": "KRW",
1192
- "₽": "RUB",
1193
- R$: "BRL",
1194
- };
1195
-
1196
- const currencyCodeCache = new Map<string, string | undefined>();
1197
-
1198
- function normalizeCurrencyCode(currency: unknown): string | undefined {
1199
- if (typeof currency !== "string" || !currency) return undefined;
1200
- if (currencyCodeCache.has(currency)) return currencyCodeCache.get(currency);
1201
- const mapped = CURRENCY_SYMBOL_TO_CODE[currency] ?? currency;
1202
- let resolved: string | undefined;
1203
- try {
1204
- new Intl.NumberFormat(undefined, { style: "currency", currency: mapped });
1205
- resolved = mapped;
1206
- } catch {
1207
- resolved = undefined;
1208
- }
1209
- currencyCodeCache.set(currency, resolved);
1210
- return resolved;
1211
- }
1212
-
1213
1182
  function CellWrapper({
1214
1183
  children,
1215
1184
  justify = "flex-start",
@@ -8,6 +8,7 @@ import {
8
8
  useReactTable,
9
9
  } from "@tanstack/react-table";
10
10
  import { useState } from "react";
11
+ import { formatCurrency, useResolvedKnobs } from "@multiplatform.one/theme";
11
12
  import { Button } from "@multiplatform.one/forms";
12
13
  import { Card, H3, Paragraph, ScrollView, Separator, Text, View, XStack, YStack } from "tamagui";
13
14
  import { DataTableColumnPinning, useColumnPinning } from "./index";
@@ -25,10 +26,25 @@ interface Person {
25
26
  performance: number;
26
27
  }
27
28
 
29
+ /**
30
+ * Seeded PRNG (mulberry32). The fixture must produce byte-identical rows on
31
+ * every page load: `Math.random()` here redrew Salary and the performance-bar
32
+ * width per capture, which is what flapped the visual baselines for this
33
+ * family. Determinism at the source keeps every pixel under comparison, where
34
+ * a screenshot mask or delay would not.
35
+ */
36
+ const seededRandom = (seed: number) => () => {
37
+ seed = (seed + 0x6d2b79f5) | 0;
38
+ let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
39
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
40
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
41
+ };
42
+
28
43
  const generateData = (count: number): Person[] => {
29
44
  const departments = ["Engineering", "Sales", "Marketing", "HR", "Finance"];
30
45
  const roles = ["Manager", "Senior", "Junior", "Lead", "Intern"];
31
46
  const statuses: ("active" | "inactive" | "pending")[] = ["active", "inactive", "pending"];
47
+ const random = seededRandom(0x5eed);
32
48
 
33
49
  return Array.from({ length: count }, (_, i) => ({
34
50
  id: i + 1,
@@ -37,16 +53,44 @@ const generateData = (count: number): Person[] => {
37
53
  email: `user${i + 1}@example.com`,
38
54
  department: departments[i % departments.length],
39
55
  role: roles[i % roles.length],
40
- salary: 50000 + Math.floor(Math.random() * 100000),
56
+ salary: 50000 + Math.floor(random() * 100000),
41
57
  startDate: new Date(2020 + (i % 4), i % 12, (i % 28) + 1).toISOString().split("T")[0],
42
58
  status: statuses[i % statuses.length],
43
- performance: Math.floor(Math.random() * 100),
59
+ performance: Math.floor(random() * 100),
44
60
  }));
45
61
  };
46
62
 
47
63
  const defaultData = generateData(50);
48
64
  const columnHelper = createColumnHelper<Person>();
49
65
 
66
+ /**
67
+ * Mini performance track. A component rather than an inline cell renderer so
68
+ * the track's radius can follow the knob (LC-60): the previous pinned `$2`
69
+ * outlived `borderRadius:none` on an 8px-tall bar, which reads as an
70
+ * undeclared knob-immune shape.
71
+ */
72
+ function PerformanceBar({ value }: { value: number }) {
73
+ const { knobProps } = useResolvedKnobs();
74
+ return (
75
+ <XStack alignItems="center" gap="$2">
76
+ <View
77
+ backgroundColor="$gray5"
78
+ height={8}
79
+ flex={1}
80
+ {...knobProps.containerRadius}
81
+ overflow="hidden"
82
+ >
83
+ <View
84
+ backgroundColor={value > 70 ? "$green10" : value > 40 ? "$yellow10" : "$red10"}
85
+ height="100%"
86
+ width={`${value}%`}
87
+ />
88
+ </View>
89
+ <Text fontSize="$1">{value}%</Text>
90
+ </XStack>
91
+ );
92
+ }
93
+
50
94
  const defaultColumns = [
51
95
  columnHelper.accessor("id", {
52
96
  header: "ID",
@@ -76,7 +120,8 @@ const defaultColumns = [
76
120
  }),
77
121
  columnHelper.accessor("salary", {
78
122
  header: "Salary",
79
- cell: (info) => `$${info.getValue().toLocaleString()}`,
123
+ cell: (info) =>
124
+ formatCurrency(info.getValue() as number, { symbol: "$", fractionDigits: "auto" }),
80
125
  }),
81
126
  columnHelper.accessor("startDate", {
82
127
  header: "Start Date",
@@ -100,20 +145,7 @@ const defaultColumns = [
100
145
  }),
101
146
  columnHelper.accessor("performance", {
102
147
  header: "Performance",
103
- cell: (info) => (
104
- <XStack alignItems="center" gap="$2">
105
- <View backgroundColor="$gray5" height={8} flex={1} borderRadius="$2" overflow="hidden">
106
- <View
107
- backgroundColor={
108
- info.getValue() > 70 ? "$green10" : info.getValue() > 40 ? "$yellow10" : "$red10"
109
- }
110
- height="100%"
111
- width={`${info.getValue()}%`}
112
- />
113
- </View>
114
- <Text fontSize="$1">{info.getValue()}%</Text>
115
- </XStack>
116
- ),
148
+ cell: (info) => <PerformanceBar value={info.getValue()} />,
117
149
  }),
118
150
  ];
119
151
 
@@ -1,5 +1,6 @@
1
1
  import { action } from "@multiplatform.one/storybook";
2
2
  import React from "react";
3
+ import { formatAbsoluteDate } from "@multiplatform.one/theme";
3
4
  import { Card, Text, XStack, YStack } from "tamagui";
4
5
  import { DataTableDateFilter, type DateFilterValue, applyDateFilter } from "./index";
5
6
 
@@ -200,10 +201,10 @@ export const WithDateConstraints = () => {
200
201
 
201
202
  <YStack gap="$2">
202
203
  <Text fontSize="$3" color="$gray11">
203
- Min Date: {minDate.toLocaleDateString()}
204
+ Min Date: {formatAbsoluteDate(minDate, { year: "always" })}
204
205
  </Text>
205
206
  <Text fontSize="$3" color="$gray11">
206
- Max Date: {maxDate.toLocaleDateString()}
207
+ Max Date: {formatAbsoluteDate(maxDate, { year: "always" })}
207
208
  </Text>
208
209
  </YStack>
209
210
  </YStack>
@@ -302,7 +303,7 @@ export const WithDataIntegration = () => {
302
303
  {task.title}
303
304
  </Text>
304
305
  <Text fontSize="$2" color="$gray11">
305
- Created: {new Date(task.createdAt).toLocaleDateString()}
306
+ Created: {formatAbsoluteDate(task.createdAt)}
306
307
  </Text>
307
308
  </YStack>
308
309
  <Text
@@ -1,4 +1,5 @@
1
1
  import { action } from "@multiplatform.one/storybook";
2
+ import { formatAbsoluteDate, formatNumber } from "@multiplatform.one/theme";
2
3
  import { Card, Text, YStack } from "tamagui";
3
4
  import { Table } from "@multiplatform.one/table-primitives";
4
5
  import { DataTableExport } from "./index";
@@ -97,7 +98,7 @@ const productColumns = [
97
98
  {
98
99
  accessorKey: "createdAt",
99
100
  header: "Created",
100
- cell: (info: any) => new Date(info.getValue()).toLocaleDateString(),
101
+ cell: (info: any) => formatAbsoluteDate(info.getValue()),
101
102
  },
102
103
  ];
103
104
 
@@ -424,7 +425,7 @@ export const WithComplexData = () => {
424
425
  {
425
426
  accessorKey: "metrics",
426
427
  header: "Users",
427
- cell: (info: any) => info.getValue().users.toLocaleString(),
428
+ cell: (info: any) => formatNumber(info.getValue().users),
428
429
  },
429
430
  {
430
431
  accessorKey: "features",
@@ -298,6 +298,7 @@ function ExportOptionsPopover({
298
298
  {...knobProps.borderRadius}
299
299
  borderWidth={1}
300
300
  borderColor="$borderColor"
301
+ transition={knobProps.transition}
301
302
  enterStyle={{ x: 0, y: -10, opacity: 0 }}
302
303
  exitStyle={{ x: 0, y: -10, opacity: 0 }}
303
304
  minWidth={popoverMinWidth}
@@ -46,7 +46,10 @@ function FieldRow({ field, highlighted, onSelect }: FieldRowProps) {
46
46
  cursor="pointer"
47
47
  paddingHorizontal="$2.5"
48
48
  paddingVertical="$2"
49
- {...knobProps.borderRadius}
49
+ // LC-55 FRAGMENT-LICENSE (D-03, G-08 arm): a hoverable option row is
50
+ // wayfinding, not chrome-earning — the full borderRadius fragment
51
+ // carries borderWidth and drew a permanent ring under the hover fill.
52
+ {...knobProps.containerRadius}
50
53
  backgroundColor={highlighted ? "$color4" : "transparent"}
51
54
  hoverStyle={{ backgroundColor: "$color4" }}
52
55
  pressStyle={{ backgroundColor: "$color5" }}
@@ -1,9 +1,15 @@
1
1
  import { fireEvent, screen } from "@testing-library/react";
2
2
  import { renderWithProviders } from "@multiplatform.one/test-utils";
3
+ import { formatNumber } from "@multiplatform.one/theme";
3
4
  import { beforeEach, describe, expect, it, vi } from "vitest";
4
5
  import "@testing-library/jest-dom";
5
6
  import { DataTable } from "../DataTable";
6
- import { type AggregationConfig, DataTableGrouping, groupData } from "./index";
7
+ import {
8
+ type AggregationConfig,
9
+ DataTableGrouping,
10
+ formatAggregateValue,
11
+ groupData,
12
+ } from "./index";
7
13
 
8
14
  // Test data type
9
15
  type SalesRecord = {
@@ -115,7 +121,58 @@ describe("DataTableGrouping", () => {
115
121
 
116
122
  // Should show aggregation values
117
123
  expect(screen.getByText("North America")).toBeInTheDocument();
118
- // The actual aggregation display would be tested in integration
124
+ // LC-12 render channel: the sum reaches the slot through the house
125
+ // number register, so it reads like the cells it summarises.
126
+ expect(screen.getByText(formatNumber(15400))).toBeInTheDocument();
127
+ expect(screen.queryByText("15400")).not.toBeInTheDocument();
128
+ });
129
+
130
+ it("routes the header item count through the house number register (LC-12)", () => {
131
+ // Same data class as the aggregates: a large group's count must read
132
+ // localized ("(1,000 items)"), never raw beside channel-formatted sums.
133
+ const bigData = Array.from({ length: 1000 }, (_, i) => ({
134
+ id: String(i),
135
+ region: "North America",
136
+ }));
137
+ renderWithProviders(
138
+ <DataTableGrouping
139
+ data={bigData}
140
+ columns={[{ accessorKey: "region", header: "Region", type: "string" as const }]}
141
+ groupBy={["region"]}
142
+ />,
143
+ );
144
+
145
+ const expected = `(${formatNumber(1000, { maximumFractionDigits: 0 })} items)`;
146
+ expect(screen.getByText(expected)).toBeInTheDocument();
147
+ expect(screen.queryByText("(1000 items)")).not.toBeInTheDocument();
148
+ });
149
+
150
+ describe("formatAggregateValue (LC-12 number channel)", () => {
151
+ const sum: AggregationConfig = { column: "totalRevenue", function: "sum" };
152
+ const count: AggregationConfig = { column: "quantity", function: "count" };
153
+
154
+ it("routes aggregates through the house number register", () => {
155
+ expect(formatAggregateValue(15400, sum)).toBe(formatNumber(15400));
156
+ expect(formatAggregateValue(5133.33, sum)).toBe(formatNumber(5133.33));
157
+ });
158
+
159
+ it("keeps counts whole", () => {
160
+ expect(formatAggregateValue(3, count)).toBe("3");
161
+ });
162
+
163
+ it("formats a numeric string from a custom aggregation", () => {
164
+ expect(formatAggregateValue("15400", sum)).toBe(formatNumber(15400));
165
+ });
166
+
167
+ it("passes non-numeric custom results through as their own text", () => {
168
+ expect(formatAggregateValue("mixed", sum)).toBe("mixed");
169
+ });
170
+
171
+ it("keeps the zero floor for missing and non-finite values", () => {
172
+ expect(formatAggregateValue(undefined, sum)).toBe("0");
173
+ expect(formatAggregateValue(Number.NaN, sum)).toBe("0");
174
+ expect(formatAggregateValue(0, sum)).toBe("0");
175
+ });
119
176
  });
120
177
 
121
178
  it("calls onGroupingChange when grouping config changes", () => {
@@ -442,19 +442,31 @@ export const WithGroupingControls = () => {
442
442
  );
443
443
  };
444
444
 
445
+ /**
446
+ * Seeded PRNG (mulberry32). The aggregate rows this story renders (Total
447
+ * Revenue, Item Count) are derived from the fixture, so `Math.random()` made
448
+ * the shot differ on every capture and flapped the visual baseline. Seeding
449
+ * keeps the whole surface under pixel comparison.
450
+ */
451
+ const seededRandom = (seed: number) => () => {
452
+ seed = (seed + 0x6d2b79f5) | 0;
453
+ let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
454
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
455
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
456
+ };
457
+
445
458
  export const PerformanceTest = () => {
446
459
  // Generate larger dataset
460
+ const random = seededRandom(0x5eed);
447
461
  const largeDataset = Array.from({ length: 1000 }, (_, i) => ({
448
462
  id: `item-${i + 1}`,
449
463
  region: ["North America", "Europe", "Asia Pacific", "South America"][i % 4],
450
464
  category: ["Electronics", "Furniture", "Appliances", "Books"][i % 4],
451
465
  salesperson: `Person ${(i % 10) + 1}`,
452
- quantity: Math.floor(Math.random() * 20) + 1,
453
- unitPrice: Math.floor(Math.random() * 1000) + 100,
466
+ quantity: Math.floor(random() * 20) + 1,
467
+ unitPrice: Math.floor(random() * 1000) + 100,
454
468
  totalRevenue: 0, // Will be calculated
455
- date: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000)
456
- .toISOString()
457
- .split("T")[0],
469
+ date: new Date(Date.now() - random() * 365 * 24 * 60 * 60 * 1000).toISOString().split("T")[0],
458
470
  })).map((item) => ({
459
471
  ...item,
460
472
  totalRevenue: item.quantity * item.unitPrice,
@@ -559,6 +559,7 @@ export function DataTableGroupingConfig<T = any>(props: DataTableGroupingConfigP
559
559
  {...knobProps.borderRadius}
560
560
  borderWidth={1}
561
561
  borderColor="$borderColor"
562
+ transition={knobProps.transition}
562
563
  enterStyle={{ x: 0, y: -10, opacity: 0 }}
563
564
  exitStyle={{ x: 0, y: -10, opacity: 0 }}
564
565
  minWidth={popoverMinWidth}
@@ -1,8 +1,10 @@
1
1
  import { action } from "@multiplatform.one/storybook";
2
+ import type { GuardrailSpecimenParameter } from "@multiplatform.one/theme";
2
3
  import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
3
4
  import React from "react";
4
- import { Button } from "@multiplatform.one/forms";
5
- import { Input, Text, View, XStack, YStack } from "tamagui";
5
+ // Catalog Button + Input (LC-56 STORY-HONESTY — no raw interactive primitives).
6
+ import { Button, Input } from "@multiplatform.one/forms";
7
+ import { Text, View, XStack, YStack } from "tamagui";
6
8
  import type {
7
9
  DataTableColumn,
8
10
  PageLoadQuery,
@@ -80,7 +82,20 @@ export const Compact: Story = {
80
82
  },
81
83
  };
82
84
 
85
+ // LC-57 allowlist: the boundary stories below EXIST to demo pager edges —
86
+ // first/last/single/empty page rest with the unreachable-direction buttons
87
+ // disabled by design (the honest boundary state, LC-40-washed). The warns
88
+ // still fire; the harvester tolerates exactly these codes on these ids.
89
+ const pagerBoundarySpecimen = {
90
+ guardrailSpecimen: {
91
+ warns: ["bare-disabled"],
92
+ reason:
93
+ "Pager-boundary specimen: unreachable nav directions rest disabled by design at page edges.",
94
+ } satisfies GuardrailSpecimenParameter,
95
+ };
96
+
83
97
  export const FirstPage: Story = {
98
+ parameters: pagerBoundarySpecimen,
84
99
  args: {
85
100
  ...Basic.args,
86
101
  pagination: {
@@ -101,6 +116,7 @@ export const MiddlePage: Story = {
101
116
  };
102
117
 
103
118
  export const LastPage: Story = {
119
+ parameters: pagerBoundarySpecimen,
104
120
  args: {
105
121
  ...Basic.args,
106
122
  pagination: {
@@ -172,6 +188,7 @@ export const SmallDataSet: Story = {
172
188
  };
173
189
 
174
190
  export const SinglePage: Story = {
191
+ parameters: pagerBoundarySpecimen,
175
192
  args: {
176
193
  ...Basic.args,
177
194
  pagination: {
@@ -406,6 +423,7 @@ export const PageJumpDemo: Story = {
406
423
  ...samplePagination,
407
424
  pageIndex: 5,
408
425
  });
426
+ const [jumpValue, setJumpValue] = React.useState("");
409
427
 
410
428
  const handlePaginationChange = (newPagination: TablePagination) => {
411
429
  setPagination({
@@ -445,24 +463,19 @@ export const PageJumpDemo: Story = {
445
463
  </Text>
446
464
  <XStack gap="$2" marginTop="$2" alignItems="center">
447
465
  <Text>Go to page:</Text>
448
- <Input
449
- keyboardType="numeric"
450
- placeholder={`1-${pagination.totalPages}`}
451
- onSubmitEditing={(e) => {
452
- handlePageJump(e.nativeEvent.text);
453
- }}
454
- padding="$1"
455
- width={80}
456
- />
457
- <Button
458
- onPress={() => {
459
- // For demo purposes, we'll use a simple approach
460
- // In a real app, you'd use a ref to get the input value
461
- handlePageJump("1");
462
- }}
463
- size="$2"
464
- padding="$1"
465
- >
466
+ <YStack width={110}>
467
+ <Input
468
+ keyboardType="numeric"
469
+ placeholder={`1-${pagination.totalPages}`}
470
+ onChangeText={setJumpValue}
471
+ inputProps={{
472
+ "aria-label": "Go to page",
473
+ onSubmitEditing: (e: { nativeEvent: { text: string } }) =>
474
+ handlePageJump(e.nativeEvent.text),
475
+ }}
476
+ />
477
+ </YStack>
478
+ <Button onPress={() => handlePageJump(jumpValue)} size="$2" padding="$1">
466
479
  Go
467
480
  </Button>
468
481
  </XStack>
@@ -571,6 +584,7 @@ export const ErrorStates: Story = {
571
584
  };
572
585
 
573
586
  export const EdgeCases: Story = {
587
+ parameters: pagerBoundarySpecimen,
574
588
  args: {
575
589
  ...Basic.args,
576
590
  pagination: {