@multiplatform.one/frappe-ui 7.66.0 → 7.67.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 (290) hide show
  1. package/dist/cjs/FormSidebar.cjs +95 -194
  2. package/dist/cjs/FormSidebar.native.js +107 -217
  3. package/dist/cjs/FormSidebar.native.js.map +1 -1
  4. package/dist/cjs/FrappeConnectionIndicator.cjs +5 -24
  5. package/dist/cjs/FrappeConnectionIndicator.native.js +5 -24
  6. package/dist/cjs/FrappeConnectionIndicator.native.js.map +1 -1
  7. package/dist/cjs/FrappeImageView.cjs +46 -65
  8. package/dist/cjs/FrappeImageView.native.js +46 -68
  9. package/dist/cjs/FrappeImageView.native.js.map +1 -1
  10. package/dist/cjs/FrappeListView.cjs +24 -67
  11. package/dist/cjs/FrappeListView.native.js +25 -71
  12. package/dist/cjs/FrappeListView.native.js.map +1 -1
  13. package/dist/cjs/FrappeRecordView.cjs +4 -0
  14. package/dist/cjs/FrappeRecordView.native.js +4 -0
  15. package/dist/cjs/FrappeRecordView.native.js.map +1 -1
  16. package/dist/cjs/FrappeReportBuilder.cjs +21 -18
  17. package/dist/cjs/FrappeReportBuilder.native.js +21 -18
  18. package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
  19. package/dist/cjs/SyncErrorNotice.cjs +8 -36
  20. package/dist/cjs/SyncErrorNotice.native.js +8 -36
  21. package/dist/cjs/SyncErrorNotice.native.js.map +1 -1
  22. package/dist/cjs/desk/DeskFormSidebar.cjs +1 -6
  23. package/dist/cjs/desk/DeskFormSidebar.native.js +1 -6
  24. package/dist/cjs/desk/DeskFormSidebar.native.js.map +1 -1
  25. package/dist/cjs/desk/DeskShell.native.js.map +1 -1
  26. package/dist/cjs/fields/Date/catalog.native.js +0 -1
  27. package/dist/cjs/fields/Date/catalog.native.js.map +1 -1
  28. package/dist/cjs/fields/Date/index.native.js +4 -363
  29. package/dist/cjs/fields/Date/index.native.js.map +1 -1
  30. package/dist/cjs/insights/InsightsFacetTags.cjs +16 -92
  31. package/dist/cjs/insights/InsightsFacetTags.native.js +15 -97
  32. package/dist/cjs/insights/InsightsFacetTags.native.js.map +1 -1
  33. package/dist/cjs/list/FacetBadge.cjs +4 -28
  34. package/dist/cjs/list/FacetBadge.native.js +4 -28
  35. package/dist/cjs/list/FacetBadge.native.js.map +1 -1
  36. package/dist/cjs/list/FacetRail.cjs +37 -128
  37. package/dist/cjs/list/FacetRail.native.js +32 -130
  38. package/dist/cjs/list/FacetRail.native.js.map +1 -1
  39. package/dist/cjs/list/RecordCard.cjs +107 -127
  40. package/dist/cjs/list/RecordCard.native.js +86 -96
  41. package/dist/cjs/list/RecordCard.native.js.map +1 -1
  42. package/dist/cjs/list/ShufflePanel.cjs +57 -36
  43. package/dist/cjs/list/ShufflePanel.native.js +64 -39
  44. package/dist/cjs/list/ShufflePanel.native.js.map +1 -1
  45. package/dist/cjs/record/RecordHero.cjs +7 -85
  46. package/dist/cjs/record/RecordHero.native.js +7 -85
  47. package/dist/cjs/record/RecordHero.native.js.map +1 -1
  48. package/dist/cjs/record/RecordPage.cjs +1 -0
  49. package/dist/cjs/record/RecordPage.native.js +1 -0
  50. package/dist/cjs/record/RecordPage.native.js.map +1 -1
  51. package/dist/cjs/report/ReportViewToolbar.cjs +70 -39
  52. package/dist/cjs/report/ReportViewToolbar.native.js +73 -40
  53. package/dist/cjs/report/ReportViewToolbar.native.js.map +1 -1
  54. package/dist/esm/FormSidebar.mjs +98 -197
  55. package/dist/esm/FormSidebar.mjs.map +1 -1
  56. package/dist/esm/FormSidebar.native.js +110 -220
  57. package/dist/esm/FormSidebar.native.js.map +1 -1
  58. package/dist/esm/FrappeConnectionIndicator.mjs +7 -26
  59. package/dist/esm/FrappeConnectionIndicator.mjs.map +1 -1
  60. package/dist/esm/FrappeConnectionIndicator.native.js +7 -26
  61. package/dist/esm/FrappeConnectionIndicator.native.js.map +1 -1
  62. package/dist/esm/FrappeImageView.mjs +48 -67
  63. package/dist/esm/FrappeImageView.mjs.map +1 -1
  64. package/dist/esm/FrappeImageView.native.js +48 -70
  65. package/dist/esm/FrappeImageView.native.js.map +1 -1
  66. package/dist/esm/FrappeListView.mjs +25 -68
  67. package/dist/esm/FrappeListView.mjs.map +1 -1
  68. package/dist/esm/FrappeListView.native.js +26 -72
  69. package/dist/esm/FrappeListView.native.js.map +1 -1
  70. package/dist/esm/FrappeRecordView.mjs +4 -0
  71. package/dist/esm/FrappeRecordView.mjs.map +1 -1
  72. package/dist/esm/FrappeRecordView.native.js +4 -0
  73. package/dist/esm/FrappeRecordView.native.js.map +1 -1
  74. package/dist/esm/FrappeReportBuilder.mjs +21 -18
  75. package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
  76. package/dist/esm/FrappeReportBuilder.native.js +21 -18
  77. package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
  78. package/dist/esm/SyncErrorNotice.mjs +10 -38
  79. package/dist/esm/SyncErrorNotice.mjs.map +1 -1
  80. package/dist/esm/SyncErrorNotice.native.js +10 -38
  81. package/dist/esm/SyncErrorNotice.native.js.map +1 -1
  82. package/dist/esm/desk/DeskFormSidebar.mjs +1 -6
  83. package/dist/esm/desk/DeskFormSidebar.mjs.map +1 -1
  84. package/dist/esm/desk/DeskFormSidebar.native.js +1 -6
  85. package/dist/esm/desk/DeskFormSidebar.native.js.map +1 -1
  86. package/dist/esm/desk/DeskShell.mjs.map +1 -1
  87. package/dist/esm/desk/DeskShell.native.js.map +1 -1
  88. package/dist/esm/fields/Date/catalog.mjs.map +1 -1
  89. package/dist/esm/fields/Date/catalog.native.js +0 -1
  90. package/dist/esm/fields/Date/catalog.native.js.map +1 -1
  91. package/dist/esm/fields/Date/index.native.js +2 -361
  92. package/dist/esm/fields/Date/index.native.js.map +1 -1
  93. package/dist/esm/insights/InsightsFacetTags.mjs +18 -94
  94. package/dist/esm/insights/InsightsFacetTags.mjs.map +1 -1
  95. package/dist/esm/insights/InsightsFacetTags.native.js +17 -99
  96. package/dist/esm/insights/InsightsFacetTags.native.js.map +1 -1
  97. package/dist/esm/list/FacetBadge.mjs +5 -29
  98. package/dist/esm/list/FacetBadge.mjs.map +1 -1
  99. package/dist/esm/list/FacetBadge.native.js +5 -29
  100. package/dist/esm/list/FacetBadge.native.js.map +1 -1
  101. package/dist/esm/list/FacetRail.mjs +40 -131
  102. package/dist/esm/list/FacetRail.mjs.map +1 -1
  103. package/dist/esm/list/FacetRail.native.js +35 -133
  104. package/dist/esm/list/FacetRail.native.js.map +1 -1
  105. package/dist/esm/list/RecordCard.mjs +107 -127
  106. package/dist/esm/list/RecordCard.mjs.map +1 -1
  107. package/dist/esm/list/RecordCard.native.js +86 -96
  108. package/dist/esm/list/RecordCard.native.js.map +1 -1
  109. package/dist/esm/list/ShufflePanel.mjs +59 -38
  110. package/dist/esm/list/ShufflePanel.mjs.map +1 -1
  111. package/dist/esm/list/ShufflePanel.native.js +66 -41
  112. package/dist/esm/list/ShufflePanel.native.js.map +1 -1
  113. package/dist/esm/record/RecordHero.mjs +8 -86
  114. package/dist/esm/record/RecordHero.mjs.map +1 -1
  115. package/dist/esm/record/RecordHero.native.js +8 -86
  116. package/dist/esm/record/RecordHero.native.js.map +1 -1
  117. package/dist/esm/record/RecordPage.mjs +2 -1
  118. package/dist/esm/record/RecordPage.mjs.map +1 -1
  119. package/dist/esm/record/RecordPage.native.js +2 -1
  120. package/dist/esm/record/RecordPage.native.js.map +1 -1
  121. package/dist/esm/report/ReportViewToolbar.mjs +72 -41
  122. package/dist/esm/report/ReportViewToolbar.mjs.map +1 -1
  123. package/dist/esm/report/ReportViewToolbar.native.js +75 -42
  124. package/dist/esm/report/ReportViewToolbar.native.js.map +1 -1
  125. package/dist/jsx/FormSidebar.mjs +98 -197
  126. package/dist/jsx/FormSidebar.mjs.map +1 -1
  127. package/dist/jsx/FormSidebar.native.js +107 -217
  128. package/dist/jsx/FormSidebar.native.js.map +1 -1
  129. package/dist/jsx/FrappeConnectionIndicator.mjs +7 -26
  130. package/dist/jsx/FrappeConnectionIndicator.mjs.map +1 -1
  131. package/dist/jsx/FrappeConnectionIndicator.native.js +5 -24
  132. package/dist/jsx/FrappeConnectionIndicator.native.js.map +1 -1
  133. package/dist/jsx/FrappeImageView.mjs +48 -67
  134. package/dist/jsx/FrappeImageView.mjs.map +1 -1
  135. package/dist/jsx/FrappeImageView.native.js +46 -68
  136. package/dist/jsx/FrappeImageView.native.js.map +1 -1
  137. package/dist/jsx/FrappeListView.mjs +25 -68
  138. package/dist/jsx/FrappeListView.mjs.map +1 -1
  139. package/dist/jsx/FrappeListView.native.js +25 -71
  140. package/dist/jsx/FrappeListView.native.js.map +1 -1
  141. package/dist/jsx/FrappeRecordView.mjs +4 -0
  142. package/dist/jsx/FrappeRecordView.mjs.map +1 -1
  143. package/dist/jsx/FrappeRecordView.native.js +4 -0
  144. package/dist/jsx/FrappeRecordView.native.js.map +1 -1
  145. package/dist/jsx/FrappeReportBuilder.mjs +21 -18
  146. package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
  147. package/dist/jsx/FrappeReportBuilder.native.js +21 -18
  148. package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
  149. package/dist/jsx/SyncErrorNotice.mjs +10 -38
  150. package/dist/jsx/SyncErrorNotice.mjs.map +1 -1
  151. package/dist/jsx/SyncErrorNotice.native.js +8 -36
  152. package/dist/jsx/SyncErrorNotice.native.js.map +1 -1
  153. package/dist/jsx/desk/DeskFormSidebar.mjs +1 -6
  154. package/dist/jsx/desk/DeskFormSidebar.mjs.map +1 -1
  155. package/dist/jsx/desk/DeskFormSidebar.native.js +1 -6
  156. package/dist/jsx/desk/DeskFormSidebar.native.js.map +1 -1
  157. package/dist/jsx/desk/DeskShell.mjs.map +1 -1
  158. package/dist/jsx/desk/DeskShell.native.js.map +1 -1
  159. package/dist/jsx/fields/Date/catalog.mjs.map +1 -1
  160. package/dist/jsx/fields/Date/catalog.native.js +0 -1
  161. package/dist/jsx/fields/Date/catalog.native.js.map +1 -1
  162. package/dist/jsx/fields/Date/index.native.js +4 -363
  163. package/dist/jsx/fields/Date/index.native.js.map +1 -1
  164. package/dist/jsx/insights/InsightsFacetTags.mjs +18 -94
  165. package/dist/jsx/insights/InsightsFacetTags.mjs.map +1 -1
  166. package/dist/jsx/insights/InsightsFacetTags.native.js +15 -97
  167. package/dist/jsx/insights/InsightsFacetTags.native.js.map +1 -1
  168. package/dist/jsx/list/FacetBadge.mjs +5 -29
  169. package/dist/jsx/list/FacetBadge.mjs.map +1 -1
  170. package/dist/jsx/list/FacetBadge.native.js +4 -28
  171. package/dist/jsx/list/FacetBadge.native.js.map +1 -1
  172. package/dist/jsx/list/FacetRail.mjs +40 -131
  173. package/dist/jsx/list/FacetRail.mjs.map +1 -1
  174. package/dist/jsx/list/FacetRail.native.js +32 -130
  175. package/dist/jsx/list/FacetRail.native.js.map +1 -1
  176. package/dist/jsx/list/RecordCard.mjs +107 -127
  177. package/dist/jsx/list/RecordCard.mjs.map +1 -1
  178. package/dist/jsx/list/RecordCard.native.js +86 -96
  179. package/dist/jsx/list/RecordCard.native.js.map +1 -1
  180. package/dist/jsx/list/ShufflePanel.mjs +59 -38
  181. package/dist/jsx/list/ShufflePanel.mjs.map +1 -1
  182. package/dist/jsx/list/ShufflePanel.native.js +64 -39
  183. package/dist/jsx/list/ShufflePanel.native.js.map +1 -1
  184. package/dist/jsx/record/RecordHero.mjs +8 -86
  185. package/dist/jsx/record/RecordHero.mjs.map +1 -1
  186. package/dist/jsx/record/RecordHero.native.js +7 -85
  187. package/dist/jsx/record/RecordHero.native.js.map +1 -1
  188. package/dist/jsx/record/RecordPage.mjs +2 -1
  189. package/dist/jsx/record/RecordPage.mjs.map +1 -1
  190. package/dist/jsx/record/RecordPage.native.js +1 -0
  191. package/dist/jsx/record/RecordPage.native.js.map +1 -1
  192. package/dist/jsx/report/ReportViewToolbar.mjs +72 -41
  193. package/dist/jsx/report/ReportViewToolbar.mjs.map +1 -1
  194. package/dist/jsx/report/ReportViewToolbar.native.js +73 -40
  195. package/dist/jsx/report/ReportViewToolbar.native.js.map +1 -1
  196. package/package.json +16 -16
  197. package/src/FormSidebar.stories.tsx +1 -1
  198. package/src/FormSidebar.tsx +128 -253
  199. package/src/FrappeConnectionIndicator.tsx +6 -28
  200. package/src/FrappeImageView.stories.tsx +1 -6
  201. package/src/FrappeImageView.tsx +52 -66
  202. package/src/FrappeListView.controls.spec.tsx +3 -2
  203. package/src/FrappeListView.tsx +23 -55
  204. package/src/FrappeRecordView.tsx +16 -3
  205. package/src/FrappeReportBuilder.tsx +21 -19
  206. package/src/SyncErrorNotice.spec.tsx +7 -12
  207. package/src/SyncErrorNotice.stories.tsx +2 -4
  208. package/src/SyncErrorNotice.tsx +13 -41
  209. package/src/desk/DeskFormSidebar.tsx +1 -2
  210. package/src/desk/DeskShell.tsx +1 -0
  211. package/src/fields/Date/catalog.tsx +6 -7
  212. package/src/fields/Date/nativeIsCatalog.spec.tsx +26 -0
  213. package/src/fields/Date/typeParity.spec.tsx +20 -98
  214. package/src/frappeRemainder.designLaw.spec.ts +8 -7
  215. package/src/insights/InsightsFacetTags.tsx +21 -102
  216. package/src/list/FacetBadge.tsx +7 -29
  217. package/src/list/FacetRail.spec.tsx +11 -10
  218. package/src/list/FacetRail.tsx +49 -161
  219. package/src/list/RecordCard.native.tsx +63 -64
  220. package/src/list/RecordCard.spec.tsx +8 -5
  221. package/src/list/RecordCard.tsx +116 -129
  222. package/src/list/ShufflePanel.tsx +65 -38
  223. package/src/record/RecordHero.tsx +11 -51
  224. package/src/record/RecordPage.tsx +6 -1
  225. package/src/report/ReportViewToolbar.spec.tsx +45 -3
  226. package/src/report/ReportViewToolbar.tsx +109 -56
  227. package/types/FormSidebar.d.ts +13 -3
  228. package/types/FormSidebar.d.ts.map +1 -1
  229. package/types/FrappeConnectionIndicator.d.ts +2 -1
  230. package/types/FrappeConnectionIndicator.d.ts.map +1 -1
  231. package/types/FrappeImageView.d.ts.map +1 -1
  232. package/types/FrappeListView.d.ts.map +1 -1
  233. package/types/FrappeRecordView.d.ts.map +1 -1
  234. package/types/FrappeReportBuilder.d.ts.map +1 -1
  235. package/types/SyncErrorNotice.d.ts +4 -6
  236. package/types/SyncErrorNotice.d.ts.map +1 -1
  237. package/types/desk/DeskFormSidebar.d.ts.map +1 -1
  238. package/types/desk/DeskShell.d.ts.map +1 -1
  239. package/types/fields/Date/catalog.d.ts +5 -4
  240. package/types/fields/Date/catalog.d.ts.map +1 -1
  241. package/types/insights/InsightsFacetTags.d.ts +4 -1
  242. package/types/insights/InsightsFacetTags.d.ts.map +1 -1
  243. package/types/list/FacetBadge.d.ts +3 -3
  244. package/types/list/FacetBadge.d.ts.map +1 -1
  245. package/types/list/FacetRail.d.ts +7 -5
  246. package/types/list/FacetRail.d.ts.map +1 -1
  247. package/types/list/RecordCard.d.ts +6 -6
  248. package/types/list/RecordCard.d.ts.map +1 -1
  249. package/types/list/RecordCard.native.d.ts +4 -3
  250. package/types/list/RecordCard.native.d.ts.map +1 -1
  251. package/types/list/ShufflePanel.d.ts +8 -3
  252. package/types/list/ShufflePanel.d.ts.map +1 -1
  253. package/types/record/RecordHero.d.ts +5 -1
  254. package/types/record/RecordHero.d.ts.map +1 -1
  255. package/types/record/RecordPage.d.ts.map +1 -1
  256. package/types/report/ReportViewToolbar.d.ts.map +1 -1
  257. package/dist/cjs/fields/Date/dateTimePickerLoader.cjs +0 -38
  258. package/dist/cjs/fields/Date/dateTimePickerLoader.native.js +0 -42
  259. package/dist/cjs/fields/Date/dateTimePickerLoader.native.js.map +0 -1
  260. package/dist/cjs/fields/Date/sheetKeyboard.cjs +0 -54
  261. package/dist/cjs/fields/Date/sheetKeyboard.native.js +0 -57
  262. package/dist/cjs/fields/Date/sheetKeyboard.native.js.map +0 -1
  263. package/dist/esm/fields/Date/dateTimePickerLoader.mjs +0 -13
  264. package/dist/esm/fields/Date/dateTimePickerLoader.mjs.map +0 -1
  265. package/dist/esm/fields/Date/dateTimePickerLoader.native.js +0 -14
  266. package/dist/esm/fields/Date/dateTimePickerLoader.native.js.map +0 -1
  267. package/dist/esm/fields/Date/sheetKeyboard.mjs +0 -21
  268. package/dist/esm/fields/Date/sheetKeyboard.mjs.map +0 -1
  269. package/dist/esm/fields/Date/sheetKeyboard.native.js +0 -21
  270. package/dist/esm/fields/Date/sheetKeyboard.native.js.map +0 -1
  271. package/dist/jsx/fields/Date/dateTimePickerLoader.mjs +0 -13
  272. package/dist/jsx/fields/Date/dateTimePickerLoader.mjs.map +0 -1
  273. package/dist/jsx/fields/Date/dateTimePickerLoader.native.js +0 -42
  274. package/dist/jsx/fields/Date/dateTimePickerLoader.native.js.map +0 -1
  275. package/dist/jsx/fields/Date/sheetKeyboard.mjs +0 -21
  276. package/dist/jsx/fields/Date/sheetKeyboard.mjs.map +0 -1
  277. package/dist/jsx/fields/Date/sheetKeyboard.native.js +0 -57
  278. package/dist/jsx/fields/Date/sheetKeyboard.native.js.map +0 -1
  279. package/src/fields/Date/dateTimePickerLoader.ts +0 -25
  280. package/src/fields/Date/index.native.tsx +0 -445
  281. package/src/fields/Date/nativeFallback.spec.tsx +0 -21
  282. package/src/fields/Date/nativePresentation.spec.tsx +0 -278
  283. package/src/fields/Date/sheetKeyboard.spec.ts +0 -60
  284. package/src/fields/Date/sheetKeyboard.ts +0 -45
  285. package/types/fields/Date/dateTimePickerLoader.d.ts +0 -11
  286. package/types/fields/Date/dateTimePickerLoader.d.ts.map +0 -1
  287. package/types/fields/Date/index.native.d.ts +0 -14
  288. package/types/fields/Date/index.native.d.ts.map +0 -1
  289. package/types/fields/Date/sheetKeyboard.d.ts +0 -20
  290. package/types/fields/Date/sheetKeyboard.d.ts.map +0 -1
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The Frappe `Date` field has no native fork (FZ-8000, LC-175): iOS and
3
+ * Android resolve the same catalog field the web does, and forms'
4
+ * `DatePicker`, `DatetimePicker` and `TimePicker` open the OS sheet
5
+ * themselves (`OsDateTimeSheet`, with the catalog overlay when the optional
6
+ * `@react-native-community/datetimepicker` peer is absent, FZ-7357).
7
+ */
8
+
9
+ import { existsSync } from "node:fs";
10
+ import { fileURLToPath } from "node:url";
11
+ import { describe, expect, it } from "vitest";
12
+ import * as catalog from "./catalog";
13
+ import * as entry from "./index";
14
+
15
+ const here = (file: string) => fileURLToPath(new URL(file, import.meta.url));
16
+
17
+ describe("native Date field", () => {
18
+ it("is not forked: no index.native.tsx sits beside the catalog field", () => {
19
+ expect(existsSync(here("./index.tsx"))).toBe(true);
20
+ expect(existsSync(here("./index.native.tsx"))).toBe(false);
21
+ });
22
+
23
+ it("is the catalog field on every platform", () => {
24
+ expect(entry.Date).toBe(catalog.Date);
25
+ });
26
+ });
@@ -5,16 +5,12 @@
5
5
  * The web field used to render ONE date-only calendar whatever the type
6
6
  * (SB-M-617): a `Datetime` dropped its time component from the picker, and a
7
7
  * `Time` rendered EMPTY, because the calendar adopts its value through
8
- * `coerceToDate` and `new Date("09:00")` is an Invalid Date. Native, meanwhile,
9
- * had just been activated with all three implemented. These cases pin the
10
- * three web controls — display, edit, and the exact wire string — and then
11
- * drive the SAME pick through both platform halves and assert the emitted
12
- * strings are byte-identical, which is the only reason one `wire.ts` body is
13
- * worth sharing.
14
- *
15
- * The suite runs on web, so the native half's two native-only edges are mocked
16
- * exactly as `nativePresentation.spec.tsx` mocks them: the OS picker (recorded
17
- * rather than rendered) and the platform flags. The web half imports neither.
8
+ * `coerceToDate` and `new Date("09:00")` is an Invalid Date. These cases pin
9
+ * the three web controls — display, edit, and the exact wire string — and
10
+ * then assert the moment an OS sheet commits on native serializes to the same
11
+ * bytes. Native has no fork of this field (FZ-8000): the same catalog field
12
+ * runs there, forms' pickers commit a `Date` from `OsDateTimeSheet`, and the
13
+ * catalog writes it through the one `wire.ts` body.
18
14
  */
19
15
 
20
16
  import { describe, it, expect, vi, beforeAll, beforeEach } from "vitest";
@@ -23,38 +19,6 @@ import { describe, it, expect, vi, beforeAll, beforeEach } from "vitest";
23
19
  // import transforms the whole fields graph.
24
20
  vi.setConfig({ testTimeout: 240_000 });
25
21
 
26
- const mockState = vi.hoisted(() => ({
27
- isIos: true,
28
- isAndroid: false,
29
- /** Props of every mounted OS picker, newest last. */
30
- picks: [] as Record<string, unknown>[],
31
- }));
32
-
33
- vi.mock("./dateTimePickerLoader", () => ({
34
- loadDateTimePicker: () => (props: Record<string, unknown>) => {
35
- mockState.picks.push(props);
36
- return null;
37
- },
38
- }));
39
-
40
- vi.mock("@multiplatform.one/platform", async (importOriginal) => {
41
- const actual = (await importOriginal()) as Record<string, unknown>;
42
- const real = actual.platform as Record<string, unknown>;
43
- return {
44
- ...actual,
45
- platform: new Proxy(
46
- {},
47
- {
48
- get(_target, prop) {
49
- if (prop === "isIos") return mockState.isIos;
50
- if (prop === "isAndroid") return mockState.isAndroid;
51
- return real[prop as string];
52
- },
53
- },
54
- ),
55
- };
56
- });
57
-
58
22
  // Mock react-native-reanimated to prevent ESM module resolution failures
59
23
  // (same shim as tests/fieldRenderers.spec.tsx).
60
24
  vi.mock("react-native-reanimated", () => ({
@@ -95,9 +59,9 @@ vi.mock("react-native-reanimated", () => ({
95
59
  import { renderWithProviders } from "@multiplatform.one/test-utils";
96
60
  import { act, cleanup, fireEvent, waitFor } from "@testing-library/react";
97
61
  import React from "react";
62
+ import { toFrappeDateValue } from "./wire";
98
63
 
99
64
  const loadWebDateField = async () => (await import(".")).Date;
100
- const loadNativeDateField = async () => (await import("./index.native")).Date;
101
65
 
102
66
  /** Intl puts a narrow no-break space before the meridiem; no test should care. */
103
67
  const norm = (value: string) => value.replace(/\s+/gu, " ").trim();
@@ -180,16 +144,13 @@ async function selectPeriod(period: "AM" | "PM") {
180
144
  // (110-155s quiet, past 240s under load), and charging it to an assertion made
181
145
  // whichever case ran first time out.
182
146
  beforeAll(async () => {
183
- await Promise.all([loadWebDateField(), loadNativeDateField()]);
147
+ await loadWebDateField();
184
148
  }, 900_000);
185
149
 
186
150
  beforeEach(() => {
187
151
  // Both halves portal their surfaces, so a previous case's chrome would
188
152
  // otherwise still be in the document when the next one queries it.
189
153
  cleanup();
190
- mockState.picks.length = 0;
191
- mockState.isIos = true;
192
- mockState.isAndroid = false;
193
154
  });
194
155
 
195
156
  // ---------------------------------------------------------------------------
@@ -356,41 +317,14 @@ describe("web Date field — type=datetime renders date AND time", () => {
356
317
  // Cross-half parity — the reason one `wire.ts` body is shared
357
318
  // ---------------------------------------------------------------------------
358
319
 
359
- /** Drive the native half through a pick and return the single emitted string. */
360
- async function nativePick(
361
- type: "date" | "datetime" | "time",
362
- value: string,
363
- triggerText: string,
364
- steps: Date[],
365
- ): Promise<string> {
366
- const DateField = await loadNativeDateField();
367
- const onChange = vi.fn();
368
- const result = renderWithProviders(
369
- <DateField label="Field" value={value} onChange={onChange} type={type} />,
370
- );
371
- await pressText(result.container, triggerText);
372
- await waitFor(() => expect(mockState.picks.length).toBeGreaterThan(0), { timeout: 30_000 });
373
-
374
- for (const [index, step] of steps.entries()) {
375
- const onPickerChange = mockState.picks[mockState.picks.length - 1].onChange as (
376
- event: unknown,
377
- date?: Date,
378
- ) => void;
379
- await act(async () => {
380
- onPickerChange({ type: "set" }, step);
381
- });
382
- // A datetime runs calendar → Next → wheel; the sheet's Next is the seam.
383
- if (index === 0 && steps.length > 1) {
384
- await pressText(document.body, "Next");
385
- await waitFor(() => expect(mockState.picks[mockState.picks.length - 1].mode).toBe("time"), {
386
- timeout: 30_000,
387
- });
388
- }
389
- }
390
- await pressText(document.body, "Done");
391
-
392
- expect(onChange).toHaveBeenCalledTimes(1);
393
- return onChange.mock.calls[0][0] as string;
320
+ /**
321
+ * What the catalog writes for the moment an OS sheet commits on native: forms'
322
+ * native `DatePicker` and `DatetimePicker` hand the field a `Date`, and its
323
+ * `TimePicker` an `HH:mm:ss` string, through the same `onChange` the web
324
+ * pickers use.
325
+ */
326
+ function nativeWrite(type: "date" | "datetime" | "time", committed: Date): string {
327
+ return toFrappeDateValue(committed, type);
394
328
  }
395
329
 
396
330
  /** Drive the web half through the equivalent pick and return the last string. */
@@ -419,9 +353,7 @@ describe("web and native persist byte-identical wire strings", () => {
419
353
  fireEvent.click(dayCell(15)!);
420
354
  });
421
355
  });
422
- cleanup();
423
- mockState.picks.length = 0;
424
- const native = await nativePick("date", "2026-03-05", "Mar 5, 2026", [new Date(2026, 2, 15)]);
356
+ const native = nativeWrite("date", new Date(2026, 2, 15));
425
357
 
426
358
  expect({ web, native }).toEqual({ web: "2026-03-15", native: "2026-03-15" });
427
359
  expect(web).toBe(native);
@@ -431,11 +363,7 @@ describe("web and native persist byte-identical wire strings", () => {
431
363
  const web = await webPick("time", "09:00:00", async () => {
432
364
  await selectPeriod("PM");
433
365
  });
434
- cleanup();
435
- mockState.picks.length = 0;
436
- const native = await nativePick("time", "09:00:00", "9:00 AM", [
437
- new Date(2026, 2, 5, 21, 0, 0),
438
- ]);
366
+ const native = nativeWrite("time", new Date(2026, 2, 5, 21, 0, 0));
439
367
 
440
368
  expect({ web, native }).toEqual({ web: "21:00:00", native: "21:00:00" });
441
369
  expect(web).toBe(native);
@@ -448,14 +376,8 @@ describe("web and native persist byte-identical wire strings", () => {
448
376
  fireEvent.click(dayCell(20)!);
449
377
  });
450
378
  });
451
- cleanup();
452
- mockState.picks.length = 0;
453
- // The native two-step: the calendar commits the day (dropping the clock,
454
- // which is why the wheel step re-supplies it), then the wheel commits it.
455
- const native = await nativePick("datetime", "2026-03-05 14:30:00", "Mar 5, 2026, 2:30 PM", [
456
- new Date(2026, 2, 20),
457
- new Date(2026, 0, 1, 14, 30),
458
- ]);
379
+ // The OS sheet's calendar-then-wheel commits one moment from both halves.
380
+ const native = nativeWrite("datetime", new Date(2026, 2, 20, 14, 30));
459
381
 
460
382
  expect({ web, native }).toEqual({
461
383
  web: "2026-03-20 14:30:00",
@@ -84,13 +84,14 @@ describe("Frappe remainder wrappers — design-law 100%", () => {
84
84
  expect(stories).toMatch(/from ["']\.\/ChartCard["']/);
85
85
  });
86
86
 
87
- it("FrappeImageView uses catalog Card, controlIcon icons, and no media pip (R11/R12)", () => {
87
+ it("FrappeImageView is the MPO ImageGrid photos grid, controlIcon icons, and no media pip (R11/R12, LC-264)", () => {
88
88
  const source = read("FrappeImageView.tsx");
89
- expect(source).toContain("Card");
89
+ expect(source).toMatch(/ImageGrid[\s\S]*from "@multiplatform.one\/components"/);
90
+ expect(source).toContain('variant="photos"');
90
91
  expect(source).toContain("controlIcon");
91
92
  expect(source).not.toMatch(/size=\{48\}/);
92
93
  expect(source).not.toContain("<Tint");
93
- expect(source).toContain("No status pip");
94
+ expect(source).toMatch(/no pip/i);
94
95
  });
95
96
 
96
97
  it("FrappeTreeView uses label fragment and controlIcon, not 12/14px", () => {
@@ -156,10 +157,10 @@ describe("Frappe remainder wrappers — design-law 100%", () => {
156
157
  expect(list).toContain("formCommonColors.muted");
157
158
  });
158
159
 
159
- it("native Date trigger/actions are house Button", () => {
160
- const source = read("fields/Date/index.native.tsx");
161
- expect(source).toMatch(/Button[\s\S]*from "@multiplatform.one\/forms"/);
162
- expect(source).not.toMatch(/Button.*from "tamagui"/);
160
+ it("native Date is the catalog field on the forms pickers, not a fork (LC-175)", () => {
161
+ const source = read("fields/Date/catalog.tsx");
162
+ expect(source).toMatch(/DatePicker[\s\S]*from "@multiplatform.one\/forms"/);
163
+ expect(read("fields/Date/index.tsx")).toContain('export { Date } from "./catalog"');
163
164
  });
164
165
 
165
166
  it("fieldRegistry TextEditor fallback is Skeleton; SmallText/LongText use textAreaProps", () => {
@@ -1,97 +1,7 @@
1
- import { Tags, Text, XStack, isWeb } from "@multiplatform.one/components";
2
- import {
3
- ensureFocusVisibleRing,
4
- ensureKeyboardModalityTracking,
5
- keyboardFocusRingProps,
6
- useResolvedKnobs,
7
- wasKeyboardFocus,
8
- } from "@multiplatform.one/theme";
9
- import { useState } from "react";
1
+ import { Chip, Tags } from "@multiplatform.one/components";
10
2
  import { useTranslation } from "react-i18next";
11
3
  import type { InsightsCount } from "./insightsData";
12
4
 
13
- const chipFocusRing = ensureFocusVisibleRing(keyboardFocusRingProps);
14
-
15
- interface FacetTagChipProps {
16
- value: string;
17
- count: number;
18
- label: string;
19
- tint?: string;
20
- onPress: () => void;
21
- testID?: string;
22
- }
23
-
24
- /**
25
- * A pressable facet value painted from its tint's ramp (DF-04), at the
26
- * compact nestedControl height with the press target restored by hitSlop
27
- * (LC-54, LC-73); the keyboard ring rides the whole chip (LC-71).
28
- */
29
- function FacetTagChip({ value, count, label, tint = "gray", onPress, testID }: FacetTagChipProps) {
30
- const { t } = useTranslation();
31
- const { knobProps } = useResolvedKnobs();
32
- const nested = knobProps.nestedControl;
33
- const [kbFocus, setKbFocus] = useState(false);
34
- if (isWeb) ensureKeyboardModalityTracking();
35
- return (
36
- <XStack
37
- {...knobProps.borderRadius}
38
- role="button"
39
- tabIndex={0}
40
- aria-label={t("{{value}}, {{count}} records", { value: label, count })}
41
- cursor="pointer"
42
- alignItems="center"
43
- gap="$1.5"
44
- paddingHorizontal="$2"
45
- minHeight={nested.px}
46
- height={nested.px}
47
- hitSlop={nested.hitSlop}
48
- data-nested-px={nested.px}
49
- backgroundColor={`$${tint}3` as never}
50
- hoverStyle={{ backgroundColor: `$${tint}4` as never }}
51
- pressStyle={{ backgroundColor: `$${tint}5` as never }}
52
- outlineWidth={0}
53
- focusStyle={{ outlineWidth: 0 }}
54
- focusVisibleStyle={chipFocusRing}
55
- {...(isWeb
56
- ? {
57
- onFocus: () => {
58
- if (wasKeyboardFocus()) setKbFocus(true);
59
- },
60
- onBlur: () => setKbFocus(false),
61
- onKeyDown: (event: { key?: string; preventDefault?: () => void }) => {
62
- if (event.key === "Enter" || event.key === " ") {
63
- event.preventDefault?.();
64
- onPress();
65
- }
66
- },
67
- }
68
- : undefined)}
69
- {...(kbFocus ? keyboardFocusRingProps : undefined)}
70
- onPress={onPress}
71
- testID={testID ? `${testID}-${value}` : undefined}
72
- >
73
- <XStack
74
- width="$1"
75
- height="$1"
76
- borderRadius={1000}
77
- backgroundColor={`$${tint}9` as never}
78
- flexShrink={0}
79
- />
80
- <Text
81
- {...knobProps.label}
82
- color={`$${tint}11` as never}
83
- textTransform="capitalize"
84
- userSelect="none"
85
- >
86
- {label}
87
- </Text>
88
- <Text {...knobProps.label} color={`$${tint}11` as never} opacity={0.7} userSelect="none">
89
- {count}
90
- </Text>
91
- </XStack>
92
- );
93
- }
94
-
95
5
  export interface InsightsFacetTagsProps {
96
6
  distribution: readonly InsightsCount[];
97
7
  label: (value: string) => string;
@@ -100,7 +10,10 @@ export interface InsightsFacetTagsProps {
100
10
  testID?: string;
101
11
  }
102
12
 
103
- /** A facet's values with their record counts, each opening the list narrowed to it. */
13
+ /**
14
+ * A facet's values with their record counts as MPO `Chip`s painted from each
15
+ * value's ramp (DF-04, LC-175), each opening the list narrowed to it.
16
+ */
104
17
  export function InsightsFacetTags({
105
18
  distribution,
106
19
  label,
@@ -115,16 +28,22 @@ export function InsightsFacetTags({
115
28
  enableAdd={false}
116
29
  enableRemove={false}
117
30
  labels={{ noTags: t("No values yet") }}
118
- renderTag={(tag) => (
119
- <FacetTagChip
120
- value={tag.id}
121
- count={distribution.find((entry) => entry.value === tag.id)?.count ?? 0}
122
- label={tag.label}
123
- tint={tint?.(tag.id)}
124
- onPress={() => onSelect(tag.id)}
125
- testID={testID}
126
- />
127
- )}
31
+ renderTag={(tag) => {
32
+ const count = distribution.find((entry) => entry.value === tag.id)?.count ?? 0;
33
+ const hue = tint?.(tag.id) ?? "gray";
34
+ return (
35
+ <Chip
36
+ color={hue}
37
+ swatch={`$${hue}9` as never}
38
+ count={count}
39
+ aria-label={t("{{value}}, {{count}} records", { value: tag.label, count })}
40
+ onPress={() => onSelect(tag.id)}
41
+ testID={testID ? `${testID}-${tag.id}` : undefined}
42
+ >
43
+ {tag.label}
44
+ </Chip>
45
+ );
46
+ }}
128
47
  />
129
48
  );
130
49
  }
@@ -1,44 +1,22 @@
1
- import { Text, XStack } from "@multiplatform.one/components";
2
- import { hairlineWidth, sizeTokenStep, useResolvedKnobs } from "@multiplatform.one/theme";
1
+ import { Chip, type ChipSize } from "@multiplatform.one/components";
3
2
  import type { SizeTokens } from "tamagui";
4
- import { facetBadgeSteps } from "./facetBadgeSteps";
5
3
 
6
4
  export interface FacetBadgeProps {
7
5
  label: string;
8
6
  /** Theme ramp (`"green"`) painting the badge (LC-147). */
9
7
  tint?: string;
10
- /** Size token (Tamagui's own `size`); below `$true` the badge draws a step smaller. */
8
+ /** Size token (Tamagui's own `size`); omitted, the chip takes the ambient size. */
11
9
  size?: SizeTokens;
12
10
  }
13
11
 
14
12
  /**
15
- * One facet value as a static badge at the ambient size token, the tint's
16
- * step-4 fill, step-7 border and step-12 text (DF-04, AA in both schemes).
13
+ * One facet value as a static MPO `Chip` tinted from its ramp: the ramp's
14
+ * step-4 fill and step-12 ink, AA in both schemes (DF-04, LC-175).
17
15
  */
18
16
  export function FacetBadge({ label, tint = "gray", size }: FacetBadgeProps) {
19
- const { knobProps } = useResolvedKnobs({ size });
20
- const small = sizeTokenStep(knobProps.sizeToken) < 0;
21
17
  return (
22
- <XStack
23
- {...knobProps.borderRadius}
24
- paddingHorizontal={small ? "$1.5" : "$2"}
25
- paddingVertical={small ? "$0.5" : "$1"}
26
- backgroundColor={`$${tint}${facetBadgeSteps.fill}` as never}
27
- borderColor={`$${tint}${facetBadgeSteps.border}` as never}
28
- borderWidth={hairlineWidth}
29
- alignItems="center"
30
- >
31
- <Text
32
- {...knobProps.label}
33
- {...knobProps.body}
34
- textTransform="capitalize"
35
- fontWeight="700"
36
- color={`$${tint}${facetBadgeSteps.ink}` as never}
37
- {...(small ? { fontSize: "$1" as const } : null)}
38
- letterSpacing={0.3}
39
- >
40
- {label}
41
- </Text>
42
- </XStack>
18
+ <Chip color={tint} size={size as ChipSize | undefined}>
19
+ {label}
20
+ </Chip>
43
21
  );
44
22
  }
@@ -1,11 +1,11 @@
1
1
  import { renderWithProviders } from "@multiplatform.one/test-utils";
2
+ import { chipFrameGeometry, chipTextSize } from "@multiplatform.one/components";
2
3
  import {
3
4
  applyThemeScale,
4
5
  createDefaultThemeConfig,
5
6
  DEFAULT_SCALE_ID,
6
7
  densePreset,
7
8
  Surface,
8
- sizeRecipeForToken,
9
9
  sizeKeyBelow,
10
10
  } from "@multiplatform.one/theme";
11
11
  import { cleanup, fireEvent } from "@testing-library/react";
@@ -22,26 +22,25 @@ const options = [
22
22
  { value: "fire", label: "Fire", count: 2 },
23
23
  ];
24
24
 
25
- describe("FacetRail chip recipes", () => {
25
+ describe("FacetRail chips are MPO Chips (LC-175)", () => {
26
26
  for (const scaleId of [DEFAULT_SCALE_ID, "dense"]) {
27
27
  for (const size of ["$true", "$3", "$5"] as const) {
28
- it(`${scaleId} at ${size} paints one full step below its controls`, () => {
28
+ it(`${scaleId} at ${size} paints the MPO chip one full step below its controls`, () => {
29
29
  applyThemeScale(config.scales![scaleId]);
30
30
  const token = sizeKeyBelow(size);
31
- const recipe = sizeRecipeForToken(token, { family: "chip" });
32
- const count = sizeRecipeForToken(sizeKeyBelow(token), { family: "chip" });
31
+ const geometry = chipFrameGeometry(token);
32
+ const font = chipTextSize(token as "$2");
33
33
  const result = renderWithProviders(
34
34
  <Surface size={size}>
35
35
  <FacetRail label="Type" value="" options={options} onChange={() => {}} />
36
36
  </Surface>,
37
37
  );
38
38
  const frame = result.getByRole("radio", { name: "Fire 2" });
39
+ expect(frame.hasAttribute("data-chip")).toBe(true);
39
40
  const style = getComputedStyle(frame);
40
- expect(style.height).toBe(`${recipe.height}px`);
41
- expect(style.paddingLeft).toBe(`${recipe.paddingHorizontal}px`);
42
- expect(style.gap).toBe(`${recipe.gap}px`);
43
- expect(getComputedStyle(result.getByText("Fire")).fontSize).toBe(`${recipe.fontSize}px`);
44
- expect(getComputedStyle(result.getByText("2")).fontSize).toBe(`${count.fontSize}px`);
41
+ expect(style.paddingLeft).toBe(`${geometry.paddingHorizontal}px`);
42
+ expect(getComputedStyle(result.getByText("Fire")).fontSize).toBe(`${font}px`);
43
+ expect(getComputedStyle(result.getByText("2")).fontSize).toBe(`${font}px`);
45
44
  });
46
45
  }
47
46
  }
@@ -53,6 +52,8 @@ describe("FacetRail chip recipes", () => {
53
52
  );
54
53
  const fire = result.getByRole("radio", { name: "Fire 2" });
55
54
  expect(fire.getAttribute("aria-checked")).toBe("true");
55
+ expect(fire.getAttribute("tabindex")).toBe("0");
56
+ expect(result.getByRole("radio", { name: "All 6" }).getAttribute("tabindex")).toBe("-1");
56
57
  fireEvent.click(fire);
57
58
  expect(onChange).toHaveBeenLastCalledWith("");
58
59
  fireEvent.keyDown(fire, { key: "ArrowLeft" });