@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
@@ -1,6 +1,7 @@
1
+ import { Checkbox } from "@multiplatform.one/forms";
1
2
  import { action } from "@multiplatform.one/storybook";
2
3
  import React from "react";
3
- import { Checkbox, Text, XStack, YStack } from "tamagui";
4
+ import { Text, XStack, YStack } from "tamagui";
4
5
  import { type BulkAction, DataTableBulkActions, useRowSelection } from "./index";
5
6
 
6
7
  // Define the data type
@@ -309,7 +310,7 @@ export const WithRowSelectionHook = () => {
309
310
  >
310
311
  <Checkbox
311
312
  checked={selection.isSelected(task)}
312
- onCheckedChange={() => {}} // Handled by onPress
313
+ onChange={() => {}} // Handled by onPress
313
314
  />
314
315
  <YStack flex={1}>
315
316
  <Text fontWeight="500">{task.title}</Text>
@@ -324,9 +325,9 @@ export const WithRowSelectionHook = () => {
324
325
  <XStack gap="$3" alignItems="center">
325
326
  <Checkbox
326
327
  checked={selection.isAllSelected || selection.isIndeterminate}
327
- onCheckedChange={() => selection.toggleAll()}
328
+ onChange={() => selection.toggleAll()}
329
+ label="Select All"
328
330
  />
329
- <Text>Select All</Text>
330
331
  <Text fontSize="$2" color="$gray11">
331
332
  ({selection.selectedItems.length} selected)
332
333
  </Text>
@@ -1,23 +1,30 @@
1
1
  /**
2
2
  * CellModalEditor — wraps tall field types (FileUpload, Signature, RichText,
3
- * etc.) in a Tamagui Dialog that opens on cell click. Cell shows a compact
4
- * `FieldDisplay` summary; click opens a centered modal with the full editor.
3
+ * etc.) in a forms FloatingPanel that opens on cell click. The cell shows a
4
+ * compact `FieldDisplay` summary; a click opens the full editor in a panel
5
+ * that attaches to the cell it was opened from (MPO-38 cover contract), the
6
+ * same FloatingPanel the native variant uses (a sheet there and at 640 and
7
+ * below). It never grows the row (LC-06).
5
8
  *
6
- * The modal is a staging buffer (UX-023 / Axiom 7 AGENCY): the inner editor
9
+ * The panel is a staging buffer (UX-023 / Axiom 7 AGENCY): the inner editor
7
10
  * is wired to a local draft seeded from the committed value on open — nothing
8
- * reaches the row while the modal is open. "Done" commits the draft once via
11
+ * reaches the row while the panel is open. "Done" commits the draft once via
9
12
  * `onCommit`; an outside click also commits (LC-21: a cell editor's draft
10
13
  * still commits on the outside-click close); "Cancel" and Escape discard the
11
14
  * draft and leave the row untouched. Cancel is never disabled (LC-30) and the
12
- * footer stays within the two-action dialog budget (LC-31).
15
+ * footer stays within the two-action budget (LC-31).
13
16
  */
14
17
 
15
- import { DialogContent, DialogOverlay } from "@multiplatform.one/components";
16
- import { Button, FieldDisplay, type FieldDisplayType } from "@multiplatform.one/forms";
18
+ import {
19
+ Button,
20
+ FieldDisplay,
21
+ FloatingPanel,
22
+ type FieldDisplayType,
23
+ } from "@multiplatform.one/forms";
17
24
  import { TableCellContext } from "@multiplatform.one/table-primitives";
18
25
  import { useResolvedKnobs } from "@multiplatform.one/theme";
19
- import { useCallback, useRef, useState, type ReactNode } from "react";
20
- import { Dialog, View, XStack, YStack, isWeb } from "tamagui";
26
+ import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
27
+ import { Text, View, XStack, YStack, isWeb } from "tamagui";
21
28
  import { t } from "../../shared/t";
22
29
 
23
30
  export interface CellModalEditorProps {
@@ -29,12 +36,12 @@ export interface CellModalEditorProps {
29
36
  /** Commits the staged draft to the row. Fires at most once per session. */
30
37
  onCommit: (value: any) => void;
31
38
  /**
32
- * Full editor mounted inside the modal, wired to the staged draft: render
39
+ * Full editor mounted inside the panel, wired to the staged draft: render
33
40
  * with `draftValue` and report edits through `onDraftChange` — never
34
41
  * straight to the row.
35
42
  */
36
43
  children: (draftValue: any, onDraftChange: (value: any) => void) => ReactNode;
37
- /** Title shown in the modal header. */
44
+ /** Title shown in the panel header. */
38
45
  title?: string;
39
46
  /**
40
47
  * Accessible name for the chromeless cell trigger (column header).
@@ -56,7 +63,7 @@ export function CellModalEditor({
56
63
  }: CellModalEditorProps) {
57
64
  const { knobProps } = useResolvedKnobs();
58
65
  const [open, setOpen] = useState(false);
59
- // Draft session: exists only while the modal is open, seeded from the
66
+ // Draft session: exists only while the panel is open, seeded from the
60
67
  // committed value so the editor starts at the cell's current value
61
68
  // (Axiom 11 VALUE IS DATA).
62
69
  const [draft, setDraft] = useState<{ value: any } | null>(null);
@@ -67,7 +74,7 @@ export function CellModalEditor({
67
74
  const onCommitRef = useRef(onCommit);
68
75
  onCommitRef.current = onCommit;
69
76
  // Escape / Cancel flag the close as a discard before onOpenChange(false)
70
- // lands; every other close path (Done, overlay/outside click) commits.
77
+ // lands; every other close path (Done, outside click) commits.
71
78
  const discardRef = useRef(false);
72
79
 
73
80
  const handleOpen = useCallback(() => {
@@ -97,6 +104,18 @@ export function CellModalEditor({
97
104
  [handleOpen],
98
105
  );
99
106
 
107
+ // FloatingPanel reports every close as onOpenChange(false). Escape is a
108
+ // dismissal, not a confirm, so it is flagged in the capture phase, before
109
+ // the panel's own Escape listener closes it.
110
+ useEffect(() => {
111
+ if (!open || !isWeb || typeof document === "undefined") return;
112
+ const flagEscape = (event: KeyboardEvent) => {
113
+ if (event.key === "Escape") discardRef.current = true;
114
+ };
115
+ document.addEventListener("keydown", flagEscape, true);
116
+ return () => document.removeEventListener("keydown", flagEscape, true);
117
+ }, [open]);
118
+
100
119
  const handleCancel = useCallback(() => {
101
120
  discardRef.current = true;
102
121
  handleOpenChange(false);
@@ -108,76 +127,77 @@ export function CellModalEditor({
108
127
  }, []);
109
128
 
110
129
  const triggerLocked = disabled || readOnly;
111
- const handleTriggerKeyDown = (event: { key?: string; preventDefault?: () => void }) => {
130
+ const handleTriggerKeyDown = (event: {
131
+ key?: string;
132
+ preventDefault?: () => void;
133
+ stopPropagation?: () => void;
134
+ }) => {
112
135
  if (triggerLocked) return;
113
136
  if (event.key === "Enter" || event.key === " ") {
114
137
  event.preventDefault?.();
138
+ event.stopPropagation?.();
115
139
  handleOpen();
116
140
  }
117
141
  };
118
142
 
143
+ const trigger = (
144
+ <View
145
+ cursor={triggerLocked ? "default" : "pointer"}
146
+ opacity={disabled ? 0.5 : 1}
147
+ flexDirection="row"
148
+ alignItems="center"
149
+ width="100%"
150
+ paddingHorizontal="$1"
151
+ paddingVertical="$1"
152
+ hoverStyle={!triggerLocked ? { backgroundColor: "$backgroundHover" } : undefined}
153
+ // LC-71: parent CellWrapper paints the one ring on the cell boundary.
154
+ outlineWidth={0}
155
+ focusVisibleStyle={{ outlineWidth: 0 }}
156
+ {...(isWeb && !triggerLocked
157
+ ? {
158
+ role: "button" as const,
159
+ tabIndex: 0,
160
+ "aria-label": label,
161
+ "aria-haspopup": "dialog" as const,
162
+ "aria-expanded": open,
163
+ onKeyDown: handleTriggerKeyDown,
164
+ }
165
+ : undefined)}
166
+ >
167
+ <FieldDisplay field={field} value={value} fieldProps={fieldProps} />
168
+ </View>
169
+ );
170
+
119
171
  return (
120
- <>
121
- <View
122
- onPress={handleOpen}
123
- cursor={triggerLocked ? "default" : "pointer"}
124
- opacity={disabled ? 0.5 : 1}
125
- flexDirection="row"
126
- alignItems="center"
127
- width="100%"
128
- paddingHorizontal="$1"
129
- paddingVertical="$1"
130
- hoverStyle={!triggerLocked ? { backgroundColor: "$backgroundHover" } : undefined}
131
- // LC-71: parent CellWrapper paints the one ring on the cell boundary.
132
- outlineWidth={0}
133
- focusVisibleStyle={{ outlineWidth: 0 }}
134
- {...(isWeb && !triggerLocked
135
- ? {
136
- role: "button" as const,
137
- tabIndex: 0,
138
- "aria-label": label,
139
- "aria-haspopup": "dialog" as const,
140
- "aria-expanded": open,
141
- onKeyDown: handleTriggerKeyDown,
142
- }
143
- : undefined)}
172
+ <FloatingPanel
173
+ open={open}
174
+ onOpenChange={handleOpenChange}
175
+ disabled={triggerLocked}
176
+ trigger={trigger}
177
+ >
178
+ <YStack
179
+ {...knobProps.gap}
180
+ width={720}
181
+ maxWidth="90vw"
182
+ data-testid="cell-modal-editor-panel"
183
+ data-dismiss-class="compose"
144
184
  >
145
- <FieldDisplay field={field} value={value} fieldProps={fieldProps} />
146
- </View>
147
- <Dialog open={open} onOpenChange={handleOpenChange} modal>
148
- <Dialog.Portal>
149
- <DialogOverlay opacity={0.5} />
150
- <DialogContent
151
- maxWidth={720}
152
- maxHeight="80vh"
153
- width="90vw"
154
- // Escape is a dismissal, not a confirm: mark the pending close as
155
- // a discard, then let the Dialog close itself.
156
- onEscapeKeyDown={() => {
157
- discardRef.current = true;
158
- }}
159
- >
160
- <Dialog.Title {...knobProps.heading}>{title ?? t("Edit")}</Dialog.Title>
161
- {/* flexShrink (not flex-basis 0): in the auto-height dialog a
162
- basis-0 wrapper collapses and its children paint over the
163
- footer / past the dialog frame (LC-10). The context reset
164
- restores full field chrome — the modal is not a table cell,
165
- but React context flows through the Dialog portal. */}
166
- <YStack flexShrink={1} minHeight={0}>
167
- <TableCellContext.Provider value={{ inTableCell: false }}>
168
- {children(draft ? draft.value : value, handleDraftChange)}
169
- </TableCellContext.Provider>
170
- </YStack>
171
- <XStack {...knobProps.gap} justifyContent="flex-end">
172
- {/* LC-30 CANCEL-ALWAYS-LIVE: the discard exit never disables. */}
173
- <Button onPress={handleCancel}>{t("Cancel")}</Button>
174
- <Button theme="accent" onPress={() => handleOpenChange(false)}>
175
- {t("Done")}
176
- </Button>
177
- </XStack>
178
- </DialogContent>
179
- </Dialog.Portal>
180
- </Dialog>
181
- </>
185
+ <Text {...knobProps.heading}>{title ?? t("Edit")}</Text>
186
+ {/* The panel is not a table cell: restore full field chrome. React
187
+ context flows in because the panel renders inline (no portal). */}
188
+ <YStack flexShrink={1} minHeight={0}>
189
+ <TableCellContext.Provider value={{ inTableCell: false }}>
190
+ {children(draft ? draft.value : value, handleDraftChange)}
191
+ </TableCellContext.Provider>
192
+ </YStack>
193
+ <XStack {...knobProps.gap} justifyContent="flex-end">
194
+ {/* LC-30 CANCEL-ALWAYS-LIVE: the discard exit never disables. */}
195
+ <Button onPress={handleCancel}>{t("Cancel")}</Button>
196
+ <Button theme="accent" onPress={() => handleOpenChange(false)}>
197
+ {t("Done")}
198
+ </Button>
199
+ </XStack>
200
+ </YStack>
201
+ </FloatingPanel>
182
202
  );
183
203
  }
@@ -153,7 +153,8 @@ function MockRadioGroup({ value, onValueChange, options }: any) {
153
153
 
154
154
  /**
155
155
  * Functional FloatingPanel fake: trigger toggles open; children render only
156
- * while open (mirrors the popover/sheet contract CellOptionsEditor relies on).
156
+ * while open; Escape inside closes it (mirrors the popover/sheet contract
157
+ * CellOptionsEditor and CellModalEditor rely on).
157
158
  */
158
159
  function MockFloatingPanel({ open, onOpenChange, trigger, children, disabled }: any) {
159
160
  return (
@@ -166,7 +167,16 @@ function MockFloatingPanel({ open, onOpenChange, trigger, children, disabled }:
166
167
  >
167
168
  {trigger}
168
169
  </div>
169
- {open ? <div data-testid="floating-panel-content">{children}</div> : null}
170
+ {open ? (
171
+ <div
172
+ data-testid="floating-panel-content"
173
+ onKeyDown={(event) => {
174
+ if (event.key === "Escape") onOpenChange?.(false);
175
+ }}
176
+ >
177
+ {children}
178
+ </div>
179
+ ) : null}
170
180
  </div>
171
181
  );
172
182
  }
@@ -1,33 +1,17 @@
1
1
  /**
2
- * CellModalEditor design-law tests — the modal is SF-OVERLAY / E-OVERLAY.
3
- *
4
- * The house DialogContent owns elevatedSurface + containerRadius + transition
5
- * (LC-18). A fixed 1px border on Dialog.Content was the recorded GAP; this
6
- * spec locks the recipe path so it cannot regress to a handwritten frame.
2
+ * CellModalEditor on web (MPO-221): the tall-field editor is a FloatingPanel
3
+ * attached to the cell it was opened from, the same primitive the native
4
+ * variant uses, not a centered Dialog. The panel owns the SF-OVERLAY /
5
+ * E-OVERLAY surface (elevatedSurface); the staging contract rides on top.
7
6
  */
8
7
 
9
8
  import React from "react";
9
+ import { readFileSync } from "node:fs";
10
+ import { join } from "node:path";
10
11
  import { renderWithProviders } from "@multiplatform.one/test-utils";
11
12
  import { fireEvent, screen, waitFor } from "@testing-library/react";
12
13
  import { describe, expect, it, vi } from "vitest";
13
14
 
14
- const dialogContentProps: Record<string, unknown>[] = [];
15
-
16
- vi.mock("@multiplatform.one/components", async (importOriginal) => {
17
- const actual = await importOriginal<typeof import("@multiplatform.one/components")>();
18
- return {
19
- ...actual,
20
- DialogContent: (props: Record<string, unknown>) => {
21
- dialogContentProps.push(props);
22
- return React.createElement(
23
- "div",
24
- { "data-testid": "cell-modal-dialog-content" },
25
- props.children as React.ReactNode,
26
- );
27
- },
28
- };
29
- });
30
-
31
15
  vi.mock("@multiplatform.one/forms", async (importOriginal) => ({
32
16
  ...(await importOriginal<typeof import("@multiplatform.one/forms")>()),
33
17
  FieldDisplay: (props: { field?: string }) =>
@@ -47,36 +31,78 @@ vi.mock("@multiplatform.one/forms", async (importOriginal) => ({
47
31
 
48
32
  import { CellModalEditor } from "./CellModalEditor";
49
33
 
50
- describe("CellModalEditor — overlay recipes", () => {
51
- it("opens DialogContent without a fixed 1px border (SF-OVERLAY recipe path)", async () => {
52
- dialogContentProps.length = 0;
53
- renderWithProviders(
54
- <CellModalEditor
55
- field="textarea"
56
- value="hello"
57
- onCommit={vi.fn()}
58
- title="Edit text"
59
- label="Notes"
60
- >
61
- {(draft, onDraftChange) =>
62
- React.createElement("textarea", {
63
- "data-testid": "draft-editor",
64
- value: draft,
65
- onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) =>
66
- onDraftChange(event.target.value),
67
- })
68
- }
69
- </CellModalEditor>,
70
- );
34
+ const panel = () =>
35
+ document.querySelector('[data-testid="floating-panel-viewport"]') as HTMLElement | null;
71
36
 
72
- fireEvent.click(screen.getByTestId("mock-FieldDisplay"));
73
- await waitFor(() => expect(screen.getByTestId("cell-modal-dialog-content")).toBeTruthy());
37
+ function renderEditor(onCommit = vi.fn()) {
38
+ const utils = renderWithProviders(
39
+ <CellModalEditor
40
+ field="textarea"
41
+ value="hello"
42
+ onCommit={onCommit}
43
+ title="Edit text"
44
+ label="Notes"
45
+ >
46
+ {(draft, onDraftChange) =>
47
+ React.createElement("textarea", {
48
+ "data-testid": "draft-editor",
49
+ value: draft,
50
+ onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) =>
51
+ onDraftChange(event.target.value),
52
+ })
53
+ }
54
+ </CellModalEditor>,
55
+ );
56
+ return { ...utils, onCommit };
57
+ }
74
58
 
75
- const frame = dialogContentProps.at(-1);
76
- expect(frame).toBeTruthy();
77
- expect(frame?.borderWidth).toBeUndefined();
78
- expect(frame?.borderColor).toBeUndefined();
59
+ describe("CellModalEditor on web — FloatingPanel, not a Dialog (MPO-221)", () => {
60
+ it("opens the editor inside the FloatingPanel viewport", async () => {
61
+ renderEditor();
62
+ fireEvent.click(screen.getByTestId("mock-FieldDisplay"));
63
+ await waitFor(() => expect(panel()).not.toBeNull());
64
+ expect(panel()!.contains(screen.getByTestId("draft-editor"))).toBe(true);
65
+ expect(panel()!.textContent).toContain("Edit text");
66
+ expect(document.querySelector('[role="dialog"]')).toBeNull();
79
67
  expect(screen.getByRole("button", { name: "Cancel" })).not.toBeDisabled();
80
68
  expect(screen.getByRole("button", { name: "Done" })).toBeTruthy();
81
69
  });
70
+
71
+ it("anchors to the cell trigger: the panel's reference wraps the cell face", async () => {
72
+ renderEditor();
73
+ const face = screen.getByTestId("mock-FieldDisplay");
74
+ expect(face.closest("[data-fp-reference]")).not.toBeNull();
75
+ fireEvent.click(face);
76
+ await waitFor(() => expect(panel()).not.toBeNull());
77
+ expect(panel()!.style.position).toBe("fixed");
78
+ });
79
+
80
+ it("an outside click commits the draft (LC-21)", async () => {
81
+ const { onCommit } = renderEditor();
82
+ fireEvent.click(screen.getByTestId("mock-FieldDisplay"));
83
+ await waitFor(() => expect(panel()).not.toBeNull());
84
+ fireEvent.change(screen.getByTestId("draft-editor"), { target: { value: "changed" } });
85
+ fireEvent.click(document.body);
86
+ await waitFor(() => expect(panel()).toBeNull());
87
+ expect(onCommit).toHaveBeenCalledTimes(1);
88
+ expect(onCommit).toHaveBeenCalledWith("changed");
89
+ });
90
+
91
+ it("Escape discards the draft", async () => {
92
+ const { onCommit } = renderEditor();
93
+ fireEvent.click(screen.getByTestId("mock-FieldDisplay"));
94
+ await waitFor(() => expect(panel()).not.toBeNull());
95
+ const editor = screen.getByTestId("draft-editor");
96
+ fireEvent.change(editor, { target: { value: "changed" } });
97
+ fireEvent.keyDown(editor, { key: "Escape" });
98
+ await waitFor(() => expect(panel()).toBeNull());
99
+ expect(onCommit).not.toHaveBeenCalled();
100
+ });
101
+
102
+ it("the web source no longer mounts a Tamagui Dialog", () => {
103
+ const source = readFileSync(join(__dirname, "CellModalEditor.tsx"), "utf8");
104
+ expect(source).not.toMatch(/\bDialog\b.*from "tamagui"/);
105
+ expect(source).not.toMatch(/DialogContent|DialogOverlay/);
106
+ expect(source).toMatch(/<FloatingPanel/);
107
+ });
82
108
  });
@@ -19,10 +19,11 @@ import { describe, expect, it, vi } from "vitest";
19
19
  // ── Mock form components ──────────────────────────────────────
20
20
 
21
21
  function createMock(name: string) {
22
- const Comp = React.forwardRef((_props: any, ref: any) =>
22
+ const Comp = React.forwardRef((props: any, ref: any) =>
23
23
  React.createElement("div", {
24
24
  "data-testid": `mock-${name}`,
25
25
  "data-component": name,
26
+ "data-currency": props.currency,
26
27
  ref,
27
28
  }),
28
29
  );
@@ -187,6 +188,17 @@ describe("DataTableCell field type parity", () => {
187
188
  expectNotRendered(container, "Input");
188
189
  });
189
190
 
191
+ it("a currency stepper cell hands the currency to Stepper (MPO-221)", () => {
192
+ const { container } = renderCell({
193
+ field: "stepper",
194
+ value: 42,
195
+ fieldProps: { currency: "USD" },
196
+ });
197
+ expect(
198
+ container.querySelector('[data-component="Stepper"]')?.getAttribute("data-currency"),
199
+ ).toBe("USD");
200
+ });
201
+
190
202
  it("rating renders Rating, not custom star icons", () => {
191
203
  const { container } = renderCell({
192
204
  field: "rating",
@@ -217,10 +229,15 @@ describe("DataTableCell field type parity", () => {
217
229
  expect(display?.getAttribute("data-field")).toBe(fieldType);
218
230
  // Editor is not mounted until the cell is clicked
219
231
  expectNotRendered(baseElement, editorName);
220
- // Clicking the cell opens the modal containing the real editor
232
+ // Clicking the cell opens the panel containing the real editor. The
233
+ // panel renders inline (no portal), so the cell FACE is the trigger
234
+ // subtree and the editor lives in the panel content beside it.
221
235
  fireEvent.click(display!);
222
236
  await waitFor(() => expectRendered(baseElement, editorName));
223
- return { container, baseElement };
237
+ const face = container.querySelector('[data-testid="floating-panel-trigger"]') as HTMLElement;
238
+ const panel = container.querySelector('[data-testid="floating-panel-content"]') as HTMLElement;
239
+ expectRendered(panel, editorName);
240
+ return { container, baseElement, face, panel };
224
241
  }
225
242
 
226
243
  it("fileupload renders FileUpload, not custom FileUploadCell", async () => {
@@ -234,14 +251,14 @@ describe("DataTableCell field type parity", () => {
234
251
  });
235
252
 
236
253
  it("signature renders Signature, not status text", async () => {
237
- const { container } = await expectModalEditor(
254
+ const { face } = await expectModalEditor(
238
255
  { field: "signature", value: "" },
239
256
  "signature",
240
257
  "Signature",
241
258
  );
242
259
  // Old implementation showed "Not signed" / "Sign" in the cell
243
- expect(container.textContent).not.toContain("Not signed");
244
- expect(container.textContent).not.toContain("Sign");
260
+ expect(face.textContent).not.toContain("Not signed");
261
+ expect(face.textContent).not.toContain("Sign");
245
262
  });
246
263
 
247
264
  it("geolocation renders Geolocation, not two plain Inputs", async () => {
@@ -271,16 +288,16 @@ describe("DataTableCell field type parity", () => {
271
288
  });
272
289
 
273
290
  it("barcode routes through the modal editor (LC-06), never inline in the cell", async () => {
274
- const { container, baseElement } = await expectModalEditor(
291
+ const { face, panel } = await expectModalEditor(
275
292
  { field: "barcode", value: "SKU-001234" },
276
293
  "barcode",
277
294
  "Barcode",
278
295
  );
279
296
  // The full editor (with its camera scanner surface) must not mount inline
280
- // in the cell — only inside the modal after click.
281
- expectNotRendered(container, "Barcode");
282
- // The modal also carries the large scannable preview.
283
- expectRendered(baseElement, "BarcodePreview");
297
+ // in the cell face — only inside the panel after click.
298
+ expectNotRendered(face, "Barcode");
299
+ // The panel also carries the large scannable preview.
300
+ expectRendered(panel, "BarcodePreview");
284
301
  });
285
302
 
286
303
  it("custom render receives the cell's honest state as the render context (D-18.4)", () => {
@@ -372,13 +389,13 @@ describe("DataTableCell field type parity", () => {
372
389
  });
373
390
 
374
391
  it("textarea routes through the modal editor (LC-06), never inline in the cell", async () => {
375
- const { container, baseElement } = await expectModalEditor(
392
+ const { face, panel } = await expectModalEditor(
376
393
  { field: "textarea", value: "line one\nline two" },
377
394
  "textarea",
378
395
  "TextArea",
379
396
  );
380
- expectNotRendered(container, "TextArea");
381
- expectRendered(baseElement, "TextArea");
397
+ expectNotRendered(face, "TextArea");
398
+ expectRendered(panel, "TextArea");
382
399
  });
383
400
 
384
401
  it("cell chrome carries the LC-71 composite ring class (keyboard-only, not mouse-within)", () => {
@@ -46,6 +46,7 @@ import {
46
46
  ColorPicker,
47
47
  // Read-only display variants
48
48
  FieldDisplay,
49
+ containsAcrossPanels,
49
50
  } from "@multiplatform.one/forms";
50
51
  import { CellModalEditor } from "./CellModalEditor";
51
52
  import { CellOptionsEditor } from "./CellOptionsEditor";
@@ -380,6 +381,7 @@ export function DataTableCell({
380
381
  max={max}
381
382
  step={step ?? 1}
382
383
  precision={precision ?? 0}
384
+ currency={currency}
383
385
  disabled={disabled}
384
386
  readOnly={readOnly}
385
387
  placeholder={placeholder || "0"}
@@ -1167,8 +1169,7 @@ function DraftFieldEditor({
1167
1169
  if (event.key === "Escape") cancelDraft();
1168
1170
  };
1169
1171
  const handleDocPointerDown = (event: PointerEvent) => {
1170
- const node = containerRef.current;
1171
- if (node && event.target instanceof Node && node.contains(event.target)) return;
1172
+ if (containsAcrossPanels(containerRef.current, event.target)) return;
1172
1173
  setSessionActive(false);
1173
1174
  commitDraft();
1174
1175
  };
@@ -156,12 +156,15 @@ describe("DataTableDateFilter - DatePicker integration", () => {
156
156
  /**
157
157
  * Open the picker behind `placeholder` and click a day in its calendar.
158
158
  * A just-closed picker panel lingers mounted (with live handlers!) until
159
- * its close timer fires, so first wait until no panel is mounted — then
160
- * the day query can safely be global (forms DatePicker spec pattern).
159
+ * its close timer fires, so first wait until the filter's own panel is the
160
+ * only one mounted — then the day query can safely be global (forms
161
+ * DatePicker spec pattern). Panels portal to the page (MPO-335), so a
162
+ * picker panel is a sibling of the filter panel, not inside it.
161
163
  */
164
+ const panels = '[data-testid="floating-panel-viewport"]';
162
165
  const pickDay = async (placeholder: string, day: number) => {
163
166
  await waitFor(() => {
164
- expect(document.querySelectorAll('[data-testid="floating-panel-viewport"]').length).toBe(0);
167
+ expect(document.querySelectorAll(panels).length).toBe(1);
165
168
  });
166
169
  fireEvent.click(screen.getByPlaceholderText(placeholder));
167
170
  await waitFor(() => {