@multiplatform.one/table 7.10.0 → 7.15.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 (307) hide show
  1. package/README.md +61 -1
  2. package/dist/cjs/components/ChildTable/index.cjs +8 -2
  3. package/dist/cjs/components/ChildTable/index.native.js +8 -2
  4. package/dist/cjs/components/ChildTable/index.native.js.map +1 -1
  5. package/dist/cjs/components/ColumnHeaderFilter/index.cjs +77 -111
  6. package/dist/cjs/components/ColumnHeaderFilter/index.native.js +89 -129
  7. package/dist/cjs/components/ColumnHeaderFilter/index.native.js.map +1 -1
  8. package/dist/cjs/components/DataTable/DataTableCardControls.cjs +90 -54
  9. package/dist/cjs/components/DataTable/DataTableCardControls.native.js +93 -56
  10. package/dist/cjs/components/DataTable/DataTableCardControls.native.js.map +1 -1
  11. package/dist/cjs/components/DataTable/DataTableRoot.cjs +68 -9
  12. package/dist/cjs/components/DataTable/DataTableRoot.native.js +78 -11
  13. package/dist/cjs/components/DataTable/DataTableRoot.native.js.map +1 -1
  14. package/dist/cjs/components/DataTableCell/CellModalEditor.cjs +72 -70
  15. package/dist/cjs/components/DataTableCell/index.cjs +2 -2
  16. package/dist/cjs/components/DataTableCell/index.native.js +2 -11
  17. package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
  18. package/dist/cjs/components/DataTableDateFilter/index.cjs +135 -158
  19. package/dist/cjs/components/DataTableDateFilter/index.native.js +153 -183
  20. package/dist/cjs/components/DataTableDateFilter/index.native.js.map +1 -1
  21. package/dist/cjs/components/DataTableExport/index.cjs +122 -139
  22. package/dist/cjs/components/DataTableExport/index.native.js +135 -157
  23. package/dist/cjs/components/DataTableExport/index.native.js.map +1 -1
  24. package/dist/cjs/components/DataTableFilterButton/index.cjs +14 -0
  25. package/dist/cjs/components/DataTableFilterButton/index.native.js +15 -0
  26. package/dist/cjs/components/DataTableFilterButton/index.native.js.map +1 -1
  27. package/dist/cjs/components/DataTableGrouping/index.cjs +192 -212
  28. package/dist/cjs/components/DataTableGrouping/index.native.js +209 -230
  29. package/dist/cjs/components/DataTableGrouping/index.native.js.map +1 -1
  30. package/dist/cjs/components/DataTableSearch/index.cjs +103 -115
  31. package/dist/cjs/components/DataTableSearch/index.native.js +111 -123
  32. package/dist/cjs/components/DataTableSearch/index.native.js.map +1 -1
  33. package/dist/cjs/components/DataTableToolbar/index.cjs +161 -101
  34. package/dist/cjs/components/DataTableToolbar/index.native.js +168 -109
  35. package/dist/cjs/components/DataTableToolbar/index.native.js.map +1 -1
  36. package/dist/cjs/components/DataTableViewOptions/index.cjs +38 -54
  37. package/dist/cjs/components/DataTableViewOptions/index.native.js +40 -56
  38. package/dist/cjs/components/DataTableViewOptions/index.native.js.map +1 -1
  39. package/dist/cjs/components/FilterOperatorValueEditor/index.cjs +3 -11
  40. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js +3 -11
  41. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  42. package/dist/cjs/components/SimpleTable/index.cjs +2 -6
  43. package/dist/cjs/components/SimpleTable/index.native.js +2 -6
  44. package/dist/cjs/components/SimpleTable/index.native.js.map +1 -1
  45. package/dist/cjs/components/TableInput/RowDetailEditor.cjs +190 -0
  46. package/dist/cjs/components/TableInput/RowDetailEditor.native.js +172 -0
  47. package/dist/cjs/components/TableInput/RowDetailEditor.native.js.map +1 -0
  48. package/dist/cjs/components/TableInput/index.cjs +42 -5
  49. package/dist/cjs/components/TableInput/index.native.js +64 -1
  50. package/dist/cjs/components/TableInput/index.native.js.map +1 -1
  51. package/dist/cjs/components/TableInput/shared.cjs +8 -0
  52. package/dist/cjs/components/TableInput/shared.native.js +10 -0
  53. package/dist/cjs/components/TableInput/shared.native.js.map +1 -1
  54. package/dist/cjs/hooks/useDataTable.cjs +43 -2
  55. package/dist/cjs/hooks/useDataTable.native.js +49 -3
  56. package/dist/cjs/hooks/useDataTable.native.js.map +1 -1
  57. package/dist/cjs/index.native.js.map +1 -1
  58. package/dist/cjs/utils/rowPress.cjs +4 -1
  59. package/dist/cjs/utils/rowPress.native.js +4 -1
  60. package/dist/cjs/utils/rowPress.native.js.map +1 -1
  61. package/dist/esm/components/ChildTable/index.mjs +8 -2
  62. package/dist/esm/components/ChildTable/index.mjs.map +1 -1
  63. package/dist/esm/components/ChildTable/index.native.js +8 -2
  64. package/dist/esm/components/ChildTable/index.native.js.map +1 -1
  65. package/dist/esm/components/ColumnHeaderFilter/index.mjs +80 -114
  66. package/dist/esm/components/ColumnHeaderFilter/index.mjs.map +1 -1
  67. package/dist/esm/components/ColumnHeaderFilter/index.native.js +92 -132
  68. package/dist/esm/components/ColumnHeaderFilter/index.native.js.map +1 -1
  69. package/dist/esm/components/DataTable/DataTableCardControls.mjs +89 -54
  70. package/dist/esm/components/DataTable/DataTableCardControls.mjs.map +1 -1
  71. package/dist/esm/components/DataTable/DataTableCardControls.native.js +92 -56
  72. package/dist/esm/components/DataTable/DataTableCardControls.native.js.map +1 -1
  73. package/dist/esm/components/DataTable/DataTableRoot.mjs +70 -11
  74. package/dist/esm/components/DataTable/DataTableRoot.mjs.map +1 -1
  75. package/dist/esm/components/DataTable/DataTableRoot.native.js +80 -13
  76. package/dist/esm/components/DataTable/DataTableRoot.native.js.map +1 -1
  77. package/dist/esm/components/DataTableCell/CellModalEditor.mjs +76 -74
  78. package/dist/esm/components/DataTableCell/CellModalEditor.mjs.map +1 -1
  79. package/dist/esm/components/DataTableCell/index.mjs +3 -3
  80. package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
  81. package/dist/esm/components/DataTableCell/index.native.js +3 -12
  82. package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
  83. package/dist/esm/components/DataTableDateFilter/index.mjs +137 -160
  84. package/dist/esm/components/DataTableDateFilter/index.mjs.map +1 -1
  85. package/dist/esm/components/DataTableDateFilter/index.native.js +155 -185
  86. package/dist/esm/components/DataTableDateFilter/index.native.js.map +1 -1
  87. package/dist/esm/components/DataTableExport/index.mjs +124 -141
  88. package/dist/esm/components/DataTableExport/index.mjs.map +1 -1
  89. package/dist/esm/components/DataTableExport/index.native.js +137 -159
  90. package/dist/esm/components/DataTableExport/index.native.js.map +1 -1
  91. package/dist/esm/components/DataTableFilterButton/index.mjs +14 -1
  92. package/dist/esm/components/DataTableFilterButton/index.mjs.map +1 -1
  93. package/dist/esm/components/DataTableFilterButton/index.native.js +15 -1
  94. package/dist/esm/components/DataTableFilterButton/index.native.js.map +1 -1
  95. package/dist/esm/components/DataTableGrouping/index.mjs +194 -214
  96. package/dist/esm/components/DataTableGrouping/index.mjs.map +1 -1
  97. package/dist/esm/components/DataTableGrouping/index.native.js +211 -232
  98. package/dist/esm/components/DataTableGrouping/index.native.js.map +1 -1
  99. package/dist/esm/components/DataTableSearch/index.mjs +105 -117
  100. package/dist/esm/components/DataTableSearch/index.mjs.map +1 -1
  101. package/dist/esm/components/DataTableSearch/index.native.js +113 -125
  102. package/dist/esm/components/DataTableSearch/index.native.js.map +1 -1
  103. package/dist/esm/components/DataTableToolbar/index.mjs +165 -106
  104. package/dist/esm/components/DataTableToolbar/index.mjs.map +1 -1
  105. package/dist/esm/components/DataTableToolbar/index.native.js +172 -114
  106. package/dist/esm/components/DataTableToolbar/index.native.js.map +1 -1
  107. package/dist/esm/components/DataTableViewOptions/index.mjs +40 -56
  108. package/dist/esm/components/DataTableViewOptions/index.mjs.map +1 -1
  109. package/dist/esm/components/DataTableViewOptions/index.native.js +42 -58
  110. package/dist/esm/components/DataTableViewOptions/index.native.js.map +1 -1
  111. package/dist/esm/components/FilterOperatorValueEditor/index.mjs +3 -11
  112. package/dist/esm/components/FilterOperatorValueEditor/index.mjs.map +1 -1
  113. package/dist/esm/components/FilterOperatorValueEditor/index.native.js +3 -11
  114. package/dist/esm/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  115. package/dist/esm/components/SimpleTable/index.mjs +3 -7
  116. package/dist/esm/components/SimpleTable/index.mjs.map +1 -1
  117. package/dist/esm/components/SimpleTable/index.native.js +3 -7
  118. package/dist/esm/components/SimpleTable/index.native.js.map +1 -1
  119. package/dist/esm/components/TableInput/RowDetailEditor.mjs +165 -0
  120. package/dist/esm/components/TableInput/RowDetailEditor.mjs.map +1 -0
  121. package/dist/esm/components/TableInput/RowDetailEditor.native.js +144 -0
  122. package/dist/esm/components/TableInput/RowDetailEditor.native.js.map +1 -0
  123. package/dist/esm/components/TableInput/index.mjs +42 -5
  124. package/dist/esm/components/TableInput/index.mjs.map +1 -1
  125. package/dist/esm/components/TableInput/index.native.js +64 -1
  126. package/dist/esm/components/TableInput/index.native.js.map +1 -1
  127. package/dist/esm/components/TableInput/shared.mjs +7 -1
  128. package/dist/esm/components/TableInput/shared.mjs.map +1 -1
  129. package/dist/esm/components/TableInput/shared.native.js +9 -1
  130. package/dist/esm/components/TableInput/shared.native.js.map +1 -1
  131. package/dist/esm/hooks/useDataTable.mjs +43 -2
  132. package/dist/esm/hooks/useDataTable.mjs.map +1 -1
  133. package/dist/esm/hooks/useDataTable.native.js +49 -3
  134. package/dist/esm/hooks/useDataTable.native.js.map +1 -1
  135. package/dist/esm/index.mjs.map +1 -1
  136. package/dist/esm/index.native.js.map +1 -1
  137. package/dist/esm/utils/rowPress.mjs +4 -1
  138. package/dist/esm/utils/rowPress.mjs.map +1 -1
  139. package/dist/esm/utils/rowPress.native.js +4 -1
  140. package/dist/esm/utils/rowPress.native.js.map +1 -1
  141. package/dist/jsx/components/ChildTable/index.mjs +8 -2
  142. package/dist/jsx/components/ChildTable/index.mjs.map +1 -1
  143. package/dist/jsx/components/ChildTable/index.native.js +8 -2
  144. package/dist/jsx/components/ChildTable/index.native.js.map +1 -1
  145. package/dist/jsx/components/ColumnHeaderFilter/index.mjs +80 -114
  146. package/dist/jsx/components/ColumnHeaderFilter/index.mjs.map +1 -1
  147. package/dist/jsx/components/ColumnHeaderFilter/index.native.js +89 -129
  148. package/dist/jsx/components/ColumnHeaderFilter/index.native.js.map +1 -1
  149. package/dist/jsx/components/DataTable/DataTableCardControls.mjs +89 -54
  150. package/dist/jsx/components/DataTable/DataTableCardControls.mjs.map +1 -1
  151. package/dist/jsx/components/DataTable/DataTableCardControls.native.js +93 -56
  152. package/dist/jsx/components/DataTable/DataTableCardControls.native.js.map +1 -1
  153. package/dist/jsx/components/DataTable/DataTableRoot.mjs +70 -11
  154. package/dist/jsx/components/DataTable/DataTableRoot.mjs.map +1 -1
  155. package/dist/jsx/components/DataTable/DataTableRoot.native.js +78 -11
  156. package/dist/jsx/components/DataTable/DataTableRoot.native.js.map +1 -1
  157. package/dist/jsx/components/DataTableCell/CellModalEditor.mjs +76 -74
  158. package/dist/jsx/components/DataTableCell/CellModalEditor.mjs.map +1 -1
  159. package/dist/jsx/components/DataTableCell/index.mjs +3 -3
  160. package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
  161. package/dist/jsx/components/DataTableCell/index.native.js +2 -11
  162. package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
  163. package/dist/jsx/components/DataTableDateFilter/index.mjs +137 -160
  164. package/dist/jsx/components/DataTableDateFilter/index.mjs.map +1 -1
  165. package/dist/jsx/components/DataTableDateFilter/index.native.js +153 -183
  166. package/dist/jsx/components/DataTableDateFilter/index.native.js.map +1 -1
  167. package/dist/jsx/components/DataTableExport/index.mjs +124 -141
  168. package/dist/jsx/components/DataTableExport/index.mjs.map +1 -1
  169. package/dist/jsx/components/DataTableExport/index.native.js +135 -157
  170. package/dist/jsx/components/DataTableExport/index.native.js.map +1 -1
  171. package/dist/jsx/components/DataTableFilterButton/index.mjs +14 -1
  172. package/dist/jsx/components/DataTableFilterButton/index.mjs.map +1 -1
  173. package/dist/jsx/components/DataTableFilterButton/index.native.js +15 -0
  174. package/dist/jsx/components/DataTableFilterButton/index.native.js.map +1 -1
  175. package/dist/jsx/components/DataTableGrouping/index.mjs +194 -214
  176. package/dist/jsx/components/DataTableGrouping/index.mjs.map +1 -1
  177. package/dist/jsx/components/DataTableGrouping/index.native.js +209 -230
  178. package/dist/jsx/components/DataTableGrouping/index.native.js.map +1 -1
  179. package/dist/jsx/components/DataTableSearch/index.mjs +105 -117
  180. package/dist/jsx/components/DataTableSearch/index.mjs.map +1 -1
  181. package/dist/jsx/components/DataTableSearch/index.native.js +111 -123
  182. package/dist/jsx/components/DataTableSearch/index.native.js.map +1 -1
  183. package/dist/jsx/components/DataTableToolbar/index.mjs +165 -106
  184. package/dist/jsx/components/DataTableToolbar/index.mjs.map +1 -1
  185. package/dist/jsx/components/DataTableToolbar/index.native.js +168 -109
  186. package/dist/jsx/components/DataTableToolbar/index.native.js.map +1 -1
  187. package/dist/jsx/components/DataTableViewOptions/index.mjs +40 -56
  188. package/dist/jsx/components/DataTableViewOptions/index.mjs.map +1 -1
  189. package/dist/jsx/components/DataTableViewOptions/index.native.js +40 -56
  190. package/dist/jsx/components/DataTableViewOptions/index.native.js.map +1 -1
  191. package/dist/jsx/components/FilterOperatorValueEditor/index.mjs +3 -11
  192. package/dist/jsx/components/FilterOperatorValueEditor/index.mjs.map +1 -1
  193. package/dist/jsx/components/FilterOperatorValueEditor/index.native.js +3 -11
  194. package/dist/jsx/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  195. package/dist/jsx/components/SimpleTable/index.mjs +3 -7
  196. package/dist/jsx/components/SimpleTable/index.mjs.map +1 -1
  197. package/dist/jsx/components/SimpleTable/index.native.js +2 -6
  198. package/dist/jsx/components/SimpleTable/index.native.js.map +1 -1
  199. package/dist/jsx/components/TableInput/RowDetailEditor.mjs +165 -0
  200. package/dist/jsx/components/TableInput/RowDetailEditor.mjs.map +1 -0
  201. package/dist/jsx/components/TableInput/RowDetailEditor.native.js +172 -0
  202. package/dist/jsx/components/TableInput/RowDetailEditor.native.js.map +1 -0
  203. package/dist/jsx/components/TableInput/index.mjs +42 -5
  204. package/dist/jsx/components/TableInput/index.mjs.map +1 -1
  205. package/dist/jsx/components/TableInput/index.native.js +64 -1
  206. package/dist/jsx/components/TableInput/index.native.js.map +1 -1
  207. package/dist/jsx/components/TableInput/shared.mjs +7 -1
  208. package/dist/jsx/components/TableInput/shared.mjs.map +1 -1
  209. package/dist/jsx/components/TableInput/shared.native.js +10 -0
  210. package/dist/jsx/components/TableInput/shared.native.js.map +1 -1
  211. package/dist/jsx/hooks/useDataTable.mjs +43 -2
  212. package/dist/jsx/hooks/useDataTable.mjs.map +1 -1
  213. package/dist/jsx/hooks/useDataTable.native.js +49 -3
  214. package/dist/jsx/hooks/useDataTable.native.js.map +1 -1
  215. package/dist/jsx/index.js.map +1 -1
  216. package/dist/jsx/index.mjs.map +1 -1
  217. package/dist/jsx/index.native.js.map +1 -1
  218. package/dist/jsx/utils/rowPress.mjs +4 -1
  219. package/dist/jsx/utils/rowPress.mjs.map +1 -1
  220. package/dist/jsx/utils/rowPress.native.js +4 -1
  221. package/dist/jsx/utils/rowPress.native.js.map +1 -1
  222. package/package.json +16 -16
  223. package/src/components/ChildTable/childTable.cellHidden.spec.tsx +57 -0
  224. package/src/components/ChildTable/childTable.rowDetail.spec.tsx +120 -0
  225. package/src/components/ChildTable/index.tsx +16 -0
  226. package/src/components/ColumnHeaderFilter/index.tsx +89 -116
  227. package/src/components/DataTable/DataTable.stories.tsx +130 -6
  228. package/src/components/DataTable/DataTableCardControls.stories.tsx +61 -2
  229. package/src/components/DataTable/DataTableCardControls.tsx +92 -66
  230. package/src/components/DataTable/DataTableRoot.tsx +109 -18
  231. package/src/components/DataTable/cardLayout.spec.tsx +4 -3
  232. package/src/components/DataTable/cardToolbar.spec.tsx +238 -0
  233. package/src/components/DataTable/chromeInterplay.spec.tsx +7 -5
  234. package/src/components/DataTable/columnWidthLayout.spec.tsx +189 -0
  235. package/src/components/DataTable/dataTableCardControls.spec.tsx +3 -2
  236. package/src/components/DataTable/dataTableMobileCards.spec.tsx +1 -4
  237. package/src/components/DataTable/footerLayout.spec.tsx +250 -0
  238. package/src/components/DataTable/headerContainment.spec.tsx +91 -0
  239. package/src/components/DataTable/pinningGeometry.spec.tsx +66 -1
  240. package/src/components/DataTable/rowPress.spec.tsx +2 -6
  241. package/src/components/DataTableBulkActions/DataTableBulkActions.stories.tsx +5 -4
  242. package/src/components/DataTableCell/CellModalEditor.tsx +95 -75
  243. package/src/components/DataTableCell/cellDraftPolicy.spec.tsx +12 -2
  244. package/src/components/DataTableCell/cellModalEditor.spec.tsx +75 -49
  245. package/src/components/DataTableCell/dataTableCell.spec.tsx +31 -14
  246. package/src/components/DataTableCell/index.tsx +3 -2
  247. package/src/components/DataTableDateFilter/DataTableDateFilter.spec.tsx +6 -3
  248. package/src/components/DataTableDateFilter/index.tsx +151 -180
  249. package/src/components/DataTableExport/DataTableExport.spec.tsx +42 -18
  250. package/src/components/DataTableExport/index.tsx +135 -142
  251. package/src/components/DataTableFilterButton/index.tsx +9 -0
  252. package/src/components/DataTableGrouping/index.tsx +193 -195
  253. package/src/components/DataTableSearch/index.tsx +111 -109
  254. package/src/components/DataTableToolbar/index.tsx +179 -107
  255. package/src/components/DataTableViewOptions/DataTableViewOptions.stories.tsx +52 -0
  256. package/src/components/DataTableViewOptions/index.tsx +32 -40
  257. package/src/components/FilterOperatorValueEditor/FilterOperatorValueEditor.spec.tsx +64 -2
  258. package/src/components/FilterOperatorValueEditor/index.tsx +0 -8
  259. package/src/components/SimpleTable/index.tsx +3 -2
  260. package/src/components/SimpleTable/simpleTable.spec.tsx +15 -0
  261. package/src/components/Table/accessibility.spec.tsx +7 -19
  262. package/src/components/TableInput/RowDetailEditor.native.tsx +134 -0
  263. package/src/components/TableInput/RowDetailEditor.tsx +173 -0
  264. package/src/components/TableInput/index.native.tsx +49 -1
  265. package/src/components/TableInput/index.tsx +65 -10
  266. package/src/components/TableInput/shared.ts +29 -0
  267. package/src/components/tableDropdowns.spec.tsx +172 -0
  268. package/src/hooks/useDataTable.spec.ts +153 -0
  269. package/src/hooks/useDataTable.ts +38 -2
  270. package/src/index.ts +1 -0
  271. package/src/types.ts +14 -12
  272. package/src/utils/rowPress.ts +3 -0
  273. package/types/components/ChildTable/index.d.ts +10 -0
  274. package/types/components/ChildTable/index.d.ts.map +1 -1
  275. package/types/components/ColumnHeaderFilter/index.d.ts +1 -1
  276. package/types/components/ColumnHeaderFilter/index.d.ts.map +1 -1
  277. package/types/components/DataTable/DataTableCardControls.d.ts +3 -0
  278. package/types/components/DataTable/DataTableCardControls.d.ts.map +1 -1
  279. package/types/components/DataTable/DataTableRoot.d.ts.map +1 -1
  280. package/types/components/DataTableCell/CellModalEditor.d.ts +10 -7
  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/DataTableDateFilter/index.d.ts.map +1 -1
  284. package/types/components/DataTableFilterButton/index.d.ts +2 -1
  285. package/types/components/DataTableFilterButton/index.d.ts.map +1 -1
  286. package/types/components/DataTableGrouping/index.d.ts.map +1 -1
  287. package/types/components/DataTableSearch/index.d.ts.map +1 -1
  288. package/types/components/DataTableToolbar/index.d.ts +1 -0
  289. package/types/components/DataTableToolbar/index.d.ts.map +1 -1
  290. package/types/components/FilterOperatorValueEditor/index.d.ts.map +1 -1
  291. package/types/components/SimpleTable/index.d.ts.map +1 -1
  292. package/types/components/TableInput/RowDetailEditor.d.ts +24 -0
  293. package/types/components/TableInput/RowDetailEditor.d.ts.map +1 -0
  294. package/types/components/TableInput/RowDetailEditor.native.d.ts +11 -0
  295. package/types/components/TableInput/RowDetailEditor.native.d.ts.map +1 -0
  296. package/types/components/TableInput/index.d.ts +11 -2
  297. package/types/components/TableInput/index.d.ts.map +1 -1
  298. package/types/components/TableInput/index.native.d.ts +8 -1
  299. package/types/components/TableInput/index.native.d.ts.map +1 -1
  300. package/types/components/TableInput/shared.d.ts +11 -0
  301. package/types/components/TableInput/shared.d.ts.map +1 -1
  302. package/types/hooks/useDataTable.d.ts.map +1 -1
  303. package/types/index.d.ts +1 -0
  304. package/types/index.d.ts.map +1 -1
  305. package/types/types.d.ts +14 -12
  306. package/types/types.d.ts.map +1 -1
  307. package/types/utils/rowPress.d.ts.map +1 -1
@@ -32,7 +32,8 @@ import type {
32
32
  } from "../../types";
33
33
  import { DataTableCell } from "../DataTableCell";
34
34
  import { withInterp } from "../../utils/withInterp";
35
- import { generateRowId, isEmptyRows, SKELETON_ROW_COUNT } from "./shared";
35
+ import { RowDetailEditor } from "./RowDetailEditor";
36
+ import { generateRowId, isEmptyRows, SKELETON_ROW_COUNT, type ChildCellHidden } from "./shared";
36
37
 
37
38
  export interface TableInputProps<TData extends DataTableData = DataTableData> {
38
39
  /** Data array for the table (ignored in favor of form state in the form branch) */
@@ -80,6 +81,12 @@ export interface TableInputProps<TData extends DataTableData = DataTableData> {
80
81
  compact?: boolean;
81
82
  /** Leading grab handle + keyboard reorder via kanbanDnd (web implements). */
82
83
  enableReorder?: boolean;
84
+ /** Per-row button that opens the row in a detail editor (MPO-22). */
85
+ rowDetail?: boolean;
86
+ /** Fields the detail editor shows. Defaults to `columns`. */
87
+ detailColumns?: DataTableColumn<TData>[];
88
+ /** Hides one row's cell, in the grid and in the detail editor. */
89
+ isCellHidden?: ChildCellHidden<TData>;
83
90
  }
84
91
 
85
92
  export function TableInput<TData extends DataTableData = DataTableData>({
@@ -103,6 +110,9 @@ export function TableInput<TData extends DataTableData = DataTableData>({
103
110
  emptyMessage,
104
111
  "aria-label": ariaLabel,
105
112
  compact = true,
113
+ rowDetail = false,
114
+ detailColumns,
115
+ isCellHidden,
106
116
  }: TableInputProps<TData>) {
107
117
  const { t } = useTranslation();
108
118
  const { resolvedForm } = useFormField({ form: formProp });
@@ -138,6 +148,9 @@ export function TableInput<TData extends DataTableData = DataTableData>({
138
148
  emptyMessage: emptyMessage ?? t("No rows"),
139
149
  ariaLabel: resolvedAriaLabel,
140
150
  compact,
151
+ rowDetail,
152
+ detailColumns: detailColumns ?? columns,
153
+ isCellHidden,
141
154
  t,
142
155
  };
143
156
 
@@ -191,6 +204,9 @@ interface TableInputInnerProps<TData extends DataTableData> {
191
204
  emptyMessage: string;
192
205
  ariaLabel: string;
193
206
  compact: boolean;
207
+ rowDetail: boolean;
208
+ detailColumns: DataTableColumn<TData>[];
209
+ isCellHidden?: ChildCellHidden<TData>;
194
210
  t: (key: string, options?: Record<string, unknown>) => string;
195
211
  }
196
212
 
@@ -210,6 +226,9 @@ function TableInputInner<TData extends DataTableData>({
210
226
  emptyMessage,
211
227
  ariaLabel,
212
228
  compact,
229
+ rowDetail,
230
+ detailColumns,
231
+ isCellHidden,
213
232
  t,
214
233
  }: TableInputInnerProps<TData>) {
215
234
  const theme = useTheme();
@@ -219,6 +238,7 @@ function TableInputInner<TData extends DataTableData>({
219
238
  const indexWidth = Math.max(36, chromePx);
220
239
  const actionWidth = chromePx + 16;
221
240
  const [activeRowId, setActiveRowId] = useState<string | null>(null);
241
+ const Detail = RowDetailEditor<TData>;
222
242
 
223
243
  const handleCellChange = useCallback(
224
244
  (rowIndex: number, columnId: string, value: unknown) => {
@@ -250,6 +270,16 @@ function TableInputInner<TData extends DataTableData>({
250
270
  [rows, commit, minRows],
251
271
  );
252
272
 
273
+ const handleRowDetailCommit = useCallback(
274
+ (rowIndex: number, next: TData, changed: string[]) => {
275
+ for (const columnId of changed) {
276
+ onCellChange?.(rowIndex, columnId, next[columnId as keyof TData]);
277
+ }
278
+ commit(rows.map((row, index) => (index === rowIndex ? next : row)));
279
+ },
280
+ [rows, commit, onCellChange],
281
+ );
282
+
253
283
  const canAddRow = !maxRows || rows.length < maxRows;
254
284
  const canDelete = !minRows || rows.length > (minRows || 0);
255
285
 
@@ -277,6 +307,7 @@ function TableInputInner<TData extends DataTableData>({
277
307
  </Text>
278
308
  </View>
279
309
  ))}
310
+ {rowDetail && <View width={actionWidth} />}
280
311
  {showDeleteRow && !readOnly && <View width={actionWidth} />}
281
312
  </XStack>
282
313
  );
@@ -304,6 +335,9 @@ function TableInputInner<TData extends DataTableData>({
304
335
  const field = (col.field || "input") as DataTableField;
305
336
  const fieldProps = col.fieldProps as DataTableFieldProps | undefined;
306
337
 
338
+ if (isCellHidden?.(item, columnId, index)) {
339
+ return <View key={columnId} flex={1} paddingHorizontal="$1" />;
340
+ }
307
341
  return (
308
342
  <View key={columnId} flex={1} paddingHorizontal="$1">
309
343
  <DataTableCell
@@ -321,6 +355,19 @@ function TableInputInner<TData extends DataTableData>({
321
355
  </View>
322
356
  );
323
357
  })}
358
+ {rowDetail && (
359
+ <View width={actionWidth} justifyContent="center" alignItems="center">
360
+ <Detail
361
+ row={item}
362
+ rowIndex={index}
363
+ columns={detailColumns}
364
+ readOnly={readOnly}
365
+ compact={compact}
366
+ isCellHidden={isCellHidden}
367
+ onCommit={(next, changed) => handleRowDetailCommit(index, next, changed)}
368
+ />
369
+ </View>
370
+ )}
324
371
  {showDeleteRow && !readOnly && (
325
372
  <View width={actionWidth} justifyContent="center" alignItems="center">
326
373
  <Button
@@ -355,6 +402,7 @@ function TableInputInner<TData extends DataTableData>({
355
402
  <Skeleton variant="rounded" width="70%" height={16} />
356
403
  </View>
357
404
  ))}
405
+ {rowDetail && <View width={actionWidth} />}
358
406
  {showDeleteRow && !readOnly && <View width={actionWidth} />}
359
407
  </XStack>
360
408
  ))}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * TableInput — the child-table field (the forms↔table bridge).
3
3
  *
4
- * Follows the forms field contract (agent-os/standards/frontend/field-architecture.md):
4
+ * Follows the forms field contract (docs/standards/frontend/field-architecture.md):
5
5
  * - Canonical change prop is `onChange(rows)` (LC-14 ONCHANGE-CANONICAL);
6
6
  * `onDataChange(rows)` survives as a deprecated alias that fires from the
7
7
  * same emit site, and `onCellChange(rowIndex, columnId, value)` is a
@@ -17,6 +17,8 @@
17
17
  * Row identity: added rows get a collision-free generated `id`. TableInput
18
18
  * reorders through the shared kanbanDnd machine (`enableReorder`); Frappe
19
19
  * `idx` renumbering is the ChildTable layer (MPO-22), not this field.
20
+ * `rowDetail` opens one row as a form over `detailColumns` (RowDetailEditor);
21
+ * `isCellHidden` blanks a cell per row, and the caller owns the rule.
20
22
  */
21
23
 
22
24
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -47,14 +49,18 @@ import type {
47
49
  } from "../../types";
48
50
  import { DataTableCell } from "../DataTableCell";
49
51
  import { withInterp } from "../../utils/withInterp";
52
+ import { RowDetailEditor } from "./RowDetailEditor";
50
53
  import {
51
54
  childRowAttrs,
52
55
  childTableKeyAction,
53
56
  generateRowId,
54
57
  isEmptyRows,
55
58
  SKELETON_ROW_COUNT,
59
+ type ChildCellHidden,
56
60
  } from "./shared";
57
61
 
62
+ const chromeColumnIds = new Set(["_reorder", "_rowNumber", "_detail", "_actions"]);
63
+
58
64
  export interface TableInputProps<TData extends DataTableData = DataTableData> {
59
65
  /** Data array for the table (ignored in favor of form state in the form branch) */
60
66
  data?: TData[];
@@ -104,6 +110,12 @@ export interface TableInputProps<TData extends DataTableData = DataTableData> {
104
110
  * this on and writes `idx` on the change payload (MPO-22).
105
111
  */
106
112
  enableReorder?: boolean;
113
+ /** Per-row button that opens the row in a detail editor (MPO-22). */
114
+ rowDetail?: boolean;
115
+ /** Fields the detail editor shows. Defaults to `columns`. */
116
+ detailColumns?: DataTableColumn<TData>[];
117
+ /** Hides one row's cell, in the grid and in the detail editor. */
118
+ isCellHidden?: ChildCellHidden<TData>;
107
119
  }
108
120
 
109
121
  export function TableInput<TData extends DataTableData = DataTableData>({
@@ -128,6 +140,9 @@ export function TableInput<TData extends DataTableData = DataTableData>({
128
140
  "aria-label": ariaLabel,
129
141
  compact = true,
130
142
  enableReorder = false,
143
+ rowDetail = false,
144
+ detailColumns,
145
+ isCellHidden,
131
146
  }: TableInputProps<TData>) {
132
147
  const { t } = useTranslation();
133
148
  const { resolvedForm } = useFormField({ form: formProp });
@@ -164,6 +179,9 @@ export function TableInput<TData extends DataTableData = DataTableData>({
164
179
  ariaLabel: resolvedAriaLabel,
165
180
  compact,
166
181
  enableReorder,
182
+ rowDetail,
183
+ detailColumns: detailColumns ?? columns,
184
+ isCellHidden,
167
185
  t,
168
186
  };
169
187
 
@@ -218,6 +236,9 @@ interface TableInputInnerProps<TData extends DataTableData> {
218
236
  ariaLabel: string;
219
237
  compact: boolean;
220
238
  enableReorder: boolean;
239
+ rowDetail: boolean;
240
+ detailColumns: DataTableColumn<TData>[];
241
+ isCellHidden?: ChildCellHidden<TData>;
221
242
  t: (key: string, options?: Record<string, unknown>) => string;
222
243
  }
223
244
 
@@ -238,6 +259,9 @@ function TableInputInner<TData extends DataTableData>({
238
259
  ariaLabel,
239
260
  compact,
240
261
  enableReorder,
262
+ rowDetail,
263
+ detailColumns,
264
+ isCellHidden,
241
265
  t,
242
266
  }: TableInputInnerProps<TData>) {
243
267
  const theme = useTheme();
@@ -248,6 +272,7 @@ function TableInputInner<TData extends DataTableData>({
248
272
  const pendingFocusRef = useRef<string | null>(null);
249
273
  const gridRef = useRef<HTMLElement | null>(null);
250
274
  const reorderEnabled = enableReorder && !readOnly && !skeleton;
275
+ const Detail = RowDetailEditor<TData>;
251
276
 
252
277
  const firstDataKey = useMemo(() => {
253
278
  const first = columns[0];
@@ -329,6 +354,16 @@ function TableInputInner<TData extends DataTableData>({
329
354
  [rows, commit, minRows],
330
355
  );
331
356
 
357
+ const handleRowDetailCommit = useCallback(
358
+ (rowIndex: number, next: TData, changed: string[]) => {
359
+ for (const columnId of changed) {
360
+ onCellChange?.(rowIndex, columnId, next[columnId as keyof TData]);
361
+ }
362
+ commit(rows.map((row, index) => (index === rowIndex ? next : row)));
363
+ },
364
+ [rows, commit, onCellChange],
365
+ );
366
+
332
367
  useEffect(() => {
333
368
  const id = pendingFocusRef.current;
334
369
  if (!id || typeof document === "undefined") return;
@@ -432,6 +467,7 @@ function TableInputInner<TData extends DataTableData>({
432
467
  ...col,
433
468
  id: columnId,
434
469
  cell: ({ row }) => {
470
+ if (isCellHidden?.(row.original, columnId, row.index)) return null;
435
471
  const value = row.original[columnId as keyof TData];
436
472
  const field = (col.field || "input") as DataTableField;
437
473
  const fieldProps = col.fieldProps as DataTableFieldProps | undefined;
@@ -454,6 +490,25 @@ function TableInputInner<TData extends DataTableData>({
454
490
  } as ColumnDef<TData, unknown>);
455
491
  });
456
492
 
493
+ if (rowDetail) {
494
+ cols.push({
495
+ id: "_detail",
496
+ header: "",
497
+ size: chromePx + 16,
498
+ cell: ({ row }) => (
499
+ <Detail
500
+ row={row.original}
501
+ rowIndex={row.index}
502
+ columns={detailColumns}
503
+ readOnly={readOnly}
504
+ compact={compact}
505
+ isCellHidden={isCellHidden}
506
+ onCommit={(next, changed) => handleRowDetailCommit(row.index, next, changed)}
507
+ />
508
+ ),
509
+ });
510
+ }
511
+
457
512
  if (showDeleteRow && !readOnly) {
458
513
  cols.push({
459
514
  id: "_actions",
@@ -495,6 +550,10 @@ function TableInputInner<TData extends DataTableData>({
495
550
  knobProps.label.fontSize,
496
551
  knobProps.nestedControl.hitSlop,
497
552
  reorderEnabled,
553
+ rowDetail,
554
+ detailColumns,
555
+ isCellHidden,
556
+ handleRowDetailCommit,
498
557
  ]);
499
558
 
500
559
  const table = useReactTable({
@@ -556,10 +615,7 @@ function TableInputInner<TData extends DataTableData>({
556
615
  key={header.id}
557
616
  style={{
558
617
  width: header.getSize(),
559
- ...(isEmpty &&
560
- header.column.id !== "_rowNumber" &&
561
- header.column.id !== "_actions" &&
562
- header.column.id !== "_reorder"
618
+ ...(isEmpty && !chromeColumnIds.has(header.column.id)
563
619
  ? { flexGrow: 1 }
564
620
  : null),
565
621
  }}
@@ -578,7 +634,9 @@ function TableInputInner<TData extends DataTableData>({
578
634
  <Table.Row key={`skeleton-${rowIndex}`}>
579
635
  {table.getAllLeafColumns().map((column) => (
580
636
  <Table.Cell key={column.id}>
581
- {column.id === "_actions" || column.id === "_reorder" ? null : (
637
+ {column.id === "_actions" ||
638
+ column.id === "_reorder" ||
639
+ column.id === "_detail" ? null : (
582
640
  <Skeleton
583
641
  variant="rounded"
584
642
  width={column.id === "_rowNumber" ? 16 : "70%"}
@@ -617,10 +675,7 @@ function TableInputInner<TData extends DataTableData>({
617
675
  >)}
618
676
  >
619
677
  {row.getVisibleCells().map((cell) => {
620
- const isData =
621
- cell.column.id !== "_rowNumber" &&
622
- cell.column.id !== "_actions" &&
623
- cell.column.id !== "_reorder";
678
+ const isData = !chromeColumnIds.has(cell.column.id);
624
679
  const field = columns.find((col) => {
625
680
  const id =
626
681
  typeof col.accessorKey === "string"
@@ -1,5 +1,7 @@
1
1
  /** Shared TableInput internals (web + native). */
2
2
 
3
+ import type { DataTableColumn, DataTableData } from "../../types";
4
+
3
5
  let rowIdCounter = 0;
4
6
 
5
7
  /**
@@ -75,3 +77,30 @@ export function childTableKeyAction(
75
77
  if (next < 0 || next >= ctx.rowCount) return null;
76
78
  return { type: "move", rowIndex: next, columnId: ctx.columnId };
77
79
  }
80
+
81
+ /**
82
+ * Per-row cell visibility (Frappe child `depends_on`, evaluated by the
83
+ * caller). True hides that cell's editor in the grid and its field in the
84
+ * row detail editor.
85
+ */
86
+ export type ChildCellHidden<TData extends DataTableData = DataTableData> = (
87
+ row: TData,
88
+ columnId: string,
89
+ rowIndex: number,
90
+ ) => boolean;
91
+
92
+ /** Row-data key a column reads (accessorKey, else id). */
93
+ export function columnKey<TData extends DataTableData>(col: DataTableColumn<TData>): string {
94
+ return typeof col.accessorKey === "string" ? col.accessorKey : col.id || String(col.accessorKey);
95
+ }
96
+
97
+ /** Column keys whose value differs between two versions of a row. */
98
+ export function changedColumns<TData extends DataTableData>(
99
+ before: TData,
100
+ after: TData,
101
+ columns: DataTableColumn<TData>[],
102
+ ): string[] {
103
+ return columns
104
+ .map(columnKey)
105
+ .filter((key) => !Object.is(before[key as keyof TData], after[key as keyof TData]));
106
+ }
@@ -0,0 +1,172 @@
1
+ /**
2
+ * MPO-221: a table dropdown is a FloatingPanel (the cover family, MPO-38),
3
+ * never Tamagui's Popover. Each surface opens the FloatingPanel viewport from
4
+ * its trigger, and no component source under components/ imports Popover.
5
+ */
6
+
7
+ import { readdirSync, readFileSync, statSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
10
+ import { fireEvent, screen, waitFor } from "@testing-library/react";
11
+ import type { ReactElement } from "react";
12
+ import { describe, expect, it, vi } from "vitest";
13
+ import { ColumnHeaderFilter } from "./ColumnHeaderFilter";
14
+ import { DataTableDateFilter } from "./DataTableDateFilter";
15
+ import { DataTableExport } from "./DataTableExport";
16
+ import { DataTableGroupingConfig } from "./DataTableGrouping";
17
+ import { DataTableSearch } from "./DataTableSearch";
18
+ import { DataTableToolbar } from "./DataTableToolbar";
19
+ import { DataTableViewOptions } from "./DataTableViewOptions";
20
+
21
+ const panel = () =>
22
+ document.querySelector('[data-testid="floating-panel-viewport"]') as HTMLElement | null;
23
+
24
+ const rows = [{ name: "Ada", city: "London" }];
25
+ const columns = [
26
+ { accessorKey: "name", header: "Name", id: "name", searchable: true },
27
+ { accessorKey: "city", header: "City", id: "city", searchable: true },
28
+ ];
29
+
30
+ interface Surface {
31
+ name: string;
32
+ ui: ReactElement;
33
+ trigger: () => HTMLElement;
34
+ content: string;
35
+ }
36
+
37
+ const surfaces: Surface[] = [
38
+ {
39
+ name: "column header filter",
40
+ ui: (
41
+ <ColumnHeaderFilter
42
+ columnId="name"
43
+ columnLabel="Name"
44
+ columnType="text"
45
+ onFilterChange={vi.fn()}
46
+ />
47
+ ),
48
+ trigger: () => screen.getByLabelText("Filter column Name"),
49
+ content: "Filter: Name",
50
+ },
51
+ {
52
+ name: "export options",
53
+ ui: <DataTableExport data={rows} columns={columns} />,
54
+ trigger: () => screen.getByRole("button", { name: "Export options" }),
55
+ content: "Export Options",
56
+ },
57
+ {
58
+ name: "grouping",
59
+ ui: (
60
+ <DataTableGroupingConfig
61
+ columns={columns}
62
+ currentConfig={{ columns: [], aggregations: [] }}
63
+ onConfigChange={vi.fn()}
64
+ />
65
+ ),
66
+ trigger: () => screen.getByLabelText("Grouping configuration"),
67
+ content: "Grouping & Aggregation",
68
+ },
69
+ {
70
+ name: "date filter",
71
+ ui: <DataTableDateFilter placeholder="Pick a date" />,
72
+ trigger: () => screen.getByText("Pick a date"),
73
+ content: "Date Filter",
74
+ },
75
+ {
76
+ name: "search options",
77
+ ui: <DataTableSearch data={rows} columns={columns} onSearchResults={vi.fn()} />,
78
+ trigger: () => screen.getByRole("button", { name: "Search options" }),
79
+ content: "Search Options",
80
+ },
81
+ {
82
+ name: "toolbar save-view",
83
+ ui: (
84
+ <DataTableToolbar
85
+ onSaveView={vi.fn()}
86
+ onLoadView={vi.fn()}
87
+ views={[]}
88
+ enableViews
89
+ showViewSelector
90
+ />
91
+ ),
92
+ trigger: () => screen.getByRole("button", { name: "Save view" }),
93
+ content: "",
94
+ },
95
+ {
96
+ name: "view options save-view",
97
+ ui: (
98
+ <DataTableViewOptions
99
+ columns={[{ id: "name", label: "Name", visible: true, order: 0 }]}
100
+ onSaveView={vi.fn()}
101
+ onLoadView={vi.fn()}
102
+ onResetToDefault={vi.fn()}
103
+ showViewManagement
104
+ />
105
+ ),
106
+ trigger: () => screen.getByText("Save Current View"),
107
+ content: "Save View",
108
+ },
109
+ ];
110
+
111
+ describe("table dropdowns open FloatingPanel (MPO-221)", () => {
112
+ it.each(surfaces)("$name opens the FloatingPanel viewport", async ({ ui, trigger, content }) => {
113
+ renderWithProviders(ui);
114
+ expect(panel()).toBeNull();
115
+ fireEvent.click(trigger());
116
+ await waitFor(() => expect(panel()).not.toBeNull());
117
+ if (content) expect(panel()!.textContent).toContain(content);
118
+ // A compose-class editor declares its dismissal contract (LC-21 VERIFY).
119
+ expect(panel()!.querySelector('[data-dismiss-class="compose"]')).not.toBeNull();
120
+ });
121
+
122
+ it.each(surfaces)("$name closes on Escape", async ({ ui, trigger }) => {
123
+ renderWithProviders(ui);
124
+ fireEvent.click(trigger());
125
+ await waitFor(() => expect(panel()).not.toBeNull());
126
+ fireEvent.keyDown(document.activeElement ?? document.body, { key: "Escape" });
127
+ await waitFor(() => expect(panel()).toBeNull());
128
+ });
129
+
130
+ it("the save-view panel holds the view-name field and its two actions", async () => {
131
+ const onSaveView = vi.fn();
132
+ renderWithProviders(
133
+ <DataTableToolbar
134
+ onSaveView={onSaveView}
135
+ onLoadView={vi.fn()}
136
+ views={[]}
137
+ enableViews
138
+ showViewSelector
139
+ />,
140
+ );
141
+ fireEvent.click(screen.getByRole("button", { name: "Save view" }));
142
+ await waitFor(() => expect(panel()).not.toBeNull());
143
+ fireEvent.change(screen.getByLabelText("View name"), { target: { value: "Mine" } });
144
+ fireEvent.click(screen.getByRole("button", { name: "Save" }));
145
+ expect(onSaveView).toHaveBeenCalledWith("Mine");
146
+ await waitFor(() => expect(panel()).toBeNull());
147
+ });
148
+ });
149
+
150
+ function componentSources(dir: string): string[] {
151
+ return readdirSync(dir).flatMap((entry) => {
152
+ const path = join(dir, entry);
153
+ if (statSync(path).isDirectory()) return componentSources(path);
154
+ return /\.tsx?$/.test(entry) && !/\.(spec|stories)\.tsx?$/.test(entry) ? [path] : [];
155
+ });
156
+ }
157
+
158
+ describe("no Tamagui Popover in the table (MPO-221)", () => {
159
+ it("no component source imports Popover from tamagui", () => {
160
+ const offenders = componentSources(__dirname).filter((file) =>
161
+ /import\s*{[^}]*\bPopover\b[^}]*}\s*from\s*["']tamagui["']/.test(readFileSync(file, "utf8")),
162
+ );
163
+ expect(offenders).toEqual([]);
164
+ });
165
+
166
+ it("no component source mounts the components PopoverContent", () => {
167
+ const offenders = componentSources(__dirname).filter((file) =>
168
+ /\bPopoverContent\b/.test(readFileSync(file, "utf8")),
169
+ );
170
+ expect(offenders).toEqual([]);
171
+ });
172
+ });
@@ -111,3 +111,156 @@ describe("useDataTable controlledState adoption", () => {
111
111
  expect(result.current.state.filters).toEqual([]);
112
112
  });
113
113
  });
114
+
115
+ describe("useDataTable column width normalization", () => {
116
+ type Row = (typeof data)[number];
117
+
118
+ it.each<[Partial<DataTableColumn<Row>>, number]>([
119
+ [{ width: 320 }, 320],
120
+ [{ width: 320, size: 90 }, 90],
121
+ [{ width: 320, minWidth: 200, maxWidth: 240 }, 240],
122
+ [{ width: 320, size: 90, minWidth: 200, minSize: 60, maxWidth: 80, maxSize: 100 }, 90],
123
+ [{ width: 320, size: 180, maxWidth: 140 }, 140],
124
+ [{ width: 125.5 }, 125.5],
125
+ [{ width: 0 }, 20],
126
+ [{ width: 0, minWidth: 0 }, 0],
127
+ [{ width: 320, size: 0, minSize: 0 }, 0],
128
+ [{ width: Number.NaN }, 150],
129
+ [{ width: Number.POSITIVE_INFINITY }, 150],
130
+ [{ width: -1 }, 150],
131
+ [{ minWidth: Number.NaN, maxWidth: -1 }, 150],
132
+ ])("resolves %j to %s through the real table", (sizing, expected) => {
133
+ const { result } = renderHook(() =>
134
+ useDataTable({ data, columns: [{ id: "name", accessorKey: "name", ...sizing }] }),
135
+ );
136
+ expect(result.current.table.getColumn("name")?.getSize()).toBe(expected);
137
+ });
138
+
139
+ it("preserves input objects and normalizes grouped leaves without distributing group width", () => {
140
+ const leaf: DataTableColumn<Row> = { id: "name", accessorKey: "name", width: 320 };
141
+ const group: DataTableColumn<Row> = {
142
+ id: "group",
143
+ header: "Group",
144
+ width: 900,
145
+ columns: [leaf],
146
+ };
147
+ const plain: DataTableColumn<Row> = { id: "status", accessorKey: "status" };
148
+ Object.freeze(leaf);
149
+ Object.freeze(group.columns);
150
+ Object.freeze(group);
151
+ Object.freeze(plain);
152
+ const source = [group, plain];
153
+ Object.freeze(source);
154
+ const { result, rerender } = renderHook(() => useDataTable({ data, columns: source }));
155
+ expect(result.current.table.getColumn("name")?.getSize()).toBe(320);
156
+ expect(leaf).not.toHaveProperty("size");
157
+ expect(group.columns?.[0]).toBe(leaf);
158
+ expect(result.current.table.options.columns[1]).toBe(plain);
159
+ const normalized = result.current.table.options.columns;
160
+ rerender();
161
+ expect(result.current.table.options.columns).toBe(normalized);
162
+ });
163
+
164
+ it("keeps sizing state ahead of updated declarations and resets to the current definition", () => {
165
+ const { result, rerender } = renderHook(
166
+ ({ width }) => useDataTable({ data, columns: [{ id: "name", accessorKey: "name", width }] }),
167
+ { initialProps: { width: 320 } },
168
+ );
169
+ expect(result.current.table.getColumn("name")?.getSize()).toBe(320);
170
+ act(() => result.current.actions.setColumnSizing({ name: 210 }));
171
+ rerender({ width: 400 });
172
+ expect(result.current.table.getColumn("name")?.getSize()).toBe(210);
173
+ act(() => result.current.actions.resetToDefault());
174
+ expect(result.current.table.getColumn("name")?.getSize()).toBe(400);
175
+ });
176
+
177
+ it("loads existing view width state without renaming its persistence field", () => {
178
+ const { result } = renderHook(() =>
179
+ useDataTable({
180
+ data,
181
+ columns: [{ id: "name", accessorKey: "name", width: 320 }],
182
+ initialState: {
183
+ views: [
184
+ {
185
+ id: "saved",
186
+ name: "Saved",
187
+ columns: { name: { id: "name", visible: true, width: 205, order: 0 } },
188
+ filters: [],
189
+ sorting: [],
190
+ pagination: { pageIndex: 0, pageSize: 10 },
191
+ },
192
+ ],
193
+ },
194
+ }),
195
+ );
196
+ act(() => result.current.actions.loadView("saved"));
197
+ expect(result.current.table.getColumn("name")?.getSize()).toBe(205);
198
+ expect(result.current.state.columnSizing).toEqual({ name: 205 });
199
+ act(() => result.current.actions.resetToDefault());
200
+ expect(result.current.table.getColumn("name")?.getSize()).toBe(320);
201
+ });
202
+
203
+ it("preserves sorting/filter aliases and explicit overrides alongside width", () => {
204
+ const sortingFn = vi.fn(() => 0);
205
+ const { result } = renderHook(() =>
206
+ useDataTable({
207
+ data,
208
+ columns: [
209
+ { id: "name", accessorKey: "name", width: 320, sortable: false, filterable: false },
210
+ {
211
+ id: "status",
212
+ accessorKey: "status",
213
+ width: 90,
214
+ sortable: false,
215
+ enableSorting: true,
216
+ sortingFn,
217
+ },
218
+ ],
219
+ }),
220
+ );
221
+ expect(result.current.table.getColumn("name")?.getSize()).toBe(320);
222
+ expect(result.current.table.getColumn("name")?.getCanSort()).toBe(false);
223
+ expect(result.current.table.getColumn("name")?.getCanFilter()).toBe(false);
224
+ expect(result.current.table.getColumn("status")?.getCanSort()).toBe(true);
225
+ expect(result.current.table.getColumn("status")?.columnDef.sortingFn).toBe(sortingFn);
226
+ });
227
+ });
228
+
229
+ // ---------------------------------------------------------------------------
230
+ // columnOrder seeding — id-less columns keep the caller's declared order
231
+ // (MPO-292: `col.id || ""` matched no column, so every id-less column fell
232
+ // behind every id'd one)
233
+ // ---------------------------------------------------------------------------
234
+
235
+ describe("useDataTable columnOrder seeding", () => {
236
+ type Row = { id: number; name: string; status: string; owner: { name: string } };
237
+ const rows: Row[] = [
238
+ { id: 1, name: "Alice", status: "Active", owner: { name: "Ops" } },
239
+ { id: 2, name: "Bob", status: "Lead", owner: { name: "Sales" } },
240
+ ];
241
+
242
+ it("seeds columnOrder with TanStack's own key for every column, id or not", () => {
243
+ const columns = [
244
+ { accessorKey: "name", header: "Name" },
245
+ { id: "_actions", header: "Actions", cell: () => null },
246
+ { accessorKey: "owner.name", header: "Owner" },
247
+ { accessorFn: (row: Row) => row.status, header: "Status" },
248
+ ] as DataTableColumn<Row>[];
249
+ const { result } = renderHook(() => useDataTable({ data: rows, columns }));
250
+ const order = result.current.state.columnOrder;
251
+ expect(order).toEqual(["name", "_actions", "owner_name", "Status"]);
252
+ expect(new Set(order).size).toBe(columns.length);
253
+ for (const key of order) expect(result.current.table.getColumn(key)?.id).toBe(key);
254
+ });
255
+
256
+ it("renders getVisibleCells() in the declared order when id'd and id-less columns mix", () => {
257
+ const columns = [
258
+ { accessorKey: "name", header: "Name" },
259
+ { id: "_actions", header: "Actions", cell: () => null },
260
+ { accessorKey: "status", header: "Status" },
261
+ ] as DataTableColumn<Row>[];
262
+ const { result } = renderHook(() => useDataTable({ data: rows, columns }));
263
+ const cells = result.current.table.getRowModel().rows[0].getVisibleCells();
264
+ expect(cells.map((cell) => cell.column.id)).toEqual(["name", "_actions", "status"]);
265
+ });
266
+ });