@nocobase/client-v2 2.1.0-alpha.40 → 2.1.0-alpha.46

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 (338) hide show
  1. package/es/Application.d.ts +7 -0
  2. package/es/BaseApplication.d.ts +13 -0
  3. package/es/RouterManager.d.ts +1 -0
  4. package/es/authRedirect.d.ts +12 -0
  5. package/es/collection-field-interface/CollectionFieldInterface.d.ts +51 -15
  6. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +82 -3
  7. package/es/collection-manager/field-configure.d.ts +80 -0
  8. package/es/collection-manager/field-validation.d.ts +43 -0
  9. package/es/collection-manager/filter-operators/index.d.ts +46 -0
  10. package/es/collection-manager/filter-operators/operators.d.ts +30 -0
  11. package/es/collection-manager/interfaces/checkbox.d.ts +1 -41
  12. package/es/collection-manager/interfaces/checkboxGroup.d.ts +12 -44
  13. package/es/collection-manager/interfaces/collection.d.ts +12 -51
  14. package/es/collection-manager/interfaces/color.d.ts +1 -16
  15. package/es/collection-manager/interfaces/createdAt.d.ts +1 -44
  16. package/es/collection-manager/interfaces/createdBy.d.ts +0 -4
  17. package/es/collection-manager/interfaces/dateOnly.d.ts +7 -44
  18. package/es/collection-manager/interfaces/datetime.d.ts +1 -44
  19. package/es/collection-manager/interfaces/datetimeNoTz.d.ts +1 -44
  20. package/es/collection-manager/interfaces/email.d.ts +1 -29
  21. package/es/collection-manager/interfaces/id.d.ts +1 -16
  22. package/es/collection-manager/interfaces/index.d.ts +2 -3
  23. package/es/collection-manager/interfaces/input.d.ts +1 -102
  24. package/es/collection-manager/interfaces/integer.d.ts +1 -95
  25. package/es/collection-manager/interfaces/json.d.ts +16 -7
  26. package/es/collection-manager/interfaces/m2m.d.ts +11 -19
  27. package/es/collection-manager/interfaces/m2o.d.ts +11 -19
  28. package/es/collection-manager/interfaces/markdown.d.ts +1 -63
  29. package/es/collection-manager/interfaces/multipleSelect.d.ts +12 -44
  30. package/es/collection-manager/interfaces/nanoid.d.ts +1 -34
  31. package/es/collection-manager/interfaces/number.d.ts +1 -87
  32. package/es/collection-manager/interfaces/o2m.d.ts +12 -24
  33. package/es/collection-manager/interfaces/obo.d.ts +207 -0
  34. package/es/collection-manager/interfaces/oho.d.ts +207 -0
  35. package/es/collection-manager/interfaces/password.d.ts +1 -56
  36. package/es/collection-manager/interfaces/percent.d.ts +1 -84
  37. package/es/collection-manager/interfaces/phone.d.ts +1 -25
  38. package/es/collection-manager/interfaces/properties/index.d.ts +0 -28
  39. package/es/collection-manager/interfaces/radioGroup.d.ts +1 -29
  40. package/es/collection-manager/interfaces/richText.d.ts +1 -63
  41. package/es/collection-manager/interfaces/select.d.ts +12 -44
  42. package/es/collection-manager/interfaces/snowflake-id.d.ts +1 -34
  43. package/es/collection-manager/interfaces/tableoid.d.ts +1 -10
  44. package/es/collection-manager/interfaces/textarea.d.ts +1 -51
  45. package/es/collection-manager/interfaces/time.d.ts +1 -16
  46. package/es/collection-manager/interfaces/types.d.ts +3 -12
  47. package/es/collection-manager/interfaces/unixTimestamp.d.ts +1 -44
  48. package/es/collection-manager/interfaces/updatedAt.d.ts +1 -44
  49. package/es/collection-manager/interfaces/updatedBy.d.ts +0 -4
  50. package/es/collection-manager/interfaces/url.d.ts +1 -20
  51. package/es/collection-manager/interfaces/uuid.d.ts +1 -34
  52. package/es/collection-manager/template-fields.d.ts +53 -0
  53. package/es/components/KeepAlive.d.ts +22 -0
  54. package/es/components/RouterBridge.d.ts +9 -0
  55. package/es/components/form/DialogFormLayout.d.ts +5 -29
  56. package/es/components/form/TypedVariableInput.d.ts +75 -0
  57. package/es/components/form/VariableInput.d.ts +53 -2
  58. package/es/components/form/filter/CollectionFilter.d.ts +49 -0
  59. package/es/components/form/filter/CollectionFilterItem.d.ts +49 -0
  60. package/es/components/form/filter/DateFilterDynamicComponent.d.ts +57 -0
  61. package/es/components/form/filter/FilterValueInput.d.ts +29 -0
  62. package/es/components/form/filter/index.d.ts +11 -0
  63. package/es/components/form/filter/useFilterActionProps.d.ts +96 -0
  64. package/es/components/form/index.d.ts +2 -0
  65. package/es/data-source/ExtendCollectionsProvider.d.ts +50 -0
  66. package/es/data-source/index.d.ts +9 -0
  67. package/es/flow/FlowPage.d.ts +2 -1
  68. package/es/flow/admin-shell/AdminLayoutRouteCoordinator.d.ts +8 -40
  69. package/es/flow/admin-shell/BaseLayoutModel.d.ts +89 -0
  70. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +74 -0
  71. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +12 -0
  72. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
  73. package/es/flow/admin-shell/admin-layout/AdminLayoutModel.d.ts +7 -92
  74. package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +11 -0
  75. package/es/flow/admin-shell/admin-layout/index.d.ts +3 -0
  76. package/es/flow/admin-shell/admin-layout/useApplications.d.ts +3 -2
  77. package/es/flow/admin-shell/useAdminLayoutRoutePage.d.ts +2 -2
  78. package/es/flow/admin-shell/useLayoutRoutePage.d.ts +23 -0
  79. package/es/flow/components/ConditionBuilder.d.ts +1 -0
  80. package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
  81. package/es/flow/components/FieldAssignValueInput.d.ts +1 -0
  82. package/es/flow/components/FlowRoute.d.ts +10 -1
  83. package/es/flow/components/fieldAssignOptions.d.ts +10 -0
  84. package/es/flow/components/filter/LinkageFilterItem.d.ts +1 -0
  85. package/es/flow/components/filter/VariableFilterItem.d.ts +1 -0
  86. package/es/flow/components/filter/index.d.ts +2 -0
  87. package/es/flow/components/filter/metaTreeAssociationDepth.d.ts +13 -0
  88. package/es/flow/components/filter/useFilterOptions.d.ts +66 -0
  89. package/es/flow/index.d.ts +4 -0
  90. package/es/flow/models/base/AssociationFieldGroupModel.d.ts +2 -33
  91. package/es/flow/models/base/PageModel/PageModel.d.ts +3 -1
  92. package/es/flow/models/blocks/assign-form/assignFieldValuesFlow.d.ts +92 -0
  93. package/es/flow/models/blocks/assign-form/index.d.ts +1 -0
  94. package/es/flow/models/blocks/form/FormActionGroupModel.d.ts +1 -0
  95. package/es/flow/models/blocks/form/FormActionModel.d.ts +9 -2
  96. package/es/flow/models/blocks/table/TableBlockModel.d.ts +10 -0
  97. package/es/flow/models/blocks/table/TableColumnModel.d.ts +2 -0
  98. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.d.ts +1 -1
  99. package/es/flow-compat/passwordUtils.d.ts +1 -1
  100. package/es/index.d.ts +6 -0
  101. package/es/index.mjs +552 -459
  102. package/es/layout-manager/LayoutContentRoute.d.ts +14 -0
  103. package/es/layout-manager/LayoutManager.d.ts +22 -0
  104. package/es/layout-manager/LayoutRoute.d.ts +14 -0
  105. package/es/layout-manager/index.d.ts +13 -0
  106. package/es/layout-manager/types.d.ts +20 -0
  107. package/es/layout-manager/utils.d.ts +14 -0
  108. package/es/nocobase-buildin-plugin/index.d.ts +3 -10
  109. package/es/settings-center/index.d.ts +1 -1
  110. package/es/settings-center/plugin-manager/BulkEnableButton.d.ts +15 -0
  111. package/es/settings-center/plugin-manager/PluginCard.d.ts +15 -0
  112. package/es/settings-center/plugin-manager/PluginDetail.d.ts +16 -0
  113. package/es/settings-center/{PluginManagerPage.d.ts → plugin-manager/index.d.ts} +1 -7
  114. package/es/settings-center/plugin-manager/types.d.ts +34 -0
  115. package/lib/index.js +552 -459
  116. package/package.json +8 -7
  117. package/src/Application.tsx +51 -12
  118. package/src/BaseApplication.tsx +32 -0
  119. package/src/PluginSettingsManager.ts +1 -1
  120. package/src/RouterManager.tsx +17 -1
  121. package/src/__tests__/PluginSettingsManager.test.ts +41 -2
  122. package/src/__tests__/app.test.tsx +17 -1
  123. package/src/__tests__/globalDeps.test.ts +1 -0
  124. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +50 -2
  125. package/src/__tests__/plugin-manager.test.tsx +177 -0
  126. package/src/__tests__/settings-center.test.tsx +24 -2
  127. package/src/authRedirect.ts +43 -0
  128. package/src/collection-field-interface/CollectionFieldInterface.ts +71 -77
  129. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +201 -4
  130. package/src/collection-manager/field-configure.ts +548 -0
  131. package/src/collection-manager/field-validation.ts +195 -0
  132. package/src/collection-manager/filter-operators/index.ts +176 -0
  133. package/src/collection-manager/{interfaces/properties → filter-operators}/operators.ts +24 -13
  134. package/src/collection-manager/interfaces/checkbox.ts +2 -9
  135. package/src/collection-manager/interfaces/checkboxGroup.ts +2 -10
  136. package/src/collection-manager/interfaces/collection.ts +2 -15
  137. package/src/collection-manager/interfaces/color.ts +2 -2
  138. package/src/collection-manager/interfaces/createdAt.ts +2 -2
  139. package/src/collection-manager/interfaces/createdBy.ts +1 -12
  140. package/src/collection-manager/interfaces/dateOnly.ts +8 -2
  141. package/src/collection-manager/interfaces/datetime.ts +2 -2
  142. package/src/collection-manager/interfaces/datetimeNoTz.ts +2 -2
  143. package/src/collection-manager/interfaces/email.ts +2 -9
  144. package/src/collection-manager/interfaces/id.ts +1 -2
  145. package/src/collection-manager/interfaces/index.ts +2 -3
  146. package/src/collection-manager/interfaces/input.ts +2 -133
  147. package/src/collection-manager/interfaces/integer.ts +2 -71
  148. package/src/collection-manager/interfaces/json.tsx +17 -11
  149. package/src/collection-manager/interfaces/m2m.tsx +0 -21
  150. package/src/collection-manager/interfaces/m2o.tsx +0 -22
  151. package/src/collection-manager/interfaces/markdown.ts +2 -51
  152. package/src/collection-manager/interfaces/multipleSelect.ts +2 -14
  153. package/src/collection-manager/interfaces/nanoid.ts +2 -2
  154. package/src/collection-manager/interfaces/number.ts +2 -85
  155. package/src/collection-manager/interfaces/o2m.tsx +1 -22
  156. package/src/collection-manager/interfaces/obo.tsx +145 -0
  157. package/src/collection-manager/interfaces/oho.tsx +145 -0
  158. package/src/collection-manager/interfaces/password.ts +2 -44
  159. package/src/collection-manager/interfaces/percent.ts +2 -74
  160. package/src/collection-manager/interfaces/phone.ts +2 -2
  161. package/src/collection-manager/interfaces/properties/index.ts +0 -133
  162. package/src/collection-manager/interfaces/radioGroup.ts +2 -2
  163. package/src/collection-manager/interfaces/richText.ts +2 -51
  164. package/src/collection-manager/interfaces/select.ts +2 -14
  165. package/src/collection-manager/interfaces/snowflake-id.ts +2 -2
  166. package/src/collection-manager/interfaces/tableoid.ts +1 -2
  167. package/src/collection-manager/interfaces/textarea.ts +2 -51
  168. package/src/collection-manager/interfaces/time.ts +2 -2
  169. package/src/collection-manager/interfaces/types.ts +4 -12
  170. package/src/collection-manager/interfaces/unixTimestamp.tsx +2 -2
  171. package/src/collection-manager/interfaces/updatedAt.ts +2 -2
  172. package/src/collection-manager/interfaces/updatedBy.ts +1 -12
  173. package/src/collection-manager/interfaces/url.ts +2 -4
  174. package/src/collection-manager/interfaces/uuid.ts +2 -2
  175. package/src/collection-manager/template-fields.ts +109 -0
  176. package/src/components/KeepAlive.tsx +131 -0
  177. package/src/components/README.md +138 -6
  178. package/src/components/README.zh-CN.md +138 -7
  179. package/src/components/RouterBridge.tsx +28 -4
  180. package/src/components/__tests__/KeepAlive.test.tsx +63 -0
  181. package/src/components/__tests__/RouterBridge.test.tsx +27 -0
  182. package/src/components/form/DialogFormLayout.tsx +5 -29
  183. package/src/components/form/TypedVariableInput.tsx +441 -0
  184. package/src/components/form/VariableInput.tsx +101 -28
  185. package/src/components/form/__tests__/TypedVariableInput.test.tsx +152 -0
  186. package/src/components/form/__tests__/VariableInput.test.ts +85 -0
  187. package/src/components/form/filter/CollectionFilter.tsx +111 -0
  188. package/src/components/form/filter/CollectionFilterItem.tsx +184 -0
  189. package/src/components/form/filter/DateFilterDynamicComponent.tsx +283 -0
  190. package/src/components/form/filter/FilterValueInput.tsx +198 -0
  191. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +247 -0
  192. package/src/components/form/filter/__tests__/DateFilterDynamicComponent.test.tsx +148 -0
  193. package/src/components/form/filter/__tests__/FilterValueInput.test.tsx +243 -0
  194. package/src/components/form/filter/__tests__/compileFilterGroup.test.ts +146 -0
  195. package/src/components/form/filter/index.ts +13 -0
  196. package/src/components/form/filter/useFilterActionProps.ts +203 -0
  197. package/src/components/form/index.tsx +2 -0
  198. package/src/data-source/ExtendCollectionsProvider.tsx +144 -0
  199. package/src/data-source/__tests__/ExtendCollectionsProvider.test.tsx +264 -0
  200. package/src/data-source/index.ts +10 -0
  201. package/src/flow/FlowPage.tsx +35 -7
  202. package/src/flow/__tests__/FlowPage.test.tsx +79 -0
  203. package/src/flow/__tests__/FlowRoute.test.tsx +537 -3
  204. package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +100 -0
  205. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +191 -0
  206. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +23 -1
  207. package/src/flow/actions/__tests__/openView.subModelKey.test.tsx +33 -0
  208. package/src/flow/actions/aclCheck.tsx +4 -0
  209. package/src/flow/actions/aclCheckRefresh.tsx +4 -0
  210. package/src/flow/actions/dataScope.tsx +8 -1
  211. package/src/flow/actions/dateTimeFormat.tsx +12 -8
  212. package/src/flow/actions/linkageRules.tsx +127 -2
  213. package/src/flow/actions/linkageRulesRefresh.tsx +2 -3
  214. package/src/flow/actions/openView.tsx +28 -4
  215. package/src/flow/actions/setTargetDataScope.tsx +1 -1
  216. package/src/flow/admin-shell/AdminLayoutRouteCoordinator.ts +11 -329
  217. package/src/flow/admin-shell/BaseLayoutModel.tsx +455 -0
  218. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +502 -0
  219. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +547 -3
  220. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +35 -7
  221. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +160 -0
  222. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +15 -13
  223. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +28 -201
  224. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +11 -2
  225. package/src/flow/admin-shell/admin-layout/AppListRender.tsx +139 -0
  226. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +76 -27
  227. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +149 -27
  228. package/src/flow/admin-shell/admin-layout/index.ts +3 -0
  229. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +11 -0
  230. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +2 -4
  231. package/src/flow/admin-shell/admin-layout/useApplications.tsx +34 -1
  232. package/src/flow/admin-shell/useAdminLayoutRoutePage.ts +10 -26
  233. package/src/flow/admin-shell/useLayoutRoutePage.ts +61 -0
  234. package/src/flow/common/Markdown/Edit.tsx +32 -4
  235. package/src/flow/components/AdminLayout.tsx +4 -154
  236. package/src/flow/components/ConditionBuilder.tsx +12 -2
  237. package/src/flow/components/FieldAssignRulesEditor.tsx +37 -46
  238. package/src/flow/components/FieldAssignValueInput.tsx +8 -4
  239. package/src/flow/components/FlowRoute.tsx +105 -15
  240. package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +112 -1
  241. package/src/flow/components/__tests__/fieldAssignOptions.test.ts +71 -1
  242. package/src/flow/components/code-editor/__tests__/runjsCompletions.test.ts +272 -27
  243. package/src/flow/components/code-editor/runjsCompletions.ts +490 -9
  244. package/src/flow/components/fieldAssignOptions.ts +23 -6
  245. package/src/flow/components/filter/LinkageFilterItem.tsx +22 -9
  246. package/src/flow/components/filter/VariableFilterItem.tsx +27 -9
  247. package/src/flow/components/filter/__tests__/fieldsToOptions.test.ts +48 -0
  248. package/src/flow/components/filter/__tests__/metaTreeAssociationDepth.test.ts +60 -0
  249. package/src/flow/components/filter/fieldsToOptions.ts +9 -4
  250. package/src/flow/components/filter/index.ts +3 -0
  251. package/src/flow/components/filter/metaTreeAssociationDepth.ts +69 -0
  252. package/src/flow/components/filter/useFilterOptions.ts +102 -0
  253. package/src/flow/index.ts +4 -0
  254. package/src/flow/models/actions/FilterActionModel.tsx +15 -3
  255. package/src/flow/models/actions/UpdateRecordActionModel.tsx +14 -95
  256. package/src/flow/models/actions/UpdateRecordActionUtils.ts +4 -7
  257. package/src/flow/models/actions/__tests__/AssignFormRefill.test.ts +116 -1
  258. package/src/flow/models/base/ActionModel.tsx +8 -1
  259. package/src/flow/models/base/AssociationFieldGroupModel.tsx +20 -3
  260. package/src/flow/models/base/GridModel.tsx +1 -1
  261. package/src/flow/models/base/PageModel/ChildPageModel.tsx +5 -1
  262. package/src/flow/models/base/PageModel/PageModel.tsx +51 -18
  263. package/src/flow/models/base/PageModel/RootPageModel.tsx +6 -13
  264. package/src/flow/models/base/PageModel/__tests__/ChildPageModel.test.ts +12 -0
  265. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +102 -1
  266. package/src/flow/models/base/RouteModel.tsx +1 -1
  267. package/src/flow/models/base/__tests__/AssociationFieldGroupModel.test.ts +86 -0
  268. package/src/flow/models/base/__tests__/GridModel.render.test.tsx +53 -0
  269. package/src/flow/models/blocks/assign-form/AssignFormGridModel.tsx +12 -3
  270. package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +77 -6
  271. package/src/flow/models/blocks/assign-form/__tests__/assignFieldValuesFlow.editor.test.tsx +216 -0
  272. package/src/flow/models/blocks/assign-form/assignFieldValuesFlow.tsx +308 -0
  273. package/src/flow/models/blocks/assign-form/index.ts +1 -0
  274. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +1 -1
  275. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +6 -1
  276. package/src/flow/models/blocks/filter-manager/flow-actions/connectFields.tsx +48 -6
  277. package/src/flow/models/blocks/form/FormActionGroupModel.tsx +14 -0
  278. package/src/flow/models/blocks/form/FormActionModel.tsx +30 -3
  279. package/src/flow/models/blocks/form/FormAssociationItemModel.tsx +9 -0
  280. package/src/flow/models/blocks/form/FormItemModel.tsx +17 -1
  281. package/src/flow/models/blocks/form/__tests__/FormActionGroupModel.test.ts +46 -0
  282. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
  283. package/src/flow/models/blocks/form/__tests__/FormItemModel.defineChildren.test.ts +27 -2
  284. package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +71 -0
  285. package/src/flow/models/blocks/form/submitHandler.ts +8 -1
  286. package/src/flow/models/blocks/form/submitValues.ts +4 -1
  287. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +29 -0
  288. package/src/flow/models/blocks/form/value-runtime/runtime.ts +5 -1
  289. package/src/flow/models/blocks/table/TableBlockModel.tsx +118 -16
  290. package/src/flow/models/blocks/table/TableColumnModel.tsx +15 -5
  291. package/src/flow/models/blocks/table/TableCustomColumnModel.tsx +2 -2
  292. package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +114 -0
  293. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +18 -0
  294. package/src/flow/models/fields/AssociationFieldModel/SubFormFieldModel.tsx +7 -1
  295. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +6 -7
  296. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +1 -1
  297. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -5
  298. package/src/flow/models/fields/ClickableFieldModel.tsx +9 -1
  299. package/src/flow/models/fields/CollectionSelectorFieldModel.tsx +8 -2
  300. package/src/flow/models/fields/DisplayEnumFieldModel.tsx +8 -2
  301. package/src/flow/models/fields/DisplayTimeFieldModel.tsx +1 -1
  302. package/src/flow/models/fields/TimeFieldModel.tsx +1 -1
  303. package/src/flow/models/fields/__tests__/TimeFieldModel.test.tsx +61 -0
  304. package/src/flow/models/fields/mobile-components/MobileDatePicker.tsx +19 -3
  305. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +5 -3
  306. package/src/flow/models/fields/mobile-components/__tests__/MobileDatePicker.test.tsx +94 -0
  307. package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +70 -0
  308. package/src/flow/models/topbar/TopbarActionModel.tsx +1 -1
  309. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +91 -0
  310. package/src/index.ts +6 -0
  311. package/src/layout-manager/LayoutContentRoute.tsx +90 -0
  312. package/src/layout-manager/LayoutManager.tsx +185 -0
  313. package/src/layout-manager/LayoutRoute.tsx +138 -0
  314. package/src/layout-manager/__tests__/LayoutManager.test.tsx +335 -0
  315. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +473 -0
  316. package/src/layout-manager/index.ts +14 -0
  317. package/src/layout-manager/types.ts +22 -0
  318. package/src/layout-manager/utils.ts +37 -0
  319. package/src/nocobase-buildin-plugin/index.tsx +69 -67
  320. package/src/nocobase-buildin-plugin/plugins/LocalePlugin.ts +1 -0
  321. package/src/settings-center/index.ts +1 -1
  322. package/src/settings-center/plugin-manager/BulkEnableButton.tsx +111 -0
  323. package/src/settings-center/plugin-manager/PluginCard.tsx +270 -0
  324. package/src/settings-center/plugin-manager/PluginDetail.tsx +195 -0
  325. package/src/settings-center/plugin-manager/index.tsx +254 -0
  326. package/src/settings-center/plugin-manager/types.ts +35 -0
  327. package/src/settings-center/utils.tsx +8 -1
  328. package/src/theme/__tests__/globalStyles.test.ts +24 -0
  329. package/src/theme/globalStyles.ts +10 -0
  330. package/src/utils/globalDeps.ts +2 -0
  331. package/es/collection-manager/interfaces/linkTo.d.ts +0 -90
  332. package/es/collection-manager/interfaces/o2o.d.ts +0 -621
  333. package/es/collection-manager/interfaces/properties/operators.d.ts +0 -294
  334. package/es/collection-manager/interfaces/subTable.d.ts +0 -172
  335. package/src/collection-manager/interfaces/linkTo.ts +0 -120
  336. package/src/collection-manager/interfaces/o2o.tsx +0 -561
  337. package/src/collection-manager/interfaces/subTable.ts +0 -218
  338. package/src/settings-center/PluginManagerPage.tsx +0 -162
@@ -8,10 +8,10 @@
8
8
  */
9
9
 
10
10
  import React from 'react';
11
- import { describe, it, expect, vi, beforeEach } from 'vitest';
11
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
12
12
  import { MemoryRouter, Route, Routes } from 'react-router-dom';
13
13
  import { render, screen, waitFor } from '@testing-library/react';
14
- import { FlowEngine, FlowEngineProvider, type FlowModel } from '@nocobase/flow-engine';
14
+ import { FlowContextProvider, FlowEngine, FlowEngineProvider, type FlowModel } from '@nocobase/flow-engine';
15
15
  import FlowRoute from '../components/FlowRoute';
16
16
 
17
17
  type MockAdminLayoutModel = FlowModel & {
@@ -32,6 +32,13 @@ describe('FlowRoute', () => {
32
32
  beforeEach(() => {
33
33
  vi.clearAllMocks();
34
34
  hookState.refresh = vi.fn();
35
+ // Fixtures mount the modern client under the `v2` segment; tell the
36
+ // runtime-prefix helper so v2-runtime detection matches.
37
+ (globalThis.window as any).__nocobase_modern_client_prefix__ = 'v2';
38
+ });
39
+
40
+ afterEach(() => {
41
+ delete (globalThis.window as any).__nocobase_modern_client_prefix__;
35
42
  });
36
43
 
37
44
  it('should bridge page lifecycle to admin-layout-model', async () => {
@@ -86,7 +93,7 @@ describe('FlowRoute', () => {
86
93
  expect(adminLayoutModel.registerRoutePage).toHaveBeenCalledWith(
87
94
  'test-page',
88
95
  expect.objectContaining({
89
- active: false,
96
+ active: true,
90
97
  refreshDesktopRoutes: expect.any(Function),
91
98
  layoutContentElement: expect.any(HTMLDivElement),
92
99
  }),
@@ -120,6 +127,200 @@ describe('FlowRoute', () => {
120
127
  expect(adminLayoutModel.unregisterRoutePage).toHaveBeenCalledWith('test-page');
121
128
  });
122
129
 
130
+ it('should sync explicit active state to layout route page', async () => {
131
+ const engine = new FlowEngine();
132
+ engine.context.defineProperty('routeRepository', {
133
+ value: {
134
+ refreshAccessible: hookState.refresh,
135
+ isAccessibleLoaded: () => true,
136
+ ensureAccessibleLoaded: vi.fn().mockResolvedValue([]),
137
+ getRouteBySchemaUid: vi.fn(() => ({ type: 'flowPage', schemaUid: 'test-page' })),
138
+ },
139
+ });
140
+ engine.context.defineProperty('app', {
141
+ value: {
142
+ getPublicPath: () => '/v2/',
143
+ router: {
144
+ getBasename: () => '/v2',
145
+ },
146
+ },
147
+ });
148
+
149
+ const adminLayoutModel: MockAdminLayoutModel = Object.assign(
150
+ engine.createModel({
151
+ uid: 'admin-layout-model',
152
+ use: 'FlowModel',
153
+ }),
154
+ {
155
+ registerRoutePage: vi.fn(),
156
+ updateRoutePage: vi.fn(),
157
+ unregisterRoutePage: vi.fn(),
158
+ },
159
+ );
160
+
161
+ const result = render(
162
+ <FlowEngineProvider engine={engine}>
163
+ <MemoryRouter initialEntries={['/flow/test-page']}>
164
+ <Routes>
165
+ <Route path="/flow/:name" element={<FlowRoute active={false} />} />
166
+ </Routes>
167
+ </MemoryRouter>
168
+ </FlowEngineProvider>,
169
+ );
170
+
171
+ await waitFor(() => {
172
+ expect(adminLayoutModel.registerRoutePage).toHaveBeenCalledWith(
173
+ 'test-page',
174
+ expect.objectContaining({
175
+ active: false,
176
+ }),
177
+ );
178
+ });
179
+
180
+ result.rerender(
181
+ <FlowEngineProvider engine={engine}>
182
+ <MemoryRouter initialEntries={['/flow/test-page']}>
183
+ <Routes>
184
+ <Route path="/flow/:name" element={<FlowRoute active={true} />} />
185
+ </Routes>
186
+ </MemoryRouter>
187
+ </FlowEngineProvider>,
188
+ );
189
+
190
+ await waitFor(() => {
191
+ expect(adminLayoutModel.updateRoutePage).toHaveBeenCalledWith(
192
+ 'test-page',
193
+ expect.objectContaining({
194
+ active: true,
195
+ }),
196
+ );
197
+ });
198
+ });
199
+
200
+ it('should bridge page lifecycle with explicit pageUid', async () => {
201
+ const engine = new FlowEngine();
202
+ engine.context.defineProperty('routeRepository', {
203
+ value: {
204
+ refreshAccessible: hookState.refresh,
205
+ isAccessibleLoaded: () => true,
206
+ ensureAccessibleLoaded: vi.fn().mockResolvedValue([]),
207
+ getRouteBySchemaUid: vi.fn(() => ({ type: 'flowPage', schemaUid: 'test-page' })),
208
+ },
209
+ });
210
+ engine.context.defineProperty('app', {
211
+ value: {
212
+ getPublicPath: () => '/v2/',
213
+ router: {
214
+ getBasename: () => '/v2',
215
+ },
216
+ },
217
+ });
218
+
219
+ const adminLayoutModel: MockAdminLayoutModel = Object.assign(
220
+ engine.createModel({
221
+ uid: 'admin-layout-model',
222
+ use: 'FlowModel',
223
+ }),
224
+ {
225
+ registerRoutePage: vi.fn(),
226
+ updateRoutePage: vi.fn(),
227
+ unregisterRoutePage: vi.fn(),
228
+ },
229
+ );
230
+
231
+ render(
232
+ <FlowEngineProvider engine={engine}>
233
+ <MemoryRouter initialEntries={['/flow']}>
234
+ <Routes>
235
+ <Route path="/flow" element={<FlowRoute pageUid="test-page" />} />
236
+ </Routes>
237
+ </MemoryRouter>
238
+ </FlowEngineProvider>,
239
+ );
240
+
241
+ await waitFor(() => {
242
+ expect(adminLayoutModel.registerRoutePage).toHaveBeenCalledWith('test-page', expect.any(Object));
243
+ });
244
+ });
245
+
246
+ it('should derive layout model from current layout context when rendered from schema', async () => {
247
+ const engine = new FlowEngine();
248
+ const routeRepository = {
249
+ refreshAccessible: hookState.refresh,
250
+ isAccessibleLoaded: () => true,
251
+ ensureAccessibleLoaded: vi.fn().mockResolvedValue([]),
252
+ getRouteBySchemaUid: vi.fn(() => ({ type: 'flowPage', schemaUid: 'test-page' })),
253
+ };
254
+ engine.context.defineProperty('route', {
255
+ value: {
256
+ params: { name: 'test-page' },
257
+ pathname: '/embed/test-page',
258
+ },
259
+ });
260
+ engine.context.defineProperty('routeRepository', {
261
+ value: routeRepository,
262
+ });
263
+ const routeModel = engine.createModel({
264
+ uid: 'route-model',
265
+ use: 'FlowModel',
266
+ });
267
+ routeModel.context.defineProperty('layout', {
268
+ value: {
269
+ routeName: 'embed',
270
+ routePath: '/embed',
271
+ rootRouteName: 'embed',
272
+ uid: 'embed-layout-model',
273
+ layoutModelClass: 'EmbedLayoutModelV2',
274
+ rootPageModelClass: 'RootPageModel',
275
+ childPageModelClass: 'ChildPageModel',
276
+ authCheck: true,
277
+ },
278
+ });
279
+ engine.context.defineProperty('app', {
280
+ value: {
281
+ getPublicPath: () => '/v2/',
282
+ router: {
283
+ getBasename: () => '/v2',
284
+ },
285
+ },
286
+ });
287
+
288
+ const embedLayoutModel: MockAdminLayoutModel = Object.assign(
289
+ engine.createModel({
290
+ uid: 'embed-layout-model',
291
+ use: 'FlowModel',
292
+ }),
293
+ {
294
+ registerRoutePage: vi.fn(),
295
+ updateRoutePage: vi.fn(),
296
+ unregisterRoutePage: vi.fn(),
297
+ },
298
+ );
299
+
300
+ render(
301
+ <FlowEngineProvider engine={engine}>
302
+ <FlowContextProvider context={routeModel.context}>
303
+ <MemoryRouter initialEntries={['/embed/test-page']}>
304
+ <Routes>
305
+ <Route path="/embed/:name" element={<FlowRoute />} />
306
+ </Routes>
307
+ </MemoryRouter>
308
+ </FlowContextProvider>
309
+ </FlowEngineProvider>,
310
+ );
311
+
312
+ await waitFor(() => {
313
+ expect(embedLayoutModel.registerRoutePage).toHaveBeenCalledWith(
314
+ 'test-page',
315
+ expect.objectContaining({
316
+ active: true,
317
+ refreshDesktopRoutes: expect.any(Function),
318
+ layoutContentElement: expect.any(HTMLDivElement),
319
+ }),
320
+ );
321
+ });
322
+ });
323
+
123
324
  it('should fail fast when admin-layout-model is missing', () => {
124
325
  const engine = new FlowEngine();
125
326
  engine.context.defineProperty('route', {
@@ -283,6 +484,339 @@ describe('FlowRoute', () => {
283
484
  }
284
485
  });
285
486
 
487
+ it('should render not found for legacy page when behavior is notFound', async () => {
488
+ const originalLocation = window.location;
489
+ const replace = vi.fn();
490
+ Object.defineProperty(window, 'location', {
491
+ configurable: true,
492
+ value: {
493
+ ...originalLocation,
494
+ pathname: '/v2/embed/test-page',
495
+ replace,
496
+ },
497
+ });
498
+
499
+ try {
500
+ const engine = new FlowEngine();
501
+ engine.context.defineProperty('routeRepository', {
502
+ value: {
503
+ refreshAccessible: hookState.refresh,
504
+ isAccessibleLoaded: () => true,
505
+ ensureAccessibleLoaded: vi.fn().mockResolvedValue([]),
506
+ getRouteBySchemaUid: vi.fn(() => ({ type: 'page', schemaUid: 'test-page' })),
507
+ },
508
+ });
509
+ engine.context.defineProperty('app', {
510
+ value: {
511
+ getPublicPath: () => '/v2/',
512
+ router: {
513
+ getBasename: () => '/v2',
514
+ },
515
+ },
516
+ });
517
+
518
+ const adminLayoutModel: MockAdminLayoutModel = Object.assign(
519
+ engine.createModel({ uid: 'admin-layout-model', use: 'FlowModel' }),
520
+ {
521
+ registerRoutePage: vi.fn(),
522
+ updateRoutePage: vi.fn(),
523
+ unregisterRoutePage: vi.fn(),
524
+ },
525
+ );
526
+
527
+ const result = render(
528
+ <FlowEngineProvider engine={engine}>
529
+ <MemoryRouter initialEntries={['/embed/test-page']}>
530
+ <Routes>
531
+ <Route path="/embed/:name" element={<FlowRoute legacyPageBehavior="notFound" />} />
532
+ </Routes>
533
+ </MemoryRouter>
534
+ </FlowEngineProvider>,
535
+ );
536
+
537
+ await result.findByText('404');
538
+ expect(replace).not.toHaveBeenCalled();
539
+ expect(adminLayoutModel.registerRoutePage).not.toHaveBeenCalled();
540
+ } finally {
541
+ Object.defineProperty(window, 'location', {
542
+ configurable: true,
543
+ value: originalLocation,
544
+ });
545
+ }
546
+ });
547
+
548
+ it('should bridge existing FlowModel when behavior is notFound and routeRepository has no route', async () => {
549
+ const engine = new FlowEngine();
550
+ engine.setModelRepository({
551
+ findOne: vi.fn().mockResolvedValue({
552
+ uid: 'public-form-1',
553
+ use: 'FlowModel',
554
+ }),
555
+ save: vi.fn(),
556
+ destroy: vi.fn(),
557
+ } as any);
558
+ engine.context.defineProperty('routeRepository', {
559
+ value: {
560
+ refreshAccessible: hookState.refresh,
561
+ isAccessibleLoaded: () => true,
562
+ ensureAccessibleLoaded: vi.fn().mockResolvedValue([]),
563
+ getRouteBySchemaUid: vi.fn(() => undefined),
564
+ },
565
+ });
566
+ engine.context.defineProperty('app', {
567
+ value: {
568
+ getPublicPath: () => '/v2/',
569
+ router: {
570
+ getBasename: () => '/v2',
571
+ },
572
+ },
573
+ });
574
+
575
+ const layoutModel: MockAdminLayoutModel = Object.assign(
576
+ engine.createModel({ uid: 'public-form-layout-model', use: 'FlowModel' }),
577
+ {
578
+ registerRoutePage: vi.fn(),
579
+ updateRoutePage: vi.fn(),
580
+ unregisterRoutePage: vi.fn(),
581
+ },
582
+ );
583
+
584
+ render(
585
+ <FlowEngineProvider engine={engine}>
586
+ <MemoryRouter initialEntries={['/public-forms/public-form-1']}>
587
+ <Routes>
588
+ <Route
589
+ path="/public-forms/:name"
590
+ element={<FlowRoute legacyPageBehavior="notFound" getLayoutModel={() => layoutModel} />}
591
+ />
592
+ </Routes>
593
+ </MemoryRouter>
594
+ </FlowEngineProvider>,
595
+ );
596
+
597
+ await waitFor(() => {
598
+ expect(layoutModel.registerRoutePage).toHaveBeenCalledWith('public-form-1', expect.any(Object));
599
+ });
600
+ expect(screen.queryByText('404')).not.toBeInTheDocument();
601
+ });
602
+
603
+ it('should check model existence without occupying the route model uid', async () => {
604
+ const engine = new FlowEngine();
605
+ const findOne = vi.fn().mockResolvedValue({
606
+ uid: 'public-form-1',
607
+ });
608
+ const request = vi.fn().mockResolvedValue({
609
+ data: {
610
+ data: {
611
+ uid: 'public-form-1',
612
+ },
613
+ },
614
+ });
615
+ engine.setModelRepository({
616
+ findOne,
617
+ save: vi.fn(),
618
+ destroy: vi.fn(),
619
+ } as any);
620
+ engine.context.defineProperty('routeRepository', {
621
+ value: {
622
+ refreshAccessible: hookState.refresh,
623
+ isAccessibleLoaded: () => true,
624
+ ensureAccessibleLoaded: vi.fn().mockResolvedValue([]),
625
+ getRouteBySchemaUid: vi.fn(() => undefined),
626
+ },
627
+ });
628
+ engine.context.defineProperty('app', {
629
+ value: {
630
+ apiClient: {
631
+ request,
632
+ },
633
+ getPublicPath: () => '/v2/',
634
+ router: {
635
+ getBasename: () => '/v2',
636
+ },
637
+ },
638
+ });
639
+ const routeModel = engine.createModel({
640
+ uid: 'public-form-route-model',
641
+ use: 'FlowModel',
642
+ });
643
+ routeModel.context.defineProperty('layout', {
644
+ value: {
645
+ routeName: 'public-forms',
646
+ routePath: '/public-forms',
647
+ rootRouteName: 'public-forms',
648
+ uid: 'public-form-layout-model',
649
+ layoutModelClass: 'PublicFormLayoutModel',
650
+ rootPageModelClass: 'PublicFormPageModel',
651
+ childPageModelClass: 'ChildPageModel',
652
+ authCheck: false,
653
+ },
654
+ });
655
+
656
+ const layoutModel: MockAdminLayoutModel = Object.assign(
657
+ engine.createModel({ uid: 'public-form-layout-model', use: 'FlowModel' }),
658
+ {
659
+ registerRoutePage: vi.fn(),
660
+ updateRoutePage: vi.fn(),
661
+ unregisterRoutePage: vi.fn(),
662
+ },
663
+ );
664
+
665
+ render(
666
+ <FlowEngineProvider engine={engine}>
667
+ <FlowContextProvider context={routeModel.context}>
668
+ <MemoryRouter initialEntries={['/public-forms/public-form-1']}>
669
+ <Routes>
670
+ <Route path="/public-forms/:name" element={<FlowRoute legacyPageBehavior="notFound" />} />
671
+ </Routes>
672
+ </MemoryRouter>
673
+ </FlowContextProvider>
674
+ </FlowEngineProvider>,
675
+ );
676
+
677
+ await waitFor(() => {
678
+ expect(layoutModel.registerRoutePage).toHaveBeenCalledWith('public-form-1', expect.any(Object));
679
+ });
680
+ expect(findOne).toHaveBeenCalledWith({ uid: 'public-form-1' });
681
+ expect(request).not.toHaveBeenCalled();
682
+ expect(engine.getModel('public-form-1')).toBeUndefined();
683
+ });
684
+
685
+ it('should not skip accessible route loading just because layout authCheck is false', async () => {
686
+ const engine = new FlowEngine();
687
+ const ensureAccessibleLoaded = vi.fn().mockRejectedValue(new Error('cannot load accessible routes'));
688
+ const getRouteBySchemaUid = vi.fn();
689
+ engine.setModelRepository({
690
+ findOne: vi.fn().mockResolvedValue({
691
+ uid: 'public-form-1',
692
+ use: 'FlowModel',
693
+ }),
694
+ save: vi.fn(),
695
+ destroy: vi.fn(),
696
+ } as any);
697
+ engine.context.defineProperty('routeRepository', {
698
+ value: {
699
+ refreshAccessible: hookState.refresh,
700
+ isAccessibleLoaded: () => false,
701
+ ensureAccessibleLoaded,
702
+ getRouteBySchemaUid,
703
+ },
704
+ });
705
+ engine.context.defineProperty('app', {
706
+ value: {
707
+ getPublicPath: () => '/v2/',
708
+ router: {
709
+ getBasename: () => '/v2',
710
+ },
711
+ },
712
+ });
713
+ const routeModel = engine.createModel({
714
+ uid: 'public-form-route-model',
715
+ use: 'FlowModel',
716
+ });
717
+ routeModel.context.defineProperty('layout', {
718
+ value: {
719
+ routeName: 'public-forms',
720
+ routePath: '/public-forms',
721
+ rootRouteName: 'public-forms',
722
+ uid: 'public-form-layout-model',
723
+ layoutModelClass: 'PublicFormLayoutModel',
724
+ rootPageModelClass: 'PublicFormPageModel',
725
+ childPageModelClass: 'ChildPageModel',
726
+ authCheck: false,
727
+ },
728
+ });
729
+
730
+ const layoutModel: MockAdminLayoutModel = Object.assign(
731
+ engine.createModel({ uid: 'public-form-layout-model', use: 'FlowModel' }),
732
+ {
733
+ registerRoutePage: vi.fn(),
734
+ updateRoutePage: vi.fn(),
735
+ unregisterRoutePage: vi.fn(),
736
+ },
737
+ );
738
+
739
+ render(
740
+ <FlowEngineProvider engine={engine}>
741
+ <FlowContextProvider context={routeModel.context}>
742
+ <MemoryRouter initialEntries={['/public-forms/public-form-1']}>
743
+ <Routes>
744
+ <Route path="/public-forms/:name" element={<FlowRoute legacyPageBehavior="notFound" />} />
745
+ </Routes>
746
+ </MemoryRouter>
747
+ </FlowContextProvider>
748
+ </FlowEngineProvider>,
749
+ );
750
+
751
+ await waitFor(() => {
752
+ expect(layoutModel.registerRoutePage).toHaveBeenCalledWith('public-form-1', expect.any(Object));
753
+ });
754
+ expect(ensureAccessibleLoaded).toHaveBeenCalledTimes(1);
755
+ expect(getRouteBySchemaUid).not.toHaveBeenCalled();
756
+ });
757
+
758
+ it('should bridge legacy page when behavior is bridge', async () => {
759
+ const originalLocation = window.location;
760
+ const replace = vi.fn();
761
+ Object.defineProperty(window, 'location', {
762
+ configurable: true,
763
+ value: {
764
+ ...originalLocation,
765
+ pathname: '/v2/admin/test-page',
766
+ replace,
767
+ },
768
+ });
769
+
770
+ try {
771
+ const engine = new FlowEngine();
772
+ engine.context.defineProperty('routeRepository', {
773
+ value: {
774
+ refreshAccessible: hookState.refresh,
775
+ isAccessibleLoaded: () => true,
776
+ ensureAccessibleLoaded: vi.fn().mockResolvedValue([]),
777
+ getRouteBySchemaUid: vi.fn(() => ({ type: 'page', schemaUid: 'test-page' })),
778
+ },
779
+ });
780
+ engine.context.defineProperty('app', {
781
+ value: {
782
+ getPublicPath: () => '/v2/',
783
+ router: {
784
+ getBasename: () => '/v2',
785
+ },
786
+ },
787
+ });
788
+
789
+ const adminLayoutModel: MockAdminLayoutModel = Object.assign(
790
+ engine.createModel({ uid: 'admin-layout-model', use: 'FlowModel' }),
791
+ {
792
+ registerRoutePage: vi.fn(),
793
+ updateRoutePage: vi.fn(),
794
+ unregisterRoutePage: vi.fn(),
795
+ },
796
+ );
797
+
798
+ render(
799
+ <FlowEngineProvider engine={engine}>
800
+ <MemoryRouter initialEntries={['/flow/test-page']}>
801
+ <Routes>
802
+ <Route path="/flow/:name" element={<FlowRoute legacyPageBehavior="bridge" />} />
803
+ </Routes>
804
+ </MemoryRouter>
805
+ </FlowEngineProvider>,
806
+ );
807
+
808
+ await waitFor(() => {
809
+ expect(adminLayoutModel.registerRoutePage).toHaveBeenCalled();
810
+ });
811
+ expect(replace).not.toHaveBeenCalled();
812
+ } finally {
813
+ Object.defineProperty(window, 'location', {
814
+ configurable: true,
815
+ value: originalLocation,
816
+ });
817
+ }
818
+ });
819
+
286
820
  it('should not redirect when route does not exist', async () => {
287
821
  const originalLocation = window.location;
288
822
  const replace = vi.fn();
@@ -0,0 +1,100 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ import { describe, expect, it, vi } from 'vitest';
11
+ import { actionLinkageRules } from '../linkageRules';
12
+
13
+ function createRule(actions: any[]) {
14
+ return {
15
+ key: 'r1',
16
+ title: 'r1',
17
+ enable: true,
18
+ condition: { logic: '$and', items: [] },
19
+ actions,
20
+ };
21
+ }
22
+
23
+ describe('linkageRules hidden state propagation', () => {
24
+ it('restores hidden state to same-target models when reverting to original props', async () => {
25
+ const blockModel = { uid: 'block-1' };
26
+
27
+ const hostModel: any = {
28
+ uid: 'host',
29
+ hidden: false,
30
+ context: { blockModel },
31
+ __allModels: [],
32
+ setProps: vi.fn(),
33
+ getFlow: vi.fn(() => ({})),
34
+ getStepParams: vi.fn((_flowKey: string, stepKey: string) => {
35
+ if (stepKey === 'init') return { fieldPath: 'org_m2o' };
36
+ return undefined;
37
+ }),
38
+ translate: (s: string) => s,
39
+ };
40
+
41
+ const sameTargetModel: any = {
42
+ uid: 'same-target',
43
+ hidden: false,
44
+ context: { blockModel },
45
+ setProps: vi.fn(),
46
+ getStepParams: vi.fn((_flowKey: string, stepKey: string) => {
47
+ if (stepKey === 'init') return { fieldPath: 'org_m2o' };
48
+ return undefined;
49
+ }),
50
+ translate: (s: string) => s,
51
+ };
52
+
53
+ const engine = {
54
+ forEachModel: (visitor: (m: any) => void) => {
55
+ [hostModel, sameTargetModel].forEach(visitor);
56
+ },
57
+ };
58
+
59
+ const ctx: any = {
60
+ flowKey: 'buttonSettings',
61
+ model: hostModel,
62
+ engine,
63
+ app: {
64
+ jsonLogic: {
65
+ apply: vi.fn(() => true),
66
+ },
67
+ },
68
+ resolveJsonTemplate: vi.fn(async (value: any) => value),
69
+ getAction: (name: string) => {
70
+ if (name !== 'linkageSetActionProps') return undefined;
71
+ return {
72
+ handler: async (_ctx: any, params: any) => {
73
+ params.setProps(hostModel, {
74
+ hiddenModel: params.value === 'hidden',
75
+ disabled: false,
76
+ hiddenText: false,
77
+ });
78
+ },
79
+ };
80
+ },
81
+ };
82
+
83
+ await actionLinkageRules.handler(ctx, {
84
+ value: [
85
+ createRule([
86
+ {
87
+ name: 'linkageSetActionProps',
88
+ params: { value: 'hidden' },
89
+ },
90
+ ]),
91
+ ],
92
+ });
93
+ expect(hostModel.hidden).toBe(true);
94
+ expect(sameTargetModel.hidden).toBe(true);
95
+
96
+ await actionLinkageRules.handler(ctx, { value: [] });
97
+ expect(hostModel.hidden).toBe(false);
98
+ expect(sameTargetModel.hidden).toBe(false);
99
+ });
100
+ });