@multiplatform.one/frappe-ui 7.11.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 (341) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/FrappeDashboard.cjs +8 -10
  3. package/dist/cjs/FrappeDashboard.native.js +8 -14
  4. package/dist/cjs/FrappeDashboard.native.js.map +1 -1
  5. package/dist/cjs/FrappeForm.cjs +465 -175
  6. package/dist/cjs/FrappeForm.native.js +611 -207
  7. package/dist/cjs/FrappeForm.native.js.map +1 -1
  8. package/dist/cjs/FrappeFormConnections.cjs +125 -0
  9. package/dist/cjs/FrappeFormConnections.native.js +149 -0
  10. package/dist/cjs/FrappeFormConnections.native.js.map +1 -0
  11. package/dist/cjs/FrappeImageView.cjs +7 -9
  12. package/dist/cjs/FrappeImageView.native.js +7 -13
  13. package/dist/cjs/FrappeImageView.native.js.map +1 -1
  14. package/dist/cjs/FrappeList.cjs +29 -4
  15. package/dist/cjs/FrappeList.native.js +37 -5
  16. package/dist/cjs/FrappeList.native.js.map +1 -1
  17. package/dist/cjs/FrappeListView.cjs +102 -29
  18. package/dist/cjs/FrappeListView.native.js +109 -29
  19. package/dist/cjs/FrappeListView.native.js.map +1 -1
  20. package/dist/cjs/FrappeProvider.cjs +7 -2
  21. package/dist/cjs/FrappeProvider.native.js +7 -2
  22. package/dist/cjs/FrappeProvider.native.js.map +1 -1
  23. package/dist/cjs/FrappeReportBuilder.cjs +10 -11
  24. package/dist/cjs/FrappeReportBuilder.native.js +10 -16
  25. package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
  26. package/dist/cjs/FrappeRowsReader.cjs +49 -0
  27. package/dist/cjs/FrappeRowsReader.native.js +53 -0
  28. package/dist/cjs/FrappeRowsReader.native.js.map +1 -0
  29. package/dist/cjs/FrappeTable.cjs +18 -6
  30. package/dist/cjs/FrappeTable.native.js +24 -6
  31. package/dist/cjs/FrappeTable.native.js.map +1 -1
  32. package/dist/cjs/fieldRegistry.cjs +36 -11
  33. package/dist/cjs/fieldRegistry.native.js +59 -11
  34. package/dist/cjs/fieldRegistry.native.js.map +1 -1
  35. package/dist/cjs/fields/Date/index.native.js +5 -2
  36. package/dist/cjs/fields/Date/index.native.js.map +1 -1
  37. package/dist/cjs/fields/Date/sheetKeyboard.cjs +2 -2
  38. package/dist/cjs/fields/Date/sheetKeyboard.native.js +2 -2
  39. package/dist/cjs/fields/Date/sheetKeyboard.native.js.map +1 -1
  40. package/dist/cjs/fields/DateRange/index.cjs +2 -1
  41. package/dist/cjs/fields/DateRange/index.native.js +4 -1
  42. package/dist/cjs/fields/DateRange/index.native.js.map +1 -1
  43. package/dist/cjs/formEngine/index.cjs +4 -2
  44. package/dist/cjs/formEngine/index.native.js +3 -1
  45. package/dist/cjs/formEngine/index.native.js.map +1 -1
  46. package/dist/cjs/formEngine/useSessionRoles.cjs +77 -0
  47. package/dist/cjs/formEngine/useSessionRoles.native.js +84 -0
  48. package/dist/cjs/formEngine/useSessionRoles.native.js.map +1 -0
  49. package/dist/cjs/frappeConnections.cjs +41 -0
  50. package/dist/cjs/frappeConnections.native.js +50 -0
  51. package/dist/cjs/frappeConnections.native.js.map +1 -0
  52. package/dist/cjs/frappeNavigation.cjs +107 -0
  53. package/dist/cjs/frappeNavigation.native.js +112 -0
  54. package/dist/cjs/frappeNavigation.native.js.map +1 -0
  55. package/dist/cjs/index.cjs +18 -1
  56. package/dist/cjs/index.native.js +18 -1
  57. package/dist/cjs/index.native.js.map +1 -1
  58. package/dist/cjs/schemaForm/index.cjs +4 -1
  59. package/dist/cjs/schemaForm/index.native.js +4 -1
  60. package/dist/cjs/schemaForm/index.native.js.map +1 -1
  61. package/dist/cjs/useConnectionFilters.cjs +146 -0
  62. package/dist/cjs/useConnectionFilters.native.js +213 -0
  63. package/dist/cjs/useConnectionFilters.native.js.map +1 -0
  64. package/dist/cjs/useFrappePagination.cjs +37 -18
  65. package/dist/cjs/useFrappePagination.native.js +59 -23
  66. package/dist/cjs/useFrappePagination.native.js.map +1 -1
  67. package/dist/cjs/useFrappeRows.native.js.map +1 -1
  68. package/dist/esm/FrappeDashboard.mjs +9 -11
  69. package/dist/esm/FrappeDashboard.mjs.map +1 -1
  70. package/dist/esm/FrappeDashboard.native.js +9 -15
  71. package/dist/esm/FrappeDashboard.native.js.map +1 -1
  72. package/dist/esm/FrappeForm.mjs +471 -181
  73. package/dist/esm/FrappeForm.mjs.map +1 -1
  74. package/dist/esm/FrappeForm.native.js +599 -195
  75. package/dist/esm/FrappeForm.native.js.map +1 -1
  76. package/dist/esm/FrappeFormConnections.mjs +99 -0
  77. package/dist/esm/FrappeFormConnections.mjs.map +1 -0
  78. package/dist/esm/FrappeFormConnections.native.js +120 -0
  79. package/dist/esm/FrappeFormConnections.native.js.map +1 -0
  80. package/dist/esm/FrappeImageView.mjs +8 -10
  81. package/dist/esm/FrappeImageView.mjs.map +1 -1
  82. package/dist/esm/FrappeImageView.native.js +8 -14
  83. package/dist/esm/FrappeImageView.native.js.map +1 -1
  84. package/dist/esm/FrappeList.mjs +31 -6
  85. package/dist/esm/FrappeList.mjs.map +1 -1
  86. package/dist/esm/FrappeList.native.js +39 -7
  87. package/dist/esm/FrappeList.native.js.map +1 -1
  88. package/dist/esm/FrappeListView.mjs +103 -30
  89. package/dist/esm/FrappeListView.mjs.map +1 -1
  90. package/dist/esm/FrappeListView.native.js +110 -30
  91. package/dist/esm/FrappeListView.native.js.map +1 -1
  92. package/dist/esm/FrappeProvider.mjs +7 -2
  93. package/dist/esm/FrappeProvider.mjs.map +1 -1
  94. package/dist/esm/FrappeProvider.native.js +7 -2
  95. package/dist/esm/FrappeProvider.native.js.map +1 -1
  96. package/dist/esm/FrappeReportBuilder.mjs +11 -12
  97. package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
  98. package/dist/esm/FrappeReportBuilder.native.js +11 -17
  99. package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
  100. package/dist/esm/FrappeRowsReader.mjs +24 -0
  101. package/dist/esm/FrappeRowsReader.mjs.map +1 -0
  102. package/dist/esm/FrappeRowsReader.native.js +25 -0
  103. package/dist/esm/FrappeRowsReader.native.js.map +1 -0
  104. package/dist/esm/FrappeTable.mjs +18 -6
  105. package/dist/esm/FrappeTable.mjs.map +1 -1
  106. package/dist/esm/FrappeTable.native.js +24 -6
  107. package/dist/esm/FrappeTable.native.js.map +1 -1
  108. package/dist/esm/fieldRegistry.mjs +35 -12
  109. package/dist/esm/fieldRegistry.mjs.map +1 -1
  110. package/dist/esm/fieldRegistry.native.js +58 -12
  111. package/dist/esm/fieldRegistry.native.js.map +1 -1
  112. package/dist/esm/fields/Date/index.native.js +6 -3
  113. package/dist/esm/fields/Date/index.native.js.map +1 -1
  114. package/dist/esm/fields/Date/sheetKeyboard.mjs +2 -2
  115. package/dist/esm/fields/Date/sheetKeyboard.mjs.map +1 -1
  116. package/dist/esm/fields/Date/sheetKeyboard.native.js +2 -2
  117. package/dist/esm/fields/Date/sheetKeyboard.native.js.map +1 -1
  118. package/dist/esm/fields/DateRange/index.mjs +2 -1
  119. package/dist/esm/fields/DateRange/index.mjs.map +1 -1
  120. package/dist/esm/fields/DateRange/index.native.js +4 -1
  121. package/dist/esm/fields/DateRange/index.native.js.map +1 -1
  122. package/dist/esm/formEngine/index.mjs +2 -1
  123. package/dist/esm/formEngine/index.mjs.map +1 -1
  124. package/dist/esm/formEngine/index.native.js +2 -1
  125. package/dist/esm/formEngine/index.native.js.map +1 -1
  126. package/dist/esm/formEngine/useSessionRoles.mjs +52 -0
  127. package/dist/esm/formEngine/useSessionRoles.mjs.map +1 -0
  128. package/dist/esm/formEngine/useSessionRoles.native.js +56 -0
  129. package/dist/esm/formEngine/useSessionRoles.native.js.map +1 -0
  130. package/dist/esm/frappeConnections.mjs +15 -0
  131. package/dist/esm/frappeConnections.mjs.map +1 -0
  132. package/dist/esm/frappeConnections.native.js +21 -0
  133. package/dist/esm/frappeConnections.native.js.map +1 -0
  134. package/dist/esm/frappeNavigation.mjs +78 -0
  135. package/dist/esm/frappeNavigation.mjs.map +1 -0
  136. package/dist/esm/frappeNavigation.native.js +80 -0
  137. package/dist/esm/frappeNavigation.native.js.map +1 -0
  138. package/dist/esm/index.mjs +7 -3
  139. package/dist/esm/index.mjs.map +1 -1
  140. package/dist/esm/index.native.js +7 -3
  141. package/dist/esm/index.native.js.map +1 -1
  142. package/dist/esm/schemaForm/index.mjs +4 -1
  143. package/dist/esm/schemaForm/index.mjs.map +1 -1
  144. package/dist/esm/schemaForm/index.native.js +4 -1
  145. package/dist/esm/schemaForm/index.native.js.map +1 -1
  146. package/dist/esm/useConnectionFilters.mjs +121 -0
  147. package/dist/esm/useConnectionFilters.mjs.map +1 -0
  148. package/dist/esm/useConnectionFilters.native.js +185 -0
  149. package/dist/esm/useConnectionFilters.native.js.map +1 -0
  150. package/dist/esm/useFrappePagination.mjs +38 -19
  151. package/dist/esm/useFrappePagination.mjs.map +1 -1
  152. package/dist/esm/useFrappePagination.native.js +60 -24
  153. package/dist/esm/useFrappePagination.native.js.map +1 -1
  154. package/dist/esm/useFrappeRows.mjs.map +1 -1
  155. package/dist/esm/useFrappeRows.native.js.map +1 -1
  156. package/dist/jsx/FrappeDashboard.mjs +9 -11
  157. package/dist/jsx/FrappeDashboard.mjs.map +1 -1
  158. package/dist/jsx/FrappeDashboard.native.js +8 -14
  159. package/dist/jsx/FrappeDashboard.native.js.map +1 -1
  160. package/dist/jsx/FrappeForm.mjs +471 -181
  161. package/dist/jsx/FrappeForm.mjs.map +1 -1
  162. package/dist/jsx/FrappeForm.native.js +611 -207
  163. package/dist/jsx/FrappeForm.native.js.map +1 -1
  164. package/dist/jsx/FrappeFormConnections.mjs +99 -0
  165. package/dist/jsx/FrappeFormConnections.mjs.map +1 -0
  166. package/dist/jsx/FrappeFormConnections.native.js +149 -0
  167. package/dist/jsx/FrappeFormConnections.native.js.map +1 -0
  168. package/dist/jsx/FrappeImageView.mjs +8 -10
  169. package/dist/jsx/FrappeImageView.mjs.map +1 -1
  170. package/dist/jsx/FrappeImageView.native.js +7 -13
  171. package/dist/jsx/FrappeImageView.native.js.map +1 -1
  172. package/dist/jsx/FrappeList.mjs +31 -6
  173. package/dist/jsx/FrappeList.mjs.map +1 -1
  174. package/dist/jsx/FrappeList.native.js +37 -5
  175. package/dist/jsx/FrappeList.native.js.map +1 -1
  176. package/dist/jsx/FrappeListView.mjs +103 -30
  177. package/dist/jsx/FrappeListView.mjs.map +1 -1
  178. package/dist/jsx/FrappeListView.native.js +109 -29
  179. package/dist/jsx/FrappeListView.native.js.map +1 -1
  180. package/dist/jsx/FrappeProvider.mjs +7 -2
  181. package/dist/jsx/FrappeProvider.mjs.map +1 -1
  182. package/dist/jsx/FrappeProvider.native.js +7 -2
  183. package/dist/jsx/FrappeProvider.native.js.map +1 -1
  184. package/dist/jsx/FrappeReportBuilder.mjs +11 -12
  185. package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
  186. package/dist/jsx/FrappeReportBuilder.native.js +10 -16
  187. package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
  188. package/dist/jsx/FrappeRowsReader.mjs +24 -0
  189. package/dist/jsx/FrappeRowsReader.mjs.map +1 -0
  190. package/dist/jsx/FrappeRowsReader.native.js +53 -0
  191. package/dist/jsx/FrappeRowsReader.native.js.map +1 -0
  192. package/dist/jsx/FrappeTable.mjs +18 -6
  193. package/dist/jsx/FrappeTable.mjs.map +1 -1
  194. package/dist/jsx/FrappeTable.native.js +24 -6
  195. package/dist/jsx/FrappeTable.native.js.map +1 -1
  196. package/dist/jsx/fieldRegistry.mjs +35 -12
  197. package/dist/jsx/fieldRegistry.mjs.map +1 -1
  198. package/dist/jsx/fieldRegistry.native.js +59 -11
  199. package/dist/jsx/fieldRegistry.native.js.map +1 -1
  200. package/dist/jsx/fields/Date/index.native.js +5 -2
  201. package/dist/jsx/fields/Date/index.native.js.map +1 -1
  202. package/dist/jsx/fields/Date/sheetKeyboard.mjs +2 -2
  203. package/dist/jsx/fields/Date/sheetKeyboard.mjs.map +1 -1
  204. package/dist/jsx/fields/Date/sheetKeyboard.native.js +2 -2
  205. package/dist/jsx/fields/Date/sheetKeyboard.native.js.map +1 -1
  206. package/dist/jsx/fields/DateRange/index.mjs +2 -1
  207. package/dist/jsx/fields/DateRange/index.mjs.map +1 -1
  208. package/dist/jsx/fields/DateRange/index.native.js +4 -1
  209. package/dist/jsx/fields/DateRange/index.native.js.map +1 -1
  210. package/dist/jsx/formEngine/index.mjs +2 -1
  211. package/dist/jsx/formEngine/index.mjs.map +1 -1
  212. package/dist/jsx/formEngine/index.native.js +3 -1
  213. package/dist/jsx/formEngine/index.native.js.map +1 -1
  214. package/dist/jsx/formEngine/useSessionRoles.mjs +52 -0
  215. package/dist/jsx/formEngine/useSessionRoles.mjs.map +1 -0
  216. package/dist/jsx/formEngine/useSessionRoles.native.js +84 -0
  217. package/dist/jsx/formEngine/useSessionRoles.native.js.map +1 -0
  218. package/dist/jsx/frappeConnections.mjs +15 -0
  219. package/dist/jsx/frappeConnections.mjs.map +1 -0
  220. package/dist/jsx/frappeConnections.native.js +50 -0
  221. package/dist/jsx/frappeConnections.native.js.map +1 -0
  222. package/dist/jsx/frappeNavigation.mjs +78 -0
  223. package/dist/jsx/frappeNavigation.mjs.map +1 -0
  224. package/dist/jsx/frappeNavigation.native.js +112 -0
  225. package/dist/jsx/frappeNavigation.native.js.map +1 -0
  226. package/dist/jsx/index.js +7 -3
  227. package/dist/jsx/index.js.map +1 -1
  228. package/dist/jsx/index.mjs +7 -3
  229. package/dist/jsx/index.mjs.map +1 -1
  230. package/dist/jsx/index.native.js +18 -1
  231. package/dist/jsx/index.native.js.map +1 -1
  232. package/dist/jsx/schemaForm/index.mjs +4 -1
  233. package/dist/jsx/schemaForm/index.mjs.map +1 -1
  234. package/dist/jsx/schemaForm/index.native.js +4 -1
  235. package/dist/jsx/schemaForm/index.native.js.map +1 -1
  236. package/dist/jsx/useConnectionFilters.mjs +121 -0
  237. package/dist/jsx/useConnectionFilters.mjs.map +1 -0
  238. package/dist/jsx/useConnectionFilters.native.js +213 -0
  239. package/dist/jsx/useConnectionFilters.native.js.map +1 -0
  240. package/dist/jsx/useFrappePagination.mjs +38 -19
  241. package/dist/jsx/useFrappePagination.mjs.map +1 -1
  242. package/dist/jsx/useFrappePagination.native.js +59 -23
  243. package/dist/jsx/useFrappePagination.native.js.map +1 -1
  244. package/dist/jsx/useFrappeRows.mjs.map +1 -1
  245. package/dist/jsx/useFrappeRows.native.js.map +1 -1
  246. package/package.json +24 -20
  247. package/src/FileBrowser/AttachLibrary.stories.tsx +23 -0
  248. package/src/FrappeDashboard.tsx +11 -16
  249. package/src/FrappeForm.connections.spec.tsx +280 -0
  250. package/src/FrappeForm.connections.stories.tsx +93 -0
  251. package/src/FrappeForm.deskSemantics.spec.tsx +284 -0
  252. package/src/FrappeForm.tsx +560 -145
  253. package/src/FrappeFormConnections.tsx +148 -0
  254. package/src/FrappeGuestMeta.spec.tsx +143 -0
  255. package/src/FrappeImageView.tsx +8 -12
  256. package/src/FrappeList.tsx +71 -15
  257. package/src/FrappeListView.cards.spec.tsx +175 -0
  258. package/src/FrappeListView.cards.stories.tsx +131 -0
  259. package/src/FrappeListView.connections.spec.tsx +192 -0
  260. package/src/FrappeListView.connections.stories.tsx +99 -0
  261. package/src/FrappeListView.seed.spec.tsx +228 -0
  262. package/src/FrappeListView.tsx +177 -31
  263. package/src/FrappeProvider.tsx +13 -1
  264. package/src/FrappeReportBuilder.tsx +11 -19
  265. package/src/FrappeRowsReader.tsx +38 -0
  266. package/src/FrappeTable.optionalProps.spec.tsx +249 -0
  267. package/src/FrappeTable.spec.tsx +32 -42
  268. package/src/FrappeTable.stories.spec.tsx +55 -0
  269. package/src/FrappeTable.stories.tsx +28 -37
  270. package/src/FrappeTable.tsx +108 -45
  271. package/src/childTableDependsOn.spec.tsx +85 -0
  272. package/src/desk/DeskFormSidebar.spec.tsx +20 -0
  273. package/src/desk/DeskFormSidebar.stories.tsx +42 -0
  274. package/src/fieldRegistry.spec.tsx +27 -64
  275. package/src/fieldRegistry.ts +71 -14
  276. package/src/fields/Date/index.native.tsx +4 -2
  277. package/src/fields/Date/sheetKeyboard.spec.ts +14 -5
  278. package/src/fields/Date/sheetKeyboard.ts +12 -4
  279. package/src/fields/DateRange/DateRange.spec.tsx +67 -0
  280. package/src/fields/DateRange/index.tsx +5 -1
  281. package/src/fields/LinkTree.spec.tsx +8 -0
  282. package/src/fields/LinkTree.stories.tsx +28 -5
  283. package/src/fields/Select/Select.hydration.spec.tsx +177 -0
  284. package/src/formEngine/doctypeMeta.ts +15 -114
  285. package/src/formEngine/index.ts +2 -0
  286. package/src/formEngine/seam.spec.ts +23 -4
  287. package/src/formEngine/useSessionRoles.ts +73 -0
  288. package/src/frappeConnections.ts +50 -0
  289. package/src/frappeNavigation.tsx +112 -0
  290. package/src/frappeRoutes.spec.tsx +335 -0
  291. package/src/index.ts +28 -1
  292. package/src/schemaForm/index.tsx +1 -0
  293. package/src/useConnectionFilters.tsx +157 -0
  294. package/src/useFrappePagination.ts +70 -26
  295. package/src/useFrappeRows.sharedPath.spec.tsx +206 -0
  296. package/src/useFrappeRows.ts +5 -4
  297. package/types/FrappeDashboard.d.ts +3 -4
  298. package/types/FrappeDashboard.d.ts.map +1 -1
  299. package/types/FrappeForm.d.ts +57 -5
  300. package/types/FrappeForm.d.ts.map +1 -1
  301. package/types/FrappeFormConnections.d.ts +18 -0
  302. package/types/FrappeFormConnections.d.ts.map +1 -0
  303. package/types/FrappeImageView.d.ts +2 -3
  304. package/types/FrappeImageView.d.ts.map +1 -1
  305. package/types/FrappeList.d.ts +20 -2
  306. package/types/FrappeList.d.ts.map +1 -1
  307. package/types/FrappeListView.d.ts +53 -3
  308. package/types/FrappeListView.d.ts.map +1 -1
  309. package/types/FrappeProvider.d.ts +8 -1
  310. package/types/FrappeProvider.d.ts.map +1 -1
  311. package/types/FrappeReportBuilder.d.ts.map +1 -1
  312. package/types/FrappeRowsReader.d.ts +26 -0
  313. package/types/FrappeRowsReader.d.ts.map +1 -0
  314. package/types/FrappeTable.d.ts +39 -8
  315. package/types/FrappeTable.d.ts.map +1 -1
  316. package/types/fieldRegistry.d.ts +17 -3
  317. package/types/fieldRegistry.d.ts.map +1 -1
  318. package/types/fields/Date/index.native.d.ts.map +1 -1
  319. package/types/fields/Date/sheetKeyboard.d.ts +4 -3
  320. package/types/fields/Date/sheetKeyboard.d.ts.map +1 -1
  321. package/types/fields/DateRange/index.d.ts.map +1 -1
  322. package/types/formEngine/doctypeMeta.d.ts +10 -114
  323. package/types/formEngine/doctypeMeta.d.ts.map +1 -1
  324. package/types/formEngine/index.d.ts +1 -0
  325. package/types/formEngine/index.d.ts.map +1 -1
  326. package/types/formEngine/useSessionRoles.d.ts +19 -0
  327. package/types/formEngine/useSessionRoles.d.ts.map +1 -0
  328. package/types/frappeConnections.d.ts +33 -0
  329. package/types/frappeConnections.d.ts.map +1 -0
  330. package/types/frappeNavigation.d.ts +49 -0
  331. package/types/frappeNavigation.d.ts.map +1 -0
  332. package/types/index.d.ts +7 -3
  333. package/types/index.d.ts.map +1 -1
  334. package/types/schemaForm/index.d.ts.map +1 -1
  335. package/types/useConnectionFilters.d.ts +34 -0
  336. package/types/useConnectionFilters.d.ts.map +1 -0
  337. package/types/useFrappePagination.d.ts +18 -5
  338. package/types/useFrappePagination.d.ts.map +1 -1
  339. package/types/useFrappeRows.d.ts +5 -4
  340. package/types/useFrappeRows.d.ts.map +1 -1
  341. package/src/FileBrowser/AttachLibrary.spec.tsx +0 -74
@@ -1,8 +1,4 @@
1
- import {
2
- useFrappeCollection,
3
- useFrappeDoctypeEvents,
4
- useLiveQuery,
5
- } from "@multiplatform.one/frappe";
1
+ import { useFrappeDoctypeEvents } from "@multiplatform.one/frappe";
6
2
  import { Dashboard, type DashboardWidget } from "@multiplatform.one/components";
7
3
  import { Button, Spinner, formCommonColors } from "@multiplatform.one/forms";
8
4
  import { ArrowsClockwiseIcon } from "@phosphor-icons/react";
@@ -11,6 +7,7 @@ import { useTranslation } from "react-i18next";
11
7
  import { Text, XStack, YStack, type YStackProps, H3 } from "tamagui";
12
8
  import { formatTimeOfDay, useResolvedKnobs } from "@multiplatform.one/theme";
13
9
  import { useFrappeConfig } from "./FrappeProvider";
10
+ import { useFrappeRows } from "./useFrappeRows";
14
11
 
15
12
  /**
16
13
  * Data source configuration for live data.
@@ -103,16 +100,15 @@ function LiveDashboardContent<TDoc extends { name: string; doctype: string }>({
103
100
  const { knobProps } = useResolvedKnobs();
104
101
  const { t } = useTranslation();
105
102
 
106
- const collection = useFrappeCollection<TDoc>(connectionConfig as any, dataSource.doctype, {
103
+ const {
104
+ rows: liveData,
105
+ isQueryLoading: isLiveLoading,
106
+ isError,
107
+ } = useFrappeRows<TDoc>(connectionConfig as any, dataSource.doctype, {
107
108
  fields: dataSource.fields,
108
109
  filters: dataSource.filters,
110
+ limit: null,
109
111
  });
110
- const {
111
- data: rawData,
112
- isLoading: isLiveLoading,
113
- isError,
114
- } = useLiveQuery((q) => (collection ? q.from({ _list: collection }) : null), [collection]);
115
- const liveData = useMemo(() => (rawData as TDoc[] | undefined) ?? [], [rawData]);
116
112
 
117
113
  // Live staleness: a realtime change on the source doctype marks the view
118
114
  // stale until the collection reconciles (data identity changes) or a
@@ -240,10 +236,9 @@ function LiveDashboardContent<TDoc extends { name: string; doctype: string }>({
240
236
  /**
241
237
  * Thin Frappe wrapper around the generic Dashboard component.
242
238
  *
243
- * Fetches live data via `useFrappeCollection` / `useLiveQuery` if a
244
- * `dataSource` is configured, transforms it into widget data, and
245
- * passes everything to the generic `Dashboard` from
246
- * `@multiplatform.one/components`.
239
+ * Fetches live data via `useFrappeRows` if a `dataSource` is configured,
240
+ * transforms it into widget data, and passes everything to the generic
241
+ * `Dashboard` from `@multiplatform.one/components`.
247
242
  */
248
243
  export function FrappeDashboard<TDoc extends { name: string; doctype: string }>({
249
244
  baseURL,
@@ -0,0 +1,280 @@
1
+ // FrappeForm renders a doctype's connections (MPO-331): the doctypes that
2
+ // link back to the record through a Link field, read from meta.links or
3
+ // declared. A Pokemon shows its type, ability and move rows, which live in
4
+ // three standalone junction doctypes, not child tables. The same form hosts
5
+ // a detail page: render-prop mode hands those rows to `children`, and a
6
+ // server-rendered document stands in until the live read answers.
7
+ import { InMemoryFixtureProvider } from "@multiplatform.one/frappe";
8
+ import { renderWithProviders, TestProviders } from "@multiplatform.one/test-utils";
9
+ import { cleanup, fireEvent, screen, waitFor } from "@testing-library/react";
10
+ import i18next from "i18next";
11
+ import { renderToString } from "react-dom/server";
12
+ import { initReactI18next } from "react-i18next";
13
+ import { Text } from "tamagui";
14
+ import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
15
+ import {
16
+ rosterFixtureData,
17
+ rosterPokemonMeta,
18
+ type RosterPokemon,
19
+ } from "../tests/fixtures/pokemonRoster";
20
+ import type { FrappeConnection } from "./frappeConnections";
21
+ import { FrappeForm, type FrappeConnectionRows, type FrappeFormProps } from "./FrappeForm";
22
+ import { FrappeProvider } from "./FrappeProvider";
23
+
24
+ const host = "https://form-connections.spec.test";
25
+
26
+ beforeAll(async () => {
27
+ await i18next.use(initReactI18next).init({
28
+ lng: "en",
29
+ interpolation: { escapeValue: false },
30
+ resources: { en: { translation: {} } },
31
+ });
32
+ });
33
+
34
+ afterEach(() => {
35
+ cleanup();
36
+ vi.restoreAllMocks();
37
+ });
38
+
39
+ const pokemonConnections: FrappeConnection[] = [
40
+ { doctype: "Pokemon Poke Type", fieldname: "pokemon", fields: ["poke_type", "slot"] },
41
+ { doctype: "Pokemon Poke Ability", fieldname: "pokemon", fields: ["poke_ability", "is_hidden"] },
42
+ {
43
+ doctype: "Pokemon Poke Move",
44
+ fieldname: "pokemon",
45
+ fields: ["poke_move", "level_learned_at", "move_learn_method"],
46
+ },
47
+ ];
48
+
49
+ function corpus(meta: Record<string, unknown> = rosterPokemonMeta) {
50
+ return new InMemoryFixtureProvider({
51
+ DocType: [{ name: "Pokemon", ...meta, doctype: "DocType" }],
52
+ ...rosterFixtureData(),
53
+ });
54
+ }
55
+
56
+ function renderForm(
57
+ props: Partial<FrappeFormProps<RosterPokemon>>,
58
+ fixtures: InMemoryFixtureProvider = corpus(),
59
+ ) {
60
+ return renderWithProviders(
61
+ <FrappeProvider baseURL={host} fixtures={fixtures}>
62
+ <FrappeForm<RosterPokemon>
63
+ doctype="Pokemon"
64
+ showSidebar={false}
65
+ showTimeline={false}
66
+ {...props}
67
+ />
68
+ </FrappeProvider>,
69
+ );
70
+ }
71
+
72
+ function RowsProbe({ connections }: { connections: FrappeConnectionRows }) {
73
+ return (
74
+ <>
75
+ {Object.entries(connections).map(([doctype, rows]) => (
76
+ <Text key={doctype} testID={`rows-${doctype}`}>
77
+ {rows
78
+ .map((row) => String(row.poke_type ?? row.poke_ability ?? row.poke_move))
79
+ .sort()
80
+ .join(",")}
81
+ </Text>
82
+ ))}
83
+ </>
84
+ );
85
+ }
86
+
87
+ describe("FrappeForm connections, schema mode", () => {
88
+ it("shows the record's type, ability and move rows from meta.links on a Connections tab", async () => {
89
+ renderForm({ name: "bulbasaur" });
90
+ await waitFor(() => expect(screen.getByText("Connections")).toBeTruthy());
91
+ fireEvent.click(screen.getByText("Connections"));
92
+ await waitFor(() => expect(screen.getByText("grass")).toBeTruthy());
93
+ expect(screen.getByText("poison")).toBeTruthy();
94
+ expect(screen.getByText("overgrow")).toBeTruthy();
95
+ expect(screen.getByText("chlorophyll")).toBeTruthy();
96
+ expect(screen.getByText("tackle")).toBeTruthy();
97
+ expect(screen.getByText("vine-whip")).toBeTruthy();
98
+ expect(screen.queryByText("thunderbolt")).toBeNull();
99
+ });
100
+
101
+ it("uses declared connections where meta carries no links", async () => {
102
+ const { links: _links, ...metaWithoutLinks } = rosterPokemonMeta;
103
+ renderForm(
104
+ { name: "pikachu", connections: pokemonConnections.slice(2) },
105
+ corpus(metaWithoutLinks),
106
+ );
107
+ await waitFor(() => expect(screen.getByText("Connections")).toBeTruthy());
108
+ fireEvent.click(screen.getByText("Connections"));
109
+ await waitFor(() => expect(screen.getByText("thunderbolt")).toBeTruthy());
110
+ expect(screen.getByText("quick-attack")).toBeTruthy();
111
+ expect(screen.queryByText("static")).toBeNull();
112
+ });
113
+
114
+ it("names each connection with its row count", async () => {
115
+ renderForm({ name: "pikachu", connections: pokemonConnections });
116
+ await waitFor(() => expect(screen.getByText("Connections")).toBeTruthy());
117
+ fireEvent.click(screen.getByText("Connections"));
118
+ await waitFor(() => expect(screen.getByText("Pokemon Poke Move (3)")).toBeTruthy());
119
+ expect(screen.getByText("Pokemon Poke Type (1)")).toBeTruthy();
120
+ });
121
+
122
+ it("an unlinked record says so instead of showing an empty table", async () => {
123
+ renderForm({ name: "eevee", connections: pokemonConnections.slice(1, 2) });
124
+ await waitFor(() => expect(screen.getByText("Connections")).toBeTruthy());
125
+ fireEvent.click(screen.getByText("Connections"));
126
+ await waitFor(() => expect(screen.getByText("No Pokemon Poke Ability rows")).toBeTruthy());
127
+ });
128
+
129
+ it("a new record has no connections yet", async () => {
130
+ renderForm({});
131
+ await waitFor(() => expect(screen.getByText("Save")).toBeTruthy());
132
+ expect(screen.queryByText("Connections")).toBeNull();
133
+ });
134
+
135
+ it("showConnections={false} leaves the tab out", async () => {
136
+ renderForm({ name: "bulbasaur", showConnections: false });
137
+ await waitFor(() => expect(screen.getByText("Save")).toBeTruthy());
138
+ expect(screen.queryByText("Connections")).toBeNull();
139
+ });
140
+ });
141
+
142
+ describe("FrappeForm connections, render-prop mode", () => {
143
+ it("hands children the record's linked rows per doctype", async () => {
144
+ renderForm({
145
+ name: "bulbasaur",
146
+ connections: pokemonConnections,
147
+ showToolbar: false,
148
+ children: ({ connections }) => <RowsProbe connections={connections} />,
149
+ });
150
+ await waitFor(() =>
151
+ expect(screen.getByTestId("rows-Pokemon Poke Type").textContent).toBe("grass,poison"),
152
+ );
153
+ expect(screen.getByTestId("rows-Pokemon Poke Ability").textContent).toBe(
154
+ "chlorophyll,overgrow",
155
+ );
156
+ expect(screen.getByTestId("rows-Pokemon Poke Move").textContent).toBe("tackle,vine-whip");
157
+ });
158
+
159
+ it("reads connections from meta.links when none are declared", async () => {
160
+ renderForm({
161
+ name: "pikachu",
162
+ showToolbar: false,
163
+ children: ({ connections }) => <RowsProbe connections={connections} />,
164
+ });
165
+ await waitFor(() =>
166
+ expect(screen.getByTestId("rows-Pokemon Poke Type").textContent).toBe("electric"),
167
+ );
168
+ });
169
+ });
170
+
171
+ describe("FrappeForm hosting a detail page", () => {
172
+ const seed = {
173
+ name: "bulbasaur",
174
+ doctype: "Pokemon",
175
+ pokemon_name: "bulbasaur (server)",
176
+ } as Partial<RosterPokemon>;
177
+
178
+ const detail = ({ doc }: { doc: Partial<RosterPokemon> }) => (
179
+ <Text testID="detail-name">{doc.pokemon_name}</Text>
180
+ );
181
+
182
+ it("renders the server-rendered document on the server", () => {
183
+ const html = renderToString(
184
+ <TestProviders>
185
+ <FrappeProvider baseURL={host} fixtures={corpus()}>
186
+ <FrappeForm<RosterPokemon>
187
+ doctype="Pokemon"
188
+ name="bulbasaur"
189
+ initialDoc={seed}
190
+ showToolbar={false}
191
+ showSidebar={false}
192
+ showTimeline={false}
193
+ notFoundFallback={<Text>missing</Text>}
194
+ >
195
+ {detail}
196
+ </FrappeForm>
197
+ </FrappeProvider>
198
+ </TestProviders>,
199
+ );
200
+ expect(html).toContain("bulbasaur (server)");
201
+ expect(html).not.toContain("missing");
202
+ expect(html).not.toContain("frappe-form-skeleton");
203
+ });
204
+
205
+ it("shows the server document until the live read lands, then the live one", async () => {
206
+ const fixtures = corpus();
207
+ let release: () => void = () => undefined;
208
+ const gate = new Promise<void>((resolve) => {
209
+ release = resolve;
210
+ });
211
+ const getDoc = fixtures.getDoc.bind(fixtures);
212
+ vi.spyOn(fixtures, "getDoc").mockImplementation(async (...args) => {
213
+ await gate;
214
+ return getDoc(...args);
215
+ });
216
+ renderForm(
217
+ { name: "bulbasaur", initialDoc: seed, showToolbar: false, children: detail },
218
+ fixtures,
219
+ );
220
+ expect(screen.getByTestId("detail-name").textContent).toBe("bulbasaur (server)");
221
+ release();
222
+ await waitFor(() => expect(screen.getByTestId("detail-name").textContent).toBe("bulbasaur"));
223
+ });
224
+
225
+ it("ignores a server document for another name", async () => {
226
+ renderForm({
227
+ name: "pikachu",
228
+ initialDoc: seed,
229
+ showToolbar: false,
230
+ children: detail,
231
+ });
232
+ await waitFor(() => expect(screen.getByTestId("detail-name").textContent).toBe("pikachu"));
233
+ });
234
+
235
+ it("takes the page's own loading, not-found and error states", async () => {
236
+ const fixtures = corpus();
237
+ let release: () => void = () => undefined;
238
+ const gate = new Promise<void>((resolve) => {
239
+ release = resolve;
240
+ });
241
+ const getDoc = fixtures.getDoc.bind(fixtures);
242
+ vi.spyOn(fixtures, "getDoc").mockImplementation(async (...args) => {
243
+ await gate;
244
+ return getDoc(...args);
245
+ });
246
+ renderForm(
247
+ {
248
+ name: "missingno",
249
+ showToolbar: false,
250
+ children: detail,
251
+ loadingFallback: <Text>page skeleton</Text>,
252
+ notFoundFallback: <Text>no such pokemon</Text>,
253
+ },
254
+ fixtures,
255
+ );
256
+ expect(screen.getByText("page skeleton")).toBeTruthy();
257
+ release();
258
+ await waitFor(() => expect(screen.getByText("no such pokemon")).toBeTruthy());
259
+ });
260
+
261
+ it("a failed read is the error state, not not-found", async () => {
262
+ const fixtures = corpus();
263
+ vi.spyOn(fixtures, "getDoc").mockImplementation(async (doctype, ...rest) => {
264
+ if (doctype === "Pokemon") throw Object.assign(new Error("PermissionError"), { status: 403 });
265
+ return InMemoryFixtureProvider.prototype.getDoc.call(fixtures, doctype, ...rest);
266
+ });
267
+ renderForm(
268
+ {
269
+ name: "bulbasaur",
270
+ showToolbar: false,
271
+ children: detail,
272
+ notFoundFallback: <Text>no such pokemon</Text>,
273
+ errorFallback: <Text>could not load</Text>,
274
+ },
275
+ fixtures,
276
+ );
277
+ await waitFor(() => expect(screen.getByText("could not load")).toBeTruthy());
278
+ expect(screen.queryByText("no such pokemon")).toBeNull();
279
+ });
280
+ });
@@ -0,0 +1,93 @@
1
+ import { Chip } from "@multiplatform.one/components";
2
+ import { InMemoryFixtureProvider } from "@multiplatform.one/frappe";
3
+ import { useResolvedKnobs } from "@multiplatform.one/theme";
4
+ import { Text, XStack, YStack } from "tamagui";
5
+ import {
6
+ rosterFixtureData,
7
+ rosterPokemonMeta,
8
+ type RosterPokemon,
9
+ } from "../tests/fixtures/pokemonRoster";
10
+ import type { FrappeConnection } from "./frappeConnections";
11
+ import { FrappeForm, type FrappeConnectionRows } from "./FrappeForm";
12
+ import { FrappeProvider } from "./FrappeProvider";
13
+
14
+ export default {
15
+ title: "FrappeUI/FrappeForm Connections",
16
+ component: FrappeForm,
17
+ tags: ["!test"],
18
+ parameters: {
19
+ status: { type: "stable" },
20
+ docs: {
21
+ description: {
22
+ component:
23
+ "A record's connections: the doctypes linking back to it through a Link field (meta.links, or declared with `connections`). Schema mode lists them on a Connections tab; render-prop mode hands the rows to `children`, which is how a detail page renders them its own way. Pokemon's types, abilities and moves are standalone junction doctypes on the pokemon fixtures.",
24
+ },
25
+ },
26
+ },
27
+ };
28
+
29
+ const host = "https://form-connections.mock.test";
30
+
31
+ const fixtures = () =>
32
+ new InMemoryFixtureProvider({
33
+ DocType: [{ ...rosterPokemonMeta, doctype: "DocType" }],
34
+ ...rosterFixtureData(),
35
+ });
36
+
37
+ const pokemonConnections: FrappeConnection[] = [
38
+ { doctype: "Pokemon Poke Type", fieldname: "pokemon", fields: ["poke_type", "slot"] },
39
+ { doctype: "Pokemon Poke Ability", fieldname: "pokemon", fields: ["poke_ability", "is_hidden"] },
40
+ {
41
+ doctype: "Pokemon Poke Move",
42
+ fieldname: "pokemon",
43
+ fields: ["poke_move", "level_learned_at", "move_learn_method"],
44
+ },
45
+ ];
46
+
47
+ export const ConnectionsTab = () => (
48
+ <FrappeProvider baseURL={host} fixtures={fixtures()}>
49
+ <FrappeForm<RosterPokemon> doctype="Pokemon" name="bulbasaur" showSidebar={false} />
50
+ </FrappeProvider>
51
+ );
52
+
53
+ function PokemonDetail({
54
+ doc,
55
+ connections,
56
+ }: {
57
+ doc: Partial<RosterPokemon>;
58
+ connections: FrappeConnectionRows;
59
+ }) {
60
+ const { knobProps } = useResolvedKnobs();
61
+ const types = connections["Pokemon Poke Type"] ?? [];
62
+ const moves = connections["Pokemon Poke Move"] ?? [];
63
+ return (
64
+ <YStack {...knobProps.gap}>
65
+ <Text {...knobProps.body} fontWeight="700" textTransform="capitalize">
66
+ {doc.pokemon_name}
67
+ </Text>
68
+ <XStack {...knobProps.gap}>
69
+ {types.map((row) => (
70
+ <Chip key={String(row.name)}>{String(row.poke_type)}</Chip>
71
+ ))}
72
+ </XStack>
73
+ <Text {...knobProps.label} color="$color10">
74
+ {moves.map((row) => String(row.poke_move)).join(" · ")}
75
+ </Text>
76
+ </YStack>
77
+ );
78
+ }
79
+
80
+ export const DetailPage = () => (
81
+ <FrappeProvider baseURL={host} fixtures={fixtures()}>
82
+ <FrappeForm<RosterPokemon>
83
+ doctype="Pokemon"
84
+ name="pikachu"
85
+ connections={pokemonConnections}
86
+ showToolbar={false}
87
+ showSidebar={false}
88
+ showTimeline={false}
89
+ >
90
+ {({ doc, connections }) => <PokemonDetail doc={doc} connections={connections} />}
91
+ </FrappeForm>
92
+ </FrappeProvider>
93
+ );