@multiplatform.one/forms 7.70.0 → 7.72.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 (333) hide show
  1. package/dist/cjs/Button/index.cjs +16 -0
  2. package/dist/cjs/Button/index.native.js +16 -0
  3. package/dist/cjs/Button/index.native.js.map +1 -1
  4. package/dist/cjs/FloatingPanel/echoContext.cjs +39 -0
  5. package/dist/cjs/FloatingPanel/echoContext.native.js +42 -0
  6. package/dist/cjs/FloatingPanel/echoContext.native.js.map +1 -0
  7. package/dist/cjs/FloatingPanel/index.cjs +212 -80
  8. package/dist/cjs/FloatingPanel/index.native.js +92 -17
  9. package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
  10. package/dist/cjs/FloatingPanel/panelWidth.cjs +92 -0
  11. package/dist/cjs/FloatingPanel/panelWidth.native.js +99 -0
  12. package/dist/cjs/FloatingPanel/panelWidth.native.js.map +1 -0
  13. package/dist/cjs/FloatingPanel/pointAnchor.cjs +15 -5
  14. package/dist/cjs/FloatingPanel/pointAnchor.native.js +15 -5
  15. package/dist/cjs/FloatingPanel/pointAnchor.native.js.map +1 -1
  16. package/dist/cjs/FloatingPanel/useFloatingPanel.cjs +58 -4
  17. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js +69 -4
  18. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  19. package/dist/cjs/InputParts/index.cjs +20 -1
  20. package/dist/cjs/InputParts/index.native.js +22 -2
  21. package/dist/cjs/InputParts/index.native.js.map +1 -1
  22. package/dist/cjs/Wheel/NativeWheel.native.js +1 -1
  23. package/dist/cjs/Wheel/NativeWheel.native.js.map +1 -1
  24. package/dist/cjs/Wheel/index.cjs +2 -1
  25. package/dist/cjs/Wheel/index.native.js +4 -1
  26. package/dist/cjs/Wheel/index.native.js.map +1 -1
  27. package/dist/cjs/Wheel/wheelShared.cjs +6 -1
  28. package/dist/cjs/Wheel/wheelShared.native.js +7 -1
  29. package/dist/cjs/Wheel/wheelShared.native.js.map +1 -1
  30. package/dist/cjs/fields/Combobox/index.cjs +1 -0
  31. package/dist/cjs/fields/Combobox/index.native.js +1 -0
  32. package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
  33. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.cjs +6 -30
  34. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
  35. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  36. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  37. package/dist/cjs/fields/DatePicker/catalog.cjs +2 -28
  38. package/dist/cjs/fields/DatePicker/catalog.native.js +2 -30
  39. package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -1
  40. package/dist/cjs/fields/DatePicker/index.native.js.map +1 -1
  41. package/dist/cjs/fields/DatePicker/parts.cjs +11 -20
  42. package/dist/cjs/fields/DatePicker/parts.native.js +13 -22
  43. package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
  44. package/dist/cjs/fields/SearchInput/index.cjs +23 -1
  45. package/dist/cjs/fields/SearchInput/index.native.js +23 -1
  46. package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
  47. package/dist/cjs/fields/Select/index.cjs +65 -44
  48. package/dist/cjs/fields/Select/index.native.js +69 -47
  49. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  50. package/dist/cjs/fields/Select/shouldInlineChoices.cjs +3 -1
  51. package/dist/cjs/fields/Select/shouldInlineChoices.native.js +3 -1
  52. package/dist/cjs/fields/Select/shouldInlineChoices.native.js.map +1 -1
  53. package/dist/cjs/fields/TimePicker/catalog.cjs +4 -25
  54. package/dist/cjs/fields/TimePicker/catalog.native.js +7 -29
  55. package/dist/cjs/fields/TimePicker/catalog.native.js.map +1 -1
  56. package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
  57. package/dist/cjs/fields/TimePicker/parts.cjs +8 -21
  58. package/dist/cjs/fields/TimePicker/parts.native.js +8 -21
  59. package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
  60. package/dist/cjs/index.native.js.map +1 -1
  61. package/dist/cjs/shared/floatingList.cjs +13 -4
  62. package/dist/cjs/shared/floatingList.native.js +15 -4
  63. package/dist/cjs/shared/floatingList.native.js.map +1 -1
  64. package/dist/esm/Button/index.mjs +17 -1
  65. package/dist/esm/Button/index.mjs.map +1 -1
  66. package/dist/esm/Button/index.native.js +17 -1
  67. package/dist/esm/Button/index.native.js.map +1 -1
  68. package/dist/esm/FloatingPanel/echoContext.mjs +11 -0
  69. package/dist/esm/FloatingPanel/echoContext.mjs.map +1 -0
  70. package/dist/esm/FloatingPanel/echoContext.native.js +11 -0
  71. package/dist/esm/FloatingPanel/echoContext.native.js.map +1 -0
  72. package/dist/esm/FloatingPanel/index.mjs +213 -83
  73. package/dist/esm/FloatingPanel/index.mjs.map +1 -1
  74. package/dist/esm/FloatingPanel/index.native.js +92 -19
  75. package/dist/esm/FloatingPanel/index.native.js.map +1 -1
  76. package/dist/esm/FloatingPanel/panelWidth.mjs +63 -0
  77. package/dist/esm/FloatingPanel/panelWidth.mjs.map +1 -0
  78. package/dist/esm/FloatingPanel/panelWidth.native.js +67 -0
  79. package/dist/esm/FloatingPanel/panelWidth.native.js.map +1 -0
  80. package/dist/esm/FloatingPanel/pointAnchor.mjs +15 -5
  81. package/dist/esm/FloatingPanel/pointAnchor.mjs.map +1 -1
  82. package/dist/esm/FloatingPanel/pointAnchor.native.js +15 -5
  83. package/dist/esm/FloatingPanel/pointAnchor.native.js.map +1 -1
  84. package/dist/esm/FloatingPanel/useFloatingPanel.mjs +58 -5
  85. package/dist/esm/FloatingPanel/useFloatingPanel.mjs.map +1 -1
  86. package/dist/esm/FloatingPanel/useFloatingPanel.native.js +69 -5
  87. package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  88. package/dist/esm/InputParts/index.mjs +21 -2
  89. package/dist/esm/InputParts/index.mjs.map +1 -1
  90. package/dist/esm/InputParts/index.native.js +23 -3
  91. package/dist/esm/InputParts/index.native.js.map +1 -1
  92. package/dist/esm/Wheel/NativeWheel.native.js +1 -1
  93. package/dist/esm/Wheel/NativeWheel.native.js.map +1 -1
  94. package/dist/esm/Wheel/index.mjs +2 -1
  95. package/dist/esm/Wheel/index.mjs.map +1 -1
  96. package/dist/esm/Wheel/index.native.js +4 -1
  97. package/dist/esm/Wheel/index.native.js.map +1 -1
  98. package/dist/esm/Wheel/wheelShared.mjs +6 -1
  99. package/dist/esm/Wheel/wheelShared.mjs.map +1 -1
  100. package/dist/esm/Wheel/wheelShared.native.js +7 -1
  101. package/dist/esm/Wheel/wheelShared.native.js.map +1 -1
  102. package/dist/esm/fields/Combobox/index.mjs +1 -0
  103. package/dist/esm/fields/Combobox/index.mjs.map +1 -1
  104. package/dist/esm/fields/Combobox/index.native.js +1 -0
  105. package/dist/esm/fields/Combobox/index.native.js.map +1 -1
  106. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs +6 -30
  107. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
  108. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
  109. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  110. package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  111. package/dist/esm/fields/DatePicker/catalog.mjs +2 -28
  112. package/dist/esm/fields/DatePicker/catalog.mjs.map +1 -1
  113. package/dist/esm/fields/DatePicker/catalog.native.js +2 -30
  114. package/dist/esm/fields/DatePicker/catalog.native.js.map +1 -1
  115. package/dist/esm/fields/DatePicker/index.native.js.map +1 -1
  116. package/dist/esm/fields/DatePicker/parts.mjs +11 -20
  117. package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
  118. package/dist/esm/fields/DatePicker/parts.native.js +13 -22
  119. package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
  120. package/dist/esm/fields/SearchInput/index.mjs +25 -3
  121. package/dist/esm/fields/SearchInput/index.mjs.map +1 -1
  122. package/dist/esm/fields/SearchInput/index.native.js +25 -3
  123. package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
  124. package/dist/esm/fields/Select/index.mjs +66 -45
  125. package/dist/esm/fields/Select/index.mjs.map +1 -1
  126. package/dist/esm/fields/Select/index.native.js +70 -48
  127. package/dist/esm/fields/Select/index.native.js.map +1 -1
  128. package/dist/esm/fields/Select/shouldInlineChoices.mjs +3 -1
  129. package/dist/esm/fields/Select/shouldInlineChoices.mjs.map +1 -1
  130. package/dist/esm/fields/Select/shouldInlineChoices.native.js +3 -1
  131. package/dist/esm/fields/Select/shouldInlineChoices.native.js.map +1 -1
  132. package/dist/esm/fields/TimePicker/catalog.mjs +4 -25
  133. package/dist/esm/fields/TimePicker/catalog.mjs.map +1 -1
  134. package/dist/esm/fields/TimePicker/catalog.native.js +7 -29
  135. package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -1
  136. package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
  137. package/dist/esm/fields/TimePicker/parts.mjs +8 -21
  138. package/dist/esm/fields/TimePicker/parts.mjs.map +1 -1
  139. package/dist/esm/fields/TimePicker/parts.native.js +8 -21
  140. package/dist/esm/fields/TimePicker/parts.native.js.map +1 -1
  141. package/dist/esm/index.mjs.map +1 -1
  142. package/dist/esm/index.native.js.map +1 -1
  143. package/dist/esm/shared/floatingList.mjs +14 -5
  144. package/dist/esm/shared/floatingList.mjs.map +1 -1
  145. package/dist/esm/shared/floatingList.native.js +16 -5
  146. package/dist/esm/shared/floatingList.native.js.map +1 -1
  147. package/dist/jsx/Button/index.mjs +17 -1
  148. package/dist/jsx/Button/index.mjs.map +1 -1
  149. package/dist/jsx/Button/index.native.js +16 -0
  150. package/dist/jsx/Button/index.native.js.map +1 -1
  151. package/dist/jsx/FloatingPanel/echoContext.mjs +11 -0
  152. package/dist/jsx/FloatingPanel/echoContext.mjs.map +1 -0
  153. package/dist/jsx/FloatingPanel/echoContext.native.js +42 -0
  154. package/dist/jsx/FloatingPanel/echoContext.native.js.map +1 -0
  155. package/dist/jsx/FloatingPanel/index.mjs +213 -83
  156. package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
  157. package/dist/jsx/FloatingPanel/index.native.js +92 -17
  158. package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
  159. package/dist/jsx/FloatingPanel/panelWidth.mjs +63 -0
  160. package/dist/jsx/FloatingPanel/panelWidth.mjs.map +1 -0
  161. package/dist/jsx/FloatingPanel/panelWidth.native.js +99 -0
  162. package/dist/jsx/FloatingPanel/panelWidth.native.js.map +1 -0
  163. package/dist/jsx/FloatingPanel/pointAnchor.mjs +15 -5
  164. package/dist/jsx/FloatingPanel/pointAnchor.mjs.map +1 -1
  165. package/dist/jsx/FloatingPanel/pointAnchor.native.js +15 -5
  166. package/dist/jsx/FloatingPanel/pointAnchor.native.js.map +1 -1
  167. package/dist/jsx/FloatingPanel/useFloatingPanel.mjs +58 -5
  168. package/dist/jsx/FloatingPanel/useFloatingPanel.mjs.map +1 -1
  169. package/dist/jsx/FloatingPanel/useFloatingPanel.native.js +69 -4
  170. package/dist/jsx/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  171. package/dist/jsx/InputParts/index.mjs +21 -2
  172. package/dist/jsx/InputParts/index.mjs.map +1 -1
  173. package/dist/jsx/InputParts/index.native.js +22 -2
  174. package/dist/jsx/InputParts/index.native.js.map +1 -1
  175. package/dist/jsx/Wheel/NativeWheel.native.js +1 -1
  176. package/dist/jsx/Wheel/NativeWheel.native.js.map +1 -1
  177. package/dist/jsx/Wheel/index.mjs +2 -1
  178. package/dist/jsx/Wheel/index.mjs.map +1 -1
  179. package/dist/jsx/Wheel/index.native.js +4 -1
  180. package/dist/jsx/Wheel/index.native.js.map +1 -1
  181. package/dist/jsx/Wheel/wheelShared.mjs +6 -1
  182. package/dist/jsx/Wheel/wheelShared.mjs.map +1 -1
  183. package/dist/jsx/Wheel/wheelShared.native.js +7 -1
  184. package/dist/jsx/Wheel/wheelShared.native.js.map +1 -1
  185. package/dist/jsx/fields/Combobox/index.mjs +1 -0
  186. package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
  187. package/dist/jsx/fields/Combobox/index.native.js +1 -0
  188. package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
  189. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs +6 -30
  190. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
  191. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
  192. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  193. package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  194. package/dist/jsx/fields/DatePicker/catalog.mjs +2 -28
  195. package/dist/jsx/fields/DatePicker/catalog.mjs.map +1 -1
  196. package/dist/jsx/fields/DatePicker/catalog.native.js +2 -30
  197. package/dist/jsx/fields/DatePicker/catalog.native.js.map +1 -1
  198. package/dist/jsx/fields/DatePicker/index.native.js.map +1 -1
  199. package/dist/jsx/fields/DatePicker/parts.mjs +11 -20
  200. package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
  201. package/dist/jsx/fields/DatePicker/parts.native.js +13 -22
  202. package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
  203. package/dist/jsx/fields/SearchInput/index.mjs +25 -3
  204. package/dist/jsx/fields/SearchInput/index.mjs.map +1 -1
  205. package/dist/jsx/fields/SearchInput/index.native.js +23 -1
  206. package/dist/jsx/fields/SearchInput/index.native.js.map +1 -1
  207. package/dist/jsx/fields/Select/index.mjs +66 -45
  208. package/dist/jsx/fields/Select/index.mjs.map +1 -1
  209. package/dist/jsx/fields/Select/index.native.js +69 -47
  210. package/dist/jsx/fields/Select/index.native.js.map +1 -1
  211. package/dist/jsx/fields/Select/shouldInlineChoices.mjs +3 -1
  212. package/dist/jsx/fields/Select/shouldInlineChoices.mjs.map +1 -1
  213. package/dist/jsx/fields/Select/shouldInlineChoices.native.js +3 -1
  214. package/dist/jsx/fields/Select/shouldInlineChoices.native.js.map +1 -1
  215. package/dist/jsx/fields/TimePicker/catalog.mjs +4 -25
  216. package/dist/jsx/fields/TimePicker/catalog.mjs.map +1 -1
  217. package/dist/jsx/fields/TimePicker/catalog.native.js +7 -29
  218. package/dist/jsx/fields/TimePicker/catalog.native.js.map +1 -1
  219. package/dist/jsx/fields/TimePicker/index.native.js.map +1 -1
  220. package/dist/jsx/fields/TimePicker/parts.mjs +8 -21
  221. package/dist/jsx/fields/TimePicker/parts.mjs.map +1 -1
  222. package/dist/jsx/fields/TimePicker/parts.native.js +8 -21
  223. package/dist/jsx/fields/TimePicker/parts.native.js.map +1 -1
  224. package/dist/jsx/index.js.map +1 -1
  225. package/dist/jsx/index.mjs.map +1 -1
  226. package/dist/jsx/index.native.js.map +1 -1
  227. package/dist/jsx/shared/floatingList.mjs +14 -5
  228. package/dist/jsx/shared/floatingList.mjs.map +1 -1
  229. package/dist/jsx/shared/floatingList.native.js +15 -4
  230. package/dist/jsx/shared/floatingList.native.js.map +1 -1
  231. package/package.json +8 -8
  232. package/src/Button/index.tsx +22 -0
  233. package/src/FloatingPanel/FloatingPanel.echoStretch.spec.tsx +67 -0
  234. package/src/FloatingPanel/FloatingPanel.menuFace.native.spec.tsx +193 -2
  235. package/src/FloatingPanel/FloatingPanel.seamlessCover.spec.tsx +237 -0
  236. package/src/FloatingPanel/echoContext.ts +22 -0
  237. package/src/FloatingPanel/index.native.tsx +106 -9
  238. package/src/FloatingPanel/index.tsx +310 -102
  239. package/src/FloatingPanel/panelWidth.spec.ts +158 -0
  240. package/src/FloatingPanel/panelWidth.ts +122 -0
  241. package/src/FloatingPanel/panelWidthContract.spec.ts +10 -10
  242. package/src/FloatingPanel/pointAnchor.spec.ts +64 -2
  243. package/src/FloatingPanel/pointAnchor.ts +28 -3
  244. package/src/FloatingPanel/useFloatingPanel.spec.tsx +124 -4
  245. package/src/FloatingPanel/useFloatingPanel.ts +106 -33
  246. package/src/InputParts/index.tsx +30 -1
  247. package/src/Wheel/NativeWheel.native.tsx +1 -1
  248. package/src/Wheel/Wheel.spec.tsx +9 -0
  249. package/src/Wheel/index.tsx +12 -3
  250. package/src/Wheel/wheelShared.ts +6 -2
  251. package/src/fields/Combobox/index.tsx +1 -0
  252. package/src/fields/DatePicker/DatePicker.stories.tsx +3 -3
  253. package/src/fields/DatePicker/DatetimePicker.catalog.tsx +17 -45
  254. package/src/fields/DatePicker/DatetimePicker.native.tsx +3 -0
  255. package/src/fields/DatePicker/catalog.tsx +8 -39
  256. package/src/fields/DatePicker/index.native.tsx +3 -0
  257. package/src/fields/DatePicker/parts.tsx +13 -23
  258. package/src/fields/DatePicker/seamlessCover.spec.tsx +180 -0
  259. package/src/fields/DatePicker/touchWeb.spec.tsx +109 -167
  260. package/src/fields/SearchInput/SearchInput.spec.tsx +40 -0
  261. package/src/fields/SearchInput/index.tsx +47 -2
  262. package/src/fields/Select/Select.containingBlock.spec.tsx +191 -0
  263. package/src/fields/Select/Select.stories.tsx +43 -0
  264. package/src/fields/Select/index.tsx +109 -45
  265. package/src/fields/Select/shouldInlineChoices.spec.ts +13 -0
  266. package/src/fields/Select/shouldInlineChoices.ts +6 -0
  267. package/src/fields/TimePicker/catalog.tsx +12 -33
  268. package/src/fields/TimePicker/index.native.tsx +2 -0
  269. package/src/fields/TimePicker/parts.tsx +6 -20
  270. package/src/index.ts +1 -0
  271. package/src/shared/floatingList.tsx +21 -5
  272. package/types/Button/index.d.ts.map +1 -1
  273. package/types/FloatingPanel/echoContext.d.ts +14 -0
  274. package/types/FloatingPanel/echoContext.d.ts.map +1 -0
  275. package/types/FloatingPanel/index.d.ts +59 -0
  276. package/types/FloatingPanel/index.d.ts.map +1 -1
  277. package/types/FloatingPanel/index.native.d.ts +9 -1
  278. package/types/FloatingPanel/index.native.d.ts.map +1 -1
  279. package/types/FloatingPanel/panelWidth.d.ts +71 -0
  280. package/types/FloatingPanel/panelWidth.d.ts.map +1 -0
  281. package/types/FloatingPanel/pointAnchor.d.ts +8 -1
  282. package/types/FloatingPanel/pointAnchor.d.ts.map +1 -1
  283. package/types/FloatingPanel/useFloatingPanel.d.ts +33 -2
  284. package/types/FloatingPanel/useFloatingPanel.d.ts.map +1 -1
  285. package/types/InputParts/index.d.ts +5 -1
  286. package/types/InputParts/index.d.ts.map +1 -1
  287. package/types/Wheel/index.d.ts.map +1 -1
  288. package/types/Wheel/wheelShared.d.ts +2 -1
  289. package/types/Wheel/wheelShared.d.ts.map +1 -1
  290. package/types/fields/Combobox/index.d.ts.map +1 -1
  291. package/types/fields/DatePicker/DatetimePicker.catalog.d.ts +6 -0
  292. package/types/fields/DatePicker/DatetimePicker.catalog.d.ts.map +1 -1
  293. package/types/fields/DatePicker/DatetimePicker.native.d.ts +3 -0
  294. package/types/fields/DatePicker/DatetimePicker.native.d.ts.map +1 -1
  295. package/types/fields/DatePicker/catalog.d.ts +6 -0
  296. package/types/fields/DatePicker/catalog.d.ts.map +1 -1
  297. package/types/fields/DatePicker/index.native.d.ts +3 -0
  298. package/types/fields/DatePicker/index.native.d.ts.map +1 -1
  299. package/types/fields/DatePicker/parts.d.ts +5 -7
  300. package/types/fields/DatePicker/parts.d.ts.map +1 -1
  301. package/types/fields/Input/index.d.ts +3 -1
  302. package/types/fields/Input/index.d.ts.map +1 -1
  303. package/types/fields/SearchInput/index.d.ts +11 -0
  304. package/types/fields/SearchInput/index.d.ts.map +1 -1
  305. package/types/fields/Select/index.d.ts +8 -1
  306. package/types/fields/Select/index.d.ts.map +1 -1
  307. package/types/fields/Select/shouldInlineChoices.d.ts +5 -1
  308. package/types/fields/Select/shouldInlineChoices.d.ts.map +1 -1
  309. package/types/fields/TimePicker/catalog.d.ts +7 -1
  310. package/types/fields/TimePicker/catalog.d.ts.map +1 -1
  311. package/types/fields/TimePicker/index.native.d.ts +2 -0
  312. package/types/fields/TimePicker/index.native.d.ts.map +1 -1
  313. package/types/fields/TimePicker/parts.d.ts +1 -3
  314. package/types/fields/TimePicker/parts.d.ts.map +1 -1
  315. package/types/index.d.ts +1 -0
  316. package/types/index.d.ts.map +1 -1
  317. package/types/shared/floatingList.d.ts +5 -1
  318. package/types/shared/floatingList.d.ts.map +1 -1
  319. package/dist/cjs/shared/touchWebPicker.cjs +0 -136
  320. package/dist/cjs/shared/touchWebPicker.native.js +0 -154
  321. package/dist/cjs/shared/touchWebPicker.native.js.map +0 -1
  322. package/dist/esm/shared/touchWebPicker.mjs +0 -105
  323. package/dist/esm/shared/touchWebPicker.mjs.map +0 -1
  324. package/dist/esm/shared/touchWebPicker.native.js +0 -120
  325. package/dist/esm/shared/touchWebPicker.native.js.map +0 -1
  326. package/dist/jsx/shared/touchWebPicker.mjs +0 -105
  327. package/dist/jsx/shared/touchWebPicker.mjs.map +0 -1
  328. package/dist/jsx/shared/touchWebPicker.native.js +0 -154
  329. package/dist/jsx/shared/touchWebPicker.native.js.map +0 -1
  330. package/src/shared/touchWebPicker.spec.ts +0 -98
  331. package/src/shared/touchWebPicker.tsx +0 -158
  332. package/types/shared/touchWebPicker.d.ts +0 -40
  333. package/types/shared/touchWebPicker.d.ts.map +0 -1
@@ -27,6 +27,7 @@ import { AnimatePresence, SizableText, Text, View, isWeb } from "tamagui";
27
27
 
28
28
  import { FloatingPanel, floatingPanelActionProps, isNestedPanelAction } from "../../FloatingPanel";
29
29
  import type { PanelActionEvent } from "../../FloatingPanel/nestedAction";
30
+ import type { PanelWidth } from "../../FloatingPanel/panelWidth";
30
31
  import { Input as InputParts } from "../../InputParts";
31
32
  import { formCommonColors } from "../../shared/colorRamps";
32
33
  import { t } from "../../shared/t";
@@ -861,11 +862,6 @@ interface DatePickerInputTriggerProps {
861
862
  "aria-describedby"?: string;
862
863
  "aria-required"?: boolean;
863
864
  "aria-invalid"?: boolean;
864
- /**
865
- * Touch web (LC-266): the browser's own picker input, laid over the box.
866
- * The input then owns the tap, the focus and the field's a11y name.
867
- */
868
- touchInput?: ReactNode;
869
865
  }
870
866
 
871
867
  export function DatePickerClearButton({
@@ -905,7 +901,6 @@ export function DatePickerInputTrigger({
905
901
  "aria-describedby": ariaDescribedBy,
906
902
  "aria-required": ariaRequired,
907
903
  "aria-invalid": ariaInvalid,
908
- touchInput,
909
904
  }: DatePickerInputTriggerProps) {
910
905
  const handleTriggerKey = (
911
906
  e: PanelActionEvent & { key?: string; code?: string; preventDefault?: () => void },
@@ -948,13 +943,10 @@ export function DatePickerInputTrigger({
948
943
  // on the closed text-field trigger.
949
944
  elevation={undefined}
950
945
  data-testid="date-picker-trigger"
951
- // The touch input is laid absolutely over this box; without a
952
- // positioning context it covers the whole page and takes every tap.
953
- position={touchInput ? "relative" : undefined}
954
- id={touchInput ? undefined : id}
955
- onPress={isWeb && !disabled && !touchInput ? onButtonPress : undefined}
946
+ id={id}
947
+ onPress={isWeb && !disabled ? onButtonPress : undefined}
956
948
  {...(disabled ? { opacity: 0.5, pointerEvents: "none" } : undefined)}
957
- {...(isWeb && !touchInput
949
+ {...(isWeb
958
950
  ? {
959
951
  role: "combobox" as const,
960
952
  "aria-haspopup": "dialog",
@@ -969,7 +961,6 @@ export function DatePickerInputTrigger({
969
961
  }
970
962
  : undefined)}
971
963
  >
972
- {touchInput}
973
964
  <InputParts.Section data-testid="date-picker-glyph">
974
965
  <InputParts.Icon adornment="leading" color={formCommonColors.muted}>
975
966
  <Glyph />
@@ -980,7 +971,6 @@ export function DatePickerInputTrigger({
980
971
  readOnly
981
972
  pointerEvents="none"
982
973
  tabIndex={-1}
983
- aria-hidden={touchInput ? true : undefined}
984
974
  value={value}
985
975
  placeholder={placeholder}
986
976
  disabled={disabled}
@@ -990,14 +980,7 @@ export function DatePickerInputTrigger({
990
980
  </InputParts.Section>
991
981
  {value && isWeb ? (
992
982
  <InputParts.Section>
993
- {touchInput ? (
994
- // Above the touch input, so Clear stays reachable on the box.
995
- <View zIndex={2}>
996
- <DatePickerClearButton onReset={onReset} disabled={disabled} />
997
- </View>
998
- ) : (
999
- <DatePickerClearButton onReset={onReset} disabled={disabled} />
1000
- )}
983
+ <DatePickerClearButton onReset={onReset} disabled={disabled} />
1001
984
  </InputParts.Section>
1002
985
  ) : null}
1003
986
  </InputParts.Box>
@@ -1078,6 +1061,8 @@ interface DatePickerPopoverShellProps {
1078
1061
  /** When set, native hosts render Clear outside the pointerEvents-none trigger. */
1079
1062
  clearValue?: string;
1080
1063
  onClear?: () => void;
1064
+ /** How wide the open picker is (LC-112, FZ-8438 r3). Unset: at least the field. */
1065
+ panelWidth?: PanelWidth;
1081
1066
  }
1082
1067
 
1083
1068
  export function DatePickerPopoverShell({
@@ -1090,6 +1075,7 @@ export function DatePickerPopoverShell({
1090
1075
  triggerA11y,
1091
1076
  clearValue,
1092
1077
  onClear,
1078
+ panelWidth,
1093
1079
  }: DatePickerPopoverShellProps) {
1094
1080
  const nativeClear =
1095
1081
  !isWeb && clearValue && onClear ? (
@@ -1104,7 +1090,11 @@ export function DatePickerPopoverShell({
1104
1090
  disabled={disabled}
1105
1091
  sizing="grow"
1106
1092
  contentPadding="none"
1107
- widthMode="fit-content"
1093
+ // LC-112 seamless cover (FZ-8438): the panel is the field grown open,
1094
+ // at least its width, on every web width and pointer.
1095
+ widthMode="at-least-trigger"
1096
+ panelWidth={panelWidth}
1097
+ compactFace={isWeb ? "menu" : undefined}
1108
1098
  triggerA11y={triggerA11y}
1109
1099
  >
1110
1100
  {children}
@@ -0,0 +1,180 @@
1
+ /**
2
+ * LC-112 seamless cover for the DatePicker family (FZ-8438). Clay rated deck
3
+ * rv_a07d4308a6c445b6 0 good: the date, time and date-and-time panels must
4
+ * open as one surface exactly over their field, at desktop and phone width,
5
+ * on a fine pointer and on touch alike.
6
+ *
7
+ * The browser half is emulated where happy-dom has no layout: the field's
8
+ * rect, and the arc its Box draws (read off the Box's own radius atom, the
9
+ * value a browser would compute from it).
10
+ */
11
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
12
+ import { act, fireEvent, waitFor } from "@testing-library/react";
13
+ import { getTokens, getVariableValue } from "tamagui";
14
+ import type { ReactElement } from "react";
15
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
16
+ import { TimePicker } from "../TimePicker";
17
+ import { DatePicker, DatetimePicker } from "./index";
18
+
19
+ type Rect = { x: number; y: number; width: number; height: number };
20
+
21
+ const viewports: Record<string, { width: number; height: number; field: Rect }> = {
22
+ "1440": { width: 1440, height: 900, field: { x: 16, y: 42, width: 400, height: 40 } },
23
+ "390": { width: 390, height: 844, field: { x: 16, y: 42, width: 358, height: 44 } },
24
+ };
25
+
26
+ const pickers: [string, () => ReactElement][] = [
27
+ ["DatePicker", () => <DatePicker label="Date" name="date" value={new Date(2026, 2, 5)} />],
28
+ ["TimePicker", () => <TimePicker label="Time" name="time" value="14:30" />],
29
+ [
30
+ "DatetimePicker",
31
+ () => (
32
+ <DatetimePicker label="Date and time" name="starts" value={new Date(2026, 2, 5, 14, 30)} />
33
+ ),
34
+ ],
35
+ ];
36
+
37
+ const originalWidth = window.innerWidth;
38
+ const originalHeight = window.innerHeight;
39
+
40
+ function setViewport(width: number, height: number) {
41
+ Object.defineProperty(window, "innerWidth", { configurable: true, value: width });
42
+ Object.defineProperty(window, "innerHeight", { configurable: true, value: height });
43
+ act(() => {
44
+ window.dispatchEvent(new Event("resize"));
45
+ });
46
+ }
47
+
48
+ /** The painted Box of each picker's trigger (the field the user sees). */
49
+ function isFieldBox(el: Element): boolean {
50
+ return (
51
+ el.matches('[data-testid="date-picker-trigger"]') ||
52
+ !!el.parentElement?.matches('[data-testid="time-picker-trigger"]')
53
+ );
54
+ }
55
+
56
+ function atomRadius(el: Element): number {
57
+ for (const c of Array.from(el.classList)) {
58
+ const px = c.match(/^_btlr-(\d+(?:\.\d+)?)px$/);
59
+ if (px) return Number(px[1]);
60
+ const token = c.match(/^_btlr-t-radius-(.+)$/);
61
+ if (token) {
62
+ const value = (getTokens().radius as Record<string, unknown>)[`$${token[1]}`];
63
+ return Number(getVariableValue(value));
64
+ }
65
+ }
66
+ return 0;
67
+ }
68
+
69
+ function emulateBrowser(field: Rect) {
70
+ const rect = {
71
+ ...field,
72
+ top: field.y,
73
+ left: field.x,
74
+ right: field.x + field.width,
75
+ bottom: field.y + field.height,
76
+ toJSON: () => ({}),
77
+ };
78
+ const originalRect = HTMLElement.prototype.getBoundingClientRect;
79
+ vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockImplementation(
80
+ function (this: HTMLElement) {
81
+ if (this.hasAttribute("data-fp-reference") || isFieldBox(this)) return rect;
82
+ return originalRect.call(this);
83
+ },
84
+ );
85
+ const original = globalThis.getComputedStyle.bind(globalThis);
86
+ const computed = (el: Element, pseudo?: string | null) => {
87
+ const style = original(el, pseudo);
88
+ if (!isFieldBox(el)) return style;
89
+ const radius = `${atomRadius(el)}px`;
90
+ return new Proxy(style, {
91
+ get(target, key) {
92
+ if (key === "borderTopLeftRadius") return radius;
93
+ const value = Reflect.get(target, key, target);
94
+ return typeof value === "function" ? value.bind(target) : value;
95
+ },
96
+ });
97
+ };
98
+ vi.spyOn(globalThis, "getComputedStyle").mockImplementation(computed as typeof getComputedStyle);
99
+ if ((window as unknown) !== globalThis) {
100
+ vi.spyOn(window, "getComputedStyle").mockImplementation(computed as typeof getComputedStyle);
101
+ }
102
+ }
103
+
104
+ function fieldBox(root: ParentNode): HTMLElement {
105
+ const box = Array.from(root.querySelectorAll<HTMLElement>("*")).find(isFieldBox);
106
+ expect(box).toBeTruthy();
107
+ return box as HTMLElement;
108
+ }
109
+
110
+ const corners = (el: Element) =>
111
+ Array.from(el.classList)
112
+ .filter((c) => /^_b[tb][lr]r-/.test(c))
113
+ .sort();
114
+
115
+ beforeEach(() => {
116
+ setViewport(1440, 900);
117
+ });
118
+
119
+ afterEach(() => {
120
+ vi.restoreAllMocks();
121
+ setViewport(originalWidth, originalHeight);
122
+ });
123
+
124
+ describe("DatePicker family: one surface over the field (LC-112, FZ-8438)", () => {
125
+ for (const [size, viewport] of Object.entries(viewports)) {
126
+ for (const [name, render] of pickers) {
127
+ it(`${name} at ${size}: top, start, width and all four arcs are the field's`, async () => {
128
+ setViewport(viewport.width, viewport.height);
129
+ emulateBrowser(viewport.field);
130
+ const result = renderWithProviders(render());
131
+ const box = fieldBox(result.container);
132
+ const drawn = Math.min(atomRadius(box), viewport.field.height / 2);
133
+ expect(drawn).toBeGreaterThan(0);
134
+
135
+ fireEvent.click(box, { detail: 0 });
136
+ const panel = (await result.findByTestId("floating-panel-viewport")) as HTMLElement;
137
+ expect(result.queryByTestId("floating-panel-sheet")).toBeNull();
138
+ expect(document.querySelector('input[type="date"], input[type="time"]')).toBeNull();
139
+
140
+ await waitFor(() => expect(panel.style.top).toBe(`${viewport.field.y}px`));
141
+ expect(panel.style.left).toBe(`${viewport.field.x}px`);
142
+ await waitFor(() =>
143
+ expect(parseFloat(panel.style.minWidth)).toBeGreaterThanOrEqual(viewport.field.width),
144
+ );
145
+ await waitFor(() =>
146
+ expect(corners(panel)).toEqual(
147
+ [`_bblr-${drawn}px`, `_bbrr-${drawn}px`, `_btlr-${drawn}px`, `_btrr-${drawn}px`].sort(),
148
+ ),
149
+ );
150
+ });
151
+
152
+ it(`${name} at ${size}: the echo draws no border or divider and sits above the field`, async () => {
153
+ setViewport(viewport.width, viewport.height);
154
+ emulateBrowser(viewport.field);
155
+ const result = renderWithProviders(render());
156
+ const live = fieldBox(result.container);
157
+ fireEvent.click(live, { detail: 0 });
158
+ const panel = (await result.findByTestId("floating-panel-viewport")) as HTMLElement;
159
+ const frame = await waitFor(() => {
160
+ const found = panel.querySelector<HTMLElement>("[data-fp-echo-frame]");
161
+ expect(found).not.toBeNull();
162
+ return found as HTMLElement;
163
+ });
164
+ for (const property of ["border-color", "background-color"]) {
165
+ expect(frame.style.getPropertyValue(property)).toBe("transparent");
166
+ expect(frame.style.getPropertyPriority(property)).toBe("important");
167
+ }
168
+ expect(frame.style.getPropertyValue("box-shadow")).toBe("none");
169
+ const row = panel.querySelector<HTMLElement>("[data-fp-trigger-row]") as HTMLElement;
170
+ expect(row.style.width).toBe(`${viewport.field.width}px`);
171
+ expect(row.style.height).toBe(`${viewport.field.height}px`);
172
+ expect(row.style.backgroundColor).toBe(getComputedStyle(panel).backgroundColor);
173
+ expect(panel.style.position).toBe("fixed");
174
+ expect(Number(panel.style.zIndex)).toBeGreaterThan(0);
175
+ expect(live.compareDocumentPosition(panel) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
176
+ expect(live.contains(panel)).toBe(false);
177
+ });
178
+ }
179
+ }
180
+ });
@@ -1,7 +1,11 @@
1
1
  /**
2
- * FZ-8114 (LC-266): Date, Time and Datetime on a coarse-pointer web viewport
3
- * hand the pick to the browser's own control, and a fine pointer keeps the
4
- * MPO calendar. Both paths must commit the same value.
2
+ * Date, Time and Datetime on touch web. FZ-8114 handed these to the browser's
3
+ * own popover; FZ-8438 replaces it with MPO's cover panel, because Safari's
4
+ * popover cannot lie over the field, match its arc or repeat its content
5
+ * (LC-112 seamless cover, Clay's rv_a07d4308a6c445b6 notes).
6
+ *
7
+ * FZ-8114's invariant still holds: a blank tap opens nothing, only the tapped
8
+ * field opens, and its clear ✕ clears without opening.
5
9
  */
6
10
  import { formatCalendarDate } from "@multiplatform.one/theme";
7
11
  import { useForm } from "@tanstack/react-form";
@@ -13,8 +17,15 @@ import { Form } from "../../Form";
13
17
  import { TimePicker } from "../TimePicker";
14
18
  import { DatePicker, DatetimePicker } from "./index";
15
19
 
16
- let coarse = false;
17
20
  const originalMatchMedia = window.matchMedia;
21
+ const originalWidth = window.innerWidth;
22
+
23
+ function setWidth(width: number) {
24
+ Object.defineProperty(window, "innerWidth", { configurable: true, value: width });
25
+ act(() => {
26
+ window.dispatchEvent(new Event("resize"));
27
+ });
28
+ }
18
29
 
19
30
  beforeEach(() => {
20
31
  Object.defineProperty(window, "matchMedia", {
@@ -22,7 +33,7 @@ beforeEach(() => {
22
33
  writable: true,
23
34
  value: (media: string) => ({
24
35
  media,
25
- matches: media === "(pointer: coarse)" ? coarse : false,
36
+ matches: media === "(pointer: coarse)" || media === "(hover: none)",
26
37
  onchange: null,
27
38
  addEventListener: () => {},
28
39
  removeEventListener: () => {},
@@ -31,23 +42,30 @@ beforeEach(() => {
31
42
  dispatchEvent: () => false,
32
43
  }),
33
44
  });
45
+ setWidth(390);
34
46
  });
35
47
 
36
48
  afterEach(() => {
37
- coarse = false;
38
49
  Object.defineProperty(window, "matchMedia", {
39
50
  configurable: true,
40
51
  writable: true,
41
52
  value: originalMatchMedia,
42
53
  });
54
+ setWidth(originalWidth);
43
55
  });
44
56
 
45
- function touchInput(root: ParentNode, type: string): HTMLInputElement {
46
- const input = root.querySelector(`input[type="${type}"]`) as HTMLInputElement | null;
47
- expect(input).not.toBeNull();
48
- return input as HTMLInputElement;
57
+ /** A finger tap as mobile Safari delivers it: pointer, compat mouse, click. */
58
+ function tap(el: Element) {
59
+ fireEvent.pointerDown(el, { pointerType: "touch", button: 0 });
60
+ fireEvent.mouseDown(el, { button: 0, detail: 1 });
61
+ fireEvent.pointerUp(el, { pointerType: "touch", button: 0 });
62
+ fireEvent.mouseUp(el, { button: 0, detail: 1 });
63
+ fireEvent.click(el, { button: 0, detail: 1 });
49
64
  }
50
65
 
66
+ const panels = () => document.querySelectorAll('[data-testid="floating-panel-viewport"]');
67
+ const sheets = () => document.querySelectorAll('[data-testid="floating-panel-sheet"]');
68
+
51
69
  function dayButton(root: ParentNode, day: string): HTMLElement {
52
70
  const match = Array.from(root.querySelectorAll('button, [role="button"]')).find(
53
71
  (el) => el.textContent?.trim() === day && el.getAttribute("aria-disabled") !== "true",
@@ -56,100 +74,74 @@ function dayButton(root: ParentNode, day: string): HTMLElement {
56
74
  return match as HTMLElement;
57
75
  }
58
76
 
59
- describe("Date on touch web (FZ-8114)", () => {
60
- it("a coarse pointer renders the browser's date input, labelled by the field", () => {
61
- coarse = true;
62
- const result = renderWithProviders(<DatePicker label="Due" name="due" />);
63
- const input = touchInput(result.container, "date");
64
- const label = result.container.querySelector("label");
65
- expect(label?.getAttribute("for")).toBe(input.id);
66
- expect(result.container.querySelector('[role="combobox"]')).toBeNull();
67
- });
77
+ function Page() {
78
+ return (
79
+ <>
80
+ <div data-testid="blank" style={{ height: 200 }} />
81
+ <DatePicker label="Date" name="date" value={new Date(2026, 2, 5)} />
82
+ <TimePicker label="Time" name="time" value="14:30" />
83
+ <DatetimePicker label="Date and time" name="starts" />
84
+ </>
85
+ );
86
+ }
68
87
 
69
- it("a fine pointer keeps the MPO combobox and no date input", () => {
70
- const result = renderWithProviders(<DatePicker label="Due" name="due" />);
71
- expect(result.container.querySelector('input[type="date"]')).toBeNull();
72
- expect(result.container.querySelector('[role="combobox"]')).not.toBeNull();
88
+ describe("touch web opens MPO's cover panel, never the browser's popover (FZ-8438)", () => {
89
+ it.each([
90
+ ["date", "date-picker-trigger", <DatePicker key="d" label="Due" name="due" />],
91
+ ["time", "time-picker-trigger", <TimePicker key="t" label="At" name="at" />],
92
+ [
93
+ "date and time",
94
+ "date-picker-trigger",
95
+ <DatetimePicker key="dt" label="Starts" name="starts" />,
96
+ ],
97
+ ] as const)("a tap on the %s field opens the MPO panel over it", async (_, testId, field) => {
98
+ const result = renderWithProviders(field);
99
+ expect(result.container.querySelector("input[type=date], input[type=time]")).toBeNull();
100
+ expect(result.container.querySelector('input[type="datetime-local"]')).toBeNull();
101
+ tap(result.getByTestId(testId));
102
+ await waitFor(() => expect(panels().length).toBe(1));
103
+ expect(sheets().length).toBe(0);
104
+ expect(document.querySelector("[data-fp-trigger-row]")).not.toBeNull();
73
105
  });
74
106
 
75
- it("the picked string shows in the MPO box and emits the calendar's Date", async () => {
76
- coarse = true;
77
- const onChange = vi.fn();
78
- const result = renderWithProviders(<DatePicker label="Due" name="due" onChange={onChange} />);
107
+ it("a blank tap opens nothing", async () => {
108
+ const result = renderWithProviders(<Page />);
109
+ tap(result.getByTestId("blank"));
110
+ tap(document.body);
79
111
  await act(async () => {
80
- fireEvent.change(touchInput(result.container, "date"), { target: { value: "2026-03-05" } });
112
+ await new Promise((resolve) => setTimeout(resolve, 50));
81
113
  });
82
- expect(onChange).toHaveBeenCalledTimes(1);
83
- const picked = onChange.mock.calls[0][0] as Date;
84
- expect(picked.getTime()).toBe(new Date(2026, 2, 5).getTime());
85
- expect(result.container.querySelector('input[value="Mar 5, 2026"]')).not.toBeNull();
86
- expect(touchInput(result.container, "date").value).toBe("2026-03-05");
114
+ expect(panels().length).toBe(0);
115
+ expect(sheets().length).toBe(0);
87
116
  });
88
117
 
89
- it("both paths commit the same value for the same day", async () => {
90
- const fine = vi.fn();
91
- const desktop = renderWithProviders(
92
- <DatePicker label="Due" name="due" value="2026-03-01" onChange={fine} />,
93
- );
94
- await act(async () => {
95
- fireEvent.click(desktop.container.querySelector('[role="combobox"]') as HTMLElement);
96
- });
97
- await waitFor(() => dayButton(document.body, "5"));
98
- await act(async () => {
99
- fireEvent.click(dayButton(document.body, "5"));
100
- });
101
- desktop.unmount();
102
-
103
- coarse = true;
104
- const touch = vi.fn();
105
- const phone = renderWithProviders(
106
- <DatePicker label="Due" name="due" value="2026-03-01" onChange={touch} />,
107
- );
108
- expect(touchInput(phone.container, "date").value).toBe("2026-03-01");
109
- await act(async () => {
110
- fireEvent.change(touchInput(phone.container, "date"), { target: { value: "2026-03-05" } });
111
- });
112
-
113
- expect(fine).toHaveBeenCalledTimes(1);
114
- expect(touch).toHaveBeenCalledTimes(1);
115
- expect((touch.mock.calls[0][0] as Date).getTime()).toBe(
116
- (fine.mock.calls[0][0] as Date).getTime(),
117
- );
118
+ it("only the tapped field opens", async () => {
119
+ const result = renderWithProviders(<Page />);
120
+ tap(result.getByTestId("time-picker-trigger"));
121
+ await waitFor(() => expect(panels().length).toBe(1));
122
+ const panel = panels()[0] as HTMLElement;
123
+ expect(panel.querySelector('[data-testid="time-picker-panel"]')).not.toBeNull();
124
+ expect(panel.textContent).not.toContain("March");
118
125
  });
119
126
 
120
- it("clearing in the browser's control clears the field", async () => {
121
- coarse = true;
127
+ it("the clear ✕ clears without opening", async () => {
122
128
  const onChange = vi.fn();
123
129
  const result = renderWithProviders(
124
- <DatePicker label="Due" name="due" value="2026-03-05" onChange={onChange} />,
130
+ <DatePicker label="Due" name="due" value={new Date(2026, 2, 5)} onChange={onChange} />,
125
131
  );
132
+ tap(result.getByTestId("date-picker-clear"));
126
133
  await act(async () => {
127
- fireEvent.change(touchInput(result.container, "date"), { target: { value: "" } });
134
+ await new Promise((resolve) => setTimeout(resolve, 50));
128
135
  });
129
136
  expect(onChange).toHaveBeenLastCalledWith(null);
137
+ expect(panels().length).toBe(0);
130
138
  });
131
139
 
132
- it("min and max ride the input as local calendar days", () => {
133
- coarse = true;
134
- const result = renderWithProviders(
135
- <DatePicker
136
- label="Due"
137
- name="due"
138
- minDate={new Date(2026, 0, 2)}
139
- maxDate={new Date(2026, 11, 30)}
140
- />,
141
- );
142
- const input = touchInput(result.container, "date");
143
- expect(input.min).toBe("2026-01-02");
144
- expect(input.max).toBe("2026-12-30");
145
- });
146
-
147
- it("commits into form state", async () => {
148
- coarse = true;
140
+ it("a day picked in the panel commits the calendar's Date into form state", async () => {
149
141
  const onSubmit = vi.fn();
150
142
  const Fixture = () => {
151
143
  const form = useForm({
152
- defaultValues: { due: null as Date | null },
144
+ defaultValues: { due: new Date(2026, 2, 1) as Date | null },
153
145
  onSubmit: async ({ value }) => onSubmit(value),
154
146
  });
155
147
  return (
@@ -160,99 +152,49 @@ describe("Date on touch web (FZ-8114)", () => {
160
152
  );
161
153
  };
162
154
  const result = renderWithProviders(<Fixture />);
155
+ tap(result.getByTestId("date-picker-trigger"));
156
+ await waitFor(() => expect(panels().length).toBe(1));
163
157
  await act(async () => {
164
- fireEvent.change(touchInput(result.container, "date"), { target: { value: "2026-08-15" } });
158
+ fireEvent.click(dayButton(panels()[0] as HTMLElement, "15"));
165
159
  });
160
+ await waitFor(() => expect(panels().length).toBe(0));
166
161
  await act(async () => {
167
162
  fireEvent.click(result.getSubmitButton() as HTMLElement);
168
163
  });
169
164
  await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
170
- expect(formatCalendarDate(onSubmit.mock.calls[0][0].due, "date")).toBe("2026-08-15");
171
- });
172
- });
173
-
174
- describe("Datetime on touch web (FZ-8114)", () => {
175
- it("a coarse pointer renders datetime-local and emits the local wall clock", async () => {
176
- coarse = true;
177
- const onChange = vi.fn();
178
- const result = renderWithProviders(
179
- <DatetimePicker label="Starts" name="starts" onChange={onChange} />,
180
- );
181
- await act(async () => {
182
- fireEvent.change(touchInput(result.container, "datetime-local"), {
183
- target: { value: "2026-03-05T14:30" },
184
- });
185
- });
186
- const picked = onChange.mock.calls[0][0] as Date;
187
- expect(formatCalendarDate(picked, "datetime")).toBe("2026-03-05 14:30:00");
188
- expect(touchInput(result.container, "datetime-local").value).toBe("2026-03-05T14:30");
189
- });
190
-
191
- it("seconds widen the input's step and survive the round trip", () => {
192
- coarse = true;
193
- const result = renderWithProviders(
194
- <DatetimePicker
195
- label="Starts"
196
- name="starts"
197
- showSeconds
198
- value={new Date(2026, 2, 5, 14, 30, 15)}
199
- />,
200
- );
201
- const input = touchInput(result.container, "datetime-local");
202
- expect(input.step).toBe("1");
203
- expect(input.value).toBe("2026-03-05T14:30:15");
204
- });
205
-
206
- it("a fine pointer keeps the MPO panel trigger", () => {
207
- const result = renderWithProviders(<DatetimePicker label="Starts" name="starts" />);
208
- expect(result.container.querySelector('input[type="datetime-local"]')).toBeNull();
209
- expect(result.container.querySelector('[role="combobox"]')).not.toBeNull();
165
+ expect(formatCalendarDate(onSubmit.mock.calls[0][0].due, "date")).toBe("2026-03-15");
210
166
  });
211
167
  });
212
168
 
213
- describe("Time on touch web (FZ-8114)", () => {
214
- it("a coarse pointer renders the time input and emits TimePicker's string", async () => {
215
- coarse = true;
216
- const onChange = vi.fn();
217
- const result = renderWithProviders(
218
- <TimePicker label="At" name="at" value="09:15" onChange={onChange} />,
169
+ describe("a field too low for the panel (LC-105 collision)", () => {
170
+ afterEach(() => {
171
+ vi.restoreAllMocks();
172
+ });
173
+
174
+ it("takes MPO's bottom sheet, still never a native input", async () => {
175
+ const field = { x: 16, y: window.innerHeight - 60, width: 358, height: 44 };
176
+ const original = HTMLElement.prototype.getBoundingClientRect;
177
+ vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockImplementation(
178
+ function (this: HTMLElement) {
179
+ if (!this.hasAttribute("data-fp-reference")) return original.call(this);
180
+ return {
181
+ ...field,
182
+ top: field.y,
183
+ left: field.x,
184
+ right: field.x + field.width,
185
+ bottom: field.y + field.height,
186
+ toJSON: () => ({}),
187
+ } as DOMRect;
188
+ },
219
189
  );
220
- const input = touchInput(result.container, "time");
221
- expect(input.value).toBe("09:15");
222
- await act(async () => {
223
- fireEvent.change(input, { target: { value: "14:30" } });
224
- });
225
- expect(onChange).toHaveBeenLastCalledWith("14:30");
226
- expect(result.container.querySelector('input[value="2:30 PM"]')).not.toBeNull();
227
- });
228
-
229
- it("showSeconds emits HH:mm:ss even when the browser drops :00", async () => {
230
- coarse = true;
231
- const onChange = vi.fn();
232
- const result = renderWithProviders(
233
- <TimePicker label="At" name="at" showSeconds onChange={onChange} />,
190
+ vi.spyOn(HTMLElement.prototype, "scrollHeight", "get").mockImplementation(
191
+ function (this: HTMLElement) {
192
+ return this.hasAttribute("data-fp-panel") ? 350 : 0;
193
+ },
234
194
  );
235
- await act(async () => {
236
- fireEvent.change(touchInput(result.container, "time"), { target: { value: "14:30" } });
237
- });
238
- expect(onChange).toHaveBeenLastCalledWith("14:30:00");
239
- });
240
-
241
- it("a fine pointer keeps the MPO wheels trigger", () => {
242
- const result = renderWithProviders(<TimePicker label="At" name="at" />);
243
- expect(result.container.querySelector('input[type="time"]')).toBeNull();
244
- });
245
- });
246
-
247
- describe("the touch input stays inside its box (FZ-8114)", () => {
248
- it.each([
249
- ["date", <DatePicker key="d" label="Due" name="due" />],
250
- ["datetime-local", <DatetimePicker key="dt" label="Starts" name="starts" />],
251
- ["time", <TimePicker key="t" label="At" name="at" />],
252
- ] as const)("the %s input's box is its positioning context", (type, field) => {
253
- coarse = true;
254
- const result = renderWithProviders(field);
255
- const box = touchInput(result.container, type).parentElement as HTMLElement;
256
- expect(getComputedStyle(box).position).toBe("relative");
195
+ const result = renderWithProviders(<DatePicker label="Due" name="due" />);
196
+ tap(result.getByTestId("date-picker-trigger"));
197
+ await waitFor(() => expect(sheets().length).toBe(1));
198
+ expect(document.querySelector("input[type=date], input[type=time]")).toBeNull();
257
199
  });
258
200
  });