@nocobase/client-v2 2.2.0-alpha.1 → 2.2.0-alpha.11

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 (334) hide show
  1. package/es/APIClient.d.ts +1 -3
  2. package/es/BaseApplication.d.ts +3 -0
  3. package/es/PluginSettingsManager.d.ts +33 -0
  4. package/es/RouteRepository.d.ts +29 -6
  5. package/es/RouterManager.d.ts +15 -0
  6. package/es/authRedirect.d.ts +1 -0
  7. package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
  8. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
  9. package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
  10. package/es/components/category-tabs/index.d.ts +9 -0
  11. package/es/components/form/JsonTextArea.d.ts +2 -1
  12. package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
  13. package/es/components/form/TypedVariableInput.d.ts +31 -5
  14. package/es/components/form/VariableJsonTextArea.d.ts +19 -0
  15. package/es/components/form/filter/CollectionFilter.d.ts +2 -0
  16. package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
  17. package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
  18. package/es/components/form/filter/index.d.ts +2 -0
  19. package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
  20. package/es/components/form/index.d.ts +1 -0
  21. package/es/components/index.d.ts +2 -0
  22. package/es/entry-actions/EntryActionManager.d.ts +24 -0
  23. package/es/entry-actions/index.d.ts +9 -0
  24. package/es/flow/actions/afterSuccess.d.ts +8 -1
  25. package/es/flow/actions/index.d.ts +2 -2
  26. package/es/flow/actions/linkageRules.d.ts +2 -0
  27. package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
  28. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -0
  29. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
  30. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
  31. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +3 -0
  32. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
  33. package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +5 -0
  34. package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
  35. package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
  36. package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
  37. package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
  38. package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
  39. package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
  40. package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
  41. package/es/flow/components/FieldAssignValueInput.d.ts +9 -0
  42. package/es/flow/components/FlowRoute.d.ts +2 -2
  43. package/es/flow/components/RunJSValueEditor.d.ts +1 -0
  44. package/es/flow/components/filter/FilterGroup.d.ts +1 -0
  45. package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
  46. package/es/flow/index.d.ts +1 -0
  47. package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
  48. package/es/flow/models/base/ActionModelCore.d.ts +2 -0
  49. package/es/flow/models/base/BlockGridModel.d.ts +3 -0
  50. package/es/flow/models/base/GridModel.d.ts +1 -1
  51. package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
  52. package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
  53. package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
  54. package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
  55. package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
  56. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
  57. package/es/flow/models/blocks/form/submitHandler.d.ts +1 -1
  58. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
  59. package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +30 -0
  60. package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
  61. package/es/flow/models/blocks/js-block/JSBlock.d.ts +1 -0
  62. package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +1 -0
  63. package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
  64. package/es/flow/models/blocks/table/TableColumnModel.d.ts +1 -0
  65. package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +1 -1
  66. package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +2 -1
  67. package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +6 -4
  68. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +2 -0
  69. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +6 -0
  70. package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
  71. package/es/flow/resolveViewParamsToViewList.d.ts +1 -1
  72. package/es/flow/routeTransientInputArgs.d.ts +14 -0
  73. package/es/flow/utils/dataScopeRowSnapshot.d.ts +59 -0
  74. package/es/flow/utils/dateTimeDisplayProps.d.ts +49 -0
  75. package/es/flow/utils/formValueDeps.d.ts +32 -0
  76. package/es/flow-compat/FieldValidation.d.ts +2 -1
  77. package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
  78. package/es/flow-compat/routeTypes.d.ts +1 -0
  79. package/es/index.d.ts +8 -1
  80. package/es/index.mjs +367 -181
  81. package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
  82. package/es/utils/index.d.ts +1 -0
  83. package/es/utils/markdownSanitize.d.ts +11 -0
  84. package/lib/index.js +367 -181
  85. package/lib/locale/languageCodes.js +3 -1
  86. package/package.json +9 -7
  87. package/src/APIClient.ts +1 -10
  88. package/src/Application.tsx +4 -0
  89. package/src/BaseApplication.tsx +13 -6
  90. package/src/PluginSettingsManager.ts +53 -0
  91. package/src/RouteRepository.ts +151 -24
  92. package/src/RouterManager.tsx +142 -1
  93. package/src/__tests__/PluginSettingsManager.test.ts +13 -0
  94. package/src/__tests__/RouteRepository.test.ts +239 -0
  95. package/src/__tests__/RouterManager.test.ts +125 -0
  96. package/src/__tests__/app.test.tsx +73 -0
  97. package/src/__tests__/authRedirect.test.ts +17 -0
  98. package/src/__tests__/browserChecker.test.ts +61 -0
  99. package/src/__tests__/exports.test.ts +16 -0
  100. package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
  101. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +234 -2
  102. package/src/__tests__/plugin-manager.test.tsx +44 -2
  103. package/src/__tests__/settings-center.test.tsx +40 -1
  104. package/src/authRedirect.ts +38 -0
  105. package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
  106. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
  107. package/src/collection-manager/field-configure.ts +1 -1
  108. package/src/collection-manager/field-validation.ts +1 -1
  109. package/src/collection-manager/interfaces/id.ts +1 -1
  110. package/src/collection-manager/interfaces/m2m.tsx +2 -2
  111. package/src/collection-manager/interfaces/m2o.tsx +2 -2
  112. package/src/collection-manager/interfaces/nanoid.ts +1 -1
  113. package/src/collection-manager/interfaces/o2m.tsx +2 -2
  114. package/src/collection-manager/interfaces/obo.tsx +2 -2
  115. package/src/collection-manager/interfaces/oho.tsx +2 -2
  116. package/src/collection-manager/interfaces/properties/index.ts +2 -2
  117. package/src/collection-manager/interfaces/uuid.ts +1 -1
  118. package/src/components/AppComponents.tsx +19 -3
  119. package/src/components/README.md +7 -1
  120. package/src/components/README.zh-CN.md +6 -1
  121. package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
  122. package/src/components/category-tabs/index.ts +10 -0
  123. package/src/components/form/JsonTextArea.tsx +25 -11
  124. package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
  125. package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
  126. package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
  127. package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
  128. package/src/components/form/TypedVariableInput.tsx +452 -101
  129. package/src/components/form/VariableJsonTextArea.tsx +175 -0
  130. package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
  131. package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
  132. package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
  133. package/src/components/form/filter/CollectionFilter.tsx +4 -0
  134. package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
  135. package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
  136. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
  137. package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
  138. package/src/components/form/filter/index.ts +2 -0
  139. package/src/components/form/filter/useFilterActionProps.ts +37 -6
  140. package/src/components/form/index.tsx +1 -0
  141. package/src/components/index.ts +2 -0
  142. package/src/entry-actions/EntryActionManager.ts +76 -0
  143. package/src/entry-actions/index.ts +10 -0
  144. package/src/flow/FlowPage.tsx +38 -3
  145. package/src/flow/__tests__/FlowPage.test.tsx +201 -27
  146. package/src/flow/__tests__/FlowRoute.test.tsx +1181 -80
  147. package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
  148. package/src/flow/__tests__/getKey.test.ts +7 -0
  149. package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +34 -0
  150. package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +115 -0
  151. package/src/flow/actions/__tests__/afterSuccess.test.ts +73 -0
  152. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +120 -0
  153. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +1124 -0
  154. package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
  155. package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
  156. package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
  157. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
  158. package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
  159. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
  160. package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +164 -0
  161. package/src/flow/actions/__tests__/subFormFieldLinkageRules.inputArgs.test.ts +2 -2
  162. package/src/flow/actions/afterSuccess.tsx +142 -3
  163. package/src/flow/actions/customVariable.tsx +1 -2
  164. package/src/flow/actions/dataScopeFilter.ts +20 -1
  165. package/src/flow/actions/dateTimeFormat.tsx +42 -28
  166. package/src/flow/actions/index.ts +3 -1
  167. package/src/flow/actions/linkageRules.tsx +209 -47
  168. package/src/flow/actions/linkageRulesFormValueRefresh.ts +22 -130
  169. package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
  170. package/src/flow/actions/openView.tsx +59 -5
  171. package/src/flow/actions/runjs.tsx +2 -14
  172. package/src/flow/actions/validation.tsx +62 -30
  173. package/src/flow/admin-shell/BaseLayoutModel.tsx +149 -3
  174. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +187 -42
  175. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1020 -110
  176. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +74 -14
  177. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +486 -0
  178. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +64 -8
  179. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
  180. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
  181. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
  182. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
  183. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +23 -9
  184. package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
  185. package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
  186. package/src/flow/admin-shell/admin-layout/HelpLite.tsx +1 -3
  187. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +435 -0
  188. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
  189. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +518 -2
  190. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +214 -2
  191. package/src/flow/admin-shell/admin-layout/index.ts +1 -0
  192. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
  193. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
  194. package/src/flow/admin-shell/admin-layout/useApplications.tsx +81 -12
  195. package/src/flow/common/Markdown/Display.tsx +14 -3
  196. package/src/flow/common/Markdown/Edit.tsx +19 -7
  197. package/src/flow/common/Markdown/style.ts +4 -0
  198. package/src/flow/components/BlockItemCard.tsx +2 -2
  199. package/src/flow/components/DefaultValue.tsx +1 -2
  200. package/src/flow/components/DynamicFlowsIcon.tsx +447 -126
  201. package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
  202. package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
  203. package/src/flow/components/FieldAssignValueInput.tsx +91 -20
  204. package/src/flow/components/FlowRoute.tsx +256 -53
  205. package/src/flow/components/RunJSValueEditor.tsx +9 -1
  206. package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +148 -2
  207. package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
  208. package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
  209. package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
  210. package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
  211. package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
  212. package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
  213. package/src/flow/components/fieldAssignOptions.ts +155 -27
  214. package/src/flow/components/filter/FilterGroup.tsx +33 -5
  215. package/src/flow/components/filter/VariableFilterItem.tsx +158 -10
  216. package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
  217. package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
  218. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +70 -1
  219. package/src/flow/flows/editMarkdownFlow.tsx +1 -1
  220. package/src/flow/getViewDiffAndUpdateHidden.tsx +1 -0
  221. package/src/flow/index.ts +4 -1
  222. package/src/flow/internal/components/Markdown/util.ts +2 -1
  223. package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
  224. package/src/flow/internal/utils/operatorSchemaHelper.ts +15 -1
  225. package/src/flow/models/actions/JSActionModel.tsx +2 -7
  226. package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
  227. package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
  228. package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
  229. package/src/flow/models/base/ActionModel.tsx +21 -8
  230. package/src/flow/models/base/ActionModelCore.tsx +11 -4
  231. package/src/flow/models/base/BlockGridModel.tsx +26 -0
  232. package/src/flow/models/base/CollectionBlockModel.tsx +6 -2
  233. package/src/flow/models/base/GridModel.tsx +38 -7
  234. package/src/flow/models/base/PageModel/PageModel.tsx +387 -50
  235. package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
  236. package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
  237. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +817 -13
  238. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
  239. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
  240. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
  241. package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
  242. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +37 -0
  243. package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
  244. package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
  245. package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
  246. package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
  247. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +59 -6
  248. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
  249. package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
  250. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
  251. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +143 -4
  252. package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +1 -1
  253. package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +56 -1
  254. package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
  255. package/src/flow/models/blocks/form/FormActionModel.tsx +1 -1
  256. package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
  257. package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
  258. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
  259. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
  260. package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
  261. package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +54 -1
  262. package/src/flow/models/blocks/form/submitHandler.ts +17 -3
  263. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +940 -75
  264. package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
  265. package/src/flow/models/blocks/form/value-runtime/runtime.ts +348 -13
  266. package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
  267. package/src/flow/models/blocks/js-block/JSBlock.tsx +225 -9
  268. package/src/flow/models/blocks/js-block/__tests__/JSBlockModel.test.tsx +150 -0
  269. package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
  270. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +38 -14
  271. package/src/flow/models/blocks/table/TableBlockModel.tsx +42 -19
  272. package/src/flow/models/blocks/table/TableColumnModel.tsx +72 -6
  273. package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +111 -1
  274. package/src/flow/models/blocks/table/__tests__/TableBlockModel.dragSort.test.ts +127 -0
  275. package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
  276. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
  277. package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +1 -0
  278. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +243 -1
  279. package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +28 -17
  280. package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +13 -6
  281. package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +15 -2
  282. package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
  283. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +31 -7
  284. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +61 -17
  285. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +141 -19
  286. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
  287. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +197 -0
  288. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
  289. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
  290. package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
  291. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +23 -0
  292. package/src/flow/models/fields/ClickableFieldModel.tsx +5 -0
  293. package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
  294. package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
  295. package/src/flow/models/fields/DisplayDateTimeFieldModel.tsx +29 -1
  296. package/src/flow/models/fields/InputFieldModel.tsx +40 -2
  297. package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
  298. package/src/flow/models/fields/JSFieldModel.tsx +2 -7
  299. package/src/flow/models/fields/JSItemModel.tsx +2 -14
  300. package/src/flow/models/fields/SelectFieldModel.tsx +6 -4
  301. package/src/flow/models/fields/TextareaFieldModel.tsx +72 -3
  302. package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
  303. package/src/flow/models/fields/__tests__/DisplayDateTimeFieldModel.test.tsx +96 -0
  304. package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +117 -1
  305. package/src/flow/models/fields/__tests__/SelectFieldModel.test.tsx +43 -0
  306. package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +131 -0
  307. package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
  308. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
  309. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
  310. package/src/flow/models/topbar/TopbarActionModel.tsx +168 -10
  311. package/src/flow/resolveViewParamsToViewList.tsx +11 -3
  312. package/src/flow/routeTransientInputArgs.ts +27 -0
  313. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +300 -0
  314. package/src/flow/utils/dataScopeFormValueClear.ts +219 -81
  315. package/src/flow/utils/dataScopeRowSnapshot.ts +616 -0
  316. package/src/flow/utils/dateTimeDisplayProps.ts +135 -0
  317. package/src/flow/utils/formValueDeps.ts +170 -0
  318. package/src/flow-compat/FieldValidation.tsx +122 -60
  319. package/src/flow-compat/Popover.tsx +43 -4
  320. package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
  321. package/src/flow-compat/fieldValidationConstants.ts +1 -1
  322. package/src/flow-compat/routeTypes.ts +1 -0
  323. package/src/index.ts +14 -1
  324. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
  325. package/src/locale/languageCodes.ts +3 -1
  326. package/src/nocobase-buildin-plugin/index.tsx +63 -15
  327. package/src/settings-center/AdminSettingsLayout.tsx +10 -2
  328. package/src/settings-center/SystemSettingsPage.tsx +1 -2
  329. package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
  330. package/src/settings-center/plugin-manager/index.tsx +3 -0
  331. package/src/settings-center/utils.tsx +0 -6
  332. package/src/utils/getRouteRuntimeVersion.ts +185 -0
  333. package/src/utils/index.tsx +1 -0
  334. package/src/utils/markdownSanitize.ts +88 -0
@@ -11,10 +11,12 @@ import React from 'react';
11
11
  import { observable } from '@formily/reactive';
12
12
  import { act, render, waitFor } from '@testing-library/react';
13
13
  import { beforeEach, describe, expect, it, vi } from 'vitest';
14
+ import { MemoryRouter } from 'react-router-dom';
14
15
 
15
- const { flowModelRendererSpy } = vi.hoisted(() => {
16
+ const { flowModelRendererSpy, adminLayoutContentEffectSpy } = vi.hoisted(() => {
16
17
  return {
17
18
  flowModelRendererSpy: vi.fn(),
19
+ adminLayoutContentEffectSpy: vi.fn(),
18
20
  };
19
21
  });
20
22
 
@@ -35,14 +37,62 @@ vi.mock('@nocobase/flow-engine', async (importOriginal) => {
35
37
  };
36
38
  });
37
39
 
40
+ vi.mock('@ant-design/pro-layout', async () => {
41
+ const ReactActual = await import('react');
42
+ const RouteContext = ReactActual.createContext({ isMobile: true });
43
+ const ProLayout = (props: { children?: React.ReactNode }) => {
44
+ return ReactActual.createElement(RouteContext.Provider, { value: { isMobile: true } }, props.children);
45
+ };
46
+
47
+ return {
48
+ default: ProLayout,
49
+ RouteContext,
50
+ };
51
+ });
52
+
53
+ vi.mock('../AdminLayoutSlotModels', async (importOriginal) => {
54
+ const actual = await importOriginal<typeof import('../AdminLayoutSlotModels')>();
55
+ const ReactActual = await import('react');
56
+
57
+ return {
58
+ ...actual,
59
+ AdminLayoutContent: (props: { onContentElementChange?: (element: HTMLDivElement | null) => void }) => {
60
+ const bindContentRef = ReactActual.useCallback(
61
+ (element: HTMLDivElement | null) => {
62
+ props.onContentElementChange?.(element);
63
+ if (element) {
64
+ adminLayoutContentEffectSpy();
65
+ }
66
+ },
67
+ [props.onContentElementChange],
68
+ );
69
+
70
+ return ReactActual.createElement('div', { ref: bindContentRef, 'data-testid': 'admin-layout-content' });
71
+ },
72
+ };
73
+ });
74
+
75
+ vi.mock('../useApplications', () => ({
76
+ useApplications: () => ({
77
+ Component: null,
78
+ appList: [],
79
+ }),
80
+ }));
81
+
82
+ vi.mock('../AppListRender', () => ({
83
+ useAppListRender: () => undefined,
84
+ }));
85
+
38
86
  import {
39
87
  FlowEngine,
40
88
  FlowEngineProvider,
41
89
  FlowModelRenderer,
90
+ encodeOpenViewRouteState,
42
91
  useFlowEngine,
43
92
  type FlowModel,
44
93
  } from '@nocobase/flow-engine';
45
- import { AdminLayoutModel, getAdminLayoutModel } from '..';
94
+ import { AdminLayoutComponent, AdminLayoutModel, getAdminLayoutModel } from '..';
95
+ import { NocoBaseDesktopRouteType } from '../../../../flow-compat';
46
96
  import { getLayoutPageRouteName, getLayoutPageViewRouteName } from '../../../../layout-manager/utils';
47
97
  import { TopbarActionModel } from '../../../models/topbar/TopbarActionModel';
48
98
  import { UserCenterTopbarActionModel } from '../../../models/topbar/UserCenterTopbarActionModel';
@@ -82,6 +132,7 @@ const TestAdminLayoutHost = (props) => {
82
132
  describe('AdminLayoutModel runtime', () => {
83
133
  beforeEach(() => {
84
134
  flowModelRendererSpy.mockClear();
135
+ adminLayoutContentEffectSpy.mockReset();
85
136
  });
86
137
 
87
138
  it('should create model via getAdminLayoutModel and update props on rerender', async () => {
@@ -141,6 +192,46 @@ describe('AdminLayoutModel runtime', () => {
141
192
  expect(model.context.layoutContentElement).toBeNull();
142
193
  });
143
194
 
195
+ it('should expose mobile layout state before initial layout content effects run', async () => {
196
+ const engine = new FlowEngine();
197
+ engine.context.defineProperty('routeRepository', {
198
+ value: {
199
+ listAccessible: vi.fn(() => []),
200
+ subscribe: vi.fn(),
201
+ unsubscribe: vi.fn(),
202
+ ensureAccessibleLoaded: vi.fn(() => Promise.resolve()),
203
+ moveRoute: vi.fn(() => Promise.resolve()),
204
+ },
205
+ });
206
+ const model = getAdminLayoutModel<AdminLayoutModel>(engine, { create: true });
207
+ if (!model) {
208
+ throw new Error('[NocoBase] Failed to create admin-layout-model.');
209
+ }
210
+ const modelLifecycle = model as unknown as { onMount: () => void; onUnmount: () => void };
211
+ modelLifecycle.onMount();
212
+ const observedMobileStates: boolean[] = [];
213
+ adminLayoutContentEffectSpy.mockImplementation(() => {
214
+ observedMobileStates.push(model.context.isMobileLayout);
215
+ });
216
+
217
+ const { unmount } = render(
218
+ <FlowEngineProvider engine={engine}>
219
+ <MemoryRouter initialEntries={['/admin/lrmg36pcahi']}>
220
+ <AdminLayoutComponent model={model} />
221
+ </MemoryRouter>
222
+ </FlowEngineProvider>,
223
+ );
224
+
225
+ await waitFor(() => {
226
+ expect(adminLayoutContentEffectSpy).toHaveBeenCalledTimes(1);
227
+ });
228
+
229
+ expect(observedMobileStates).toEqual([true]);
230
+
231
+ unmount();
232
+ modelLifecycle.onUnmount();
233
+ });
234
+
144
235
  it('should expose layout definition only while mounted', async () => {
145
236
  const engine = new FlowEngine();
146
237
  const { unmount } = render(
@@ -213,6 +304,211 @@ describe('AdminLayoutModel runtime', () => {
213
304
  });
214
305
  });
215
306
 
307
+ it('should keep route state when route page registers after route sync', async () => {
308
+ const engine = new FlowEngine();
309
+
310
+ render(
311
+ <FlowEngineProvider engine={engine}>
312
+ <TestAdminLayoutHost />
313
+ </FlowEngineProvider>,
314
+ );
315
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
316
+ const routeState = {
317
+ __nocobaseOpenViewInputArgs: {
318
+ popup: {
319
+ formData: {
320
+ start: '2026-06-24',
321
+ end: '2026-06-25',
322
+ },
323
+ },
324
+ },
325
+ };
326
+
327
+ act(() => {
328
+ model.syncLayoutRoute({
329
+ name: getLayoutPageViewRouteName('admin'),
330
+ pathname: '/admin/page-1/view/popup',
331
+ layoutBasePathname: '/admin',
332
+ state: routeState,
333
+ });
334
+ });
335
+
336
+ const coordinator = (model as any).getCoordinator();
337
+ const syncRoute = vi.spyOn(coordinator, 'syncRoute').mockImplementation(() => undefined);
338
+
339
+ act(() => {
340
+ model.registerRoutePage('page-1', {
341
+ active: true,
342
+ layoutContentElement: document.createElement('div'),
343
+ });
344
+ });
345
+
346
+ expect(syncRoute).toHaveBeenCalledWith(
347
+ expect.objectContaining({
348
+ pageUid: 'page-1',
349
+ pathname: '/admin/page-1/view/popup',
350
+ state: routeState,
351
+ }),
352
+ );
353
+ });
354
+
355
+ it('should parse RunJS openView route params into layout route view stack state', async () => {
356
+ const token = encodeOpenViewRouteState('popup', { mode: 'dialog', size: 'large' });
357
+ if (!token) {
358
+ throw new Error('Expected openView route state token.');
359
+ }
360
+ const engine = new FlowEngine();
361
+ engine.context.defineProperty('routeRepository', {
362
+ value: {
363
+ getRouteBySchemaUid: (pageUid: string) => ({ title: pageUid }),
364
+ },
365
+ });
366
+
367
+ render(
368
+ <FlowEngineProvider engine={engine}>
369
+ <TestAdminLayoutHost />
370
+ </FlowEngineProvider>,
371
+ );
372
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
373
+ expect(model).toBeTruthy();
374
+
375
+ act(() => {
376
+ model.syncLayoutRoute({
377
+ name: getLayoutPageViewRouteName('admin'),
378
+ pathname: `/admin/page-1/view/popup/opts/${token}/filterbytk/1`,
379
+ layoutBasePathname: '/admin',
380
+ });
381
+ });
382
+
383
+ await waitFor(() => {
384
+ expect(model.context.layoutRoute).toMatchObject({
385
+ type: 'page',
386
+ pageUid: 'page-1',
387
+ viewStack: [
388
+ { viewUid: 'page-1' },
389
+ {
390
+ viewUid: 'popup',
391
+ openViewRouteState: { mode: 'dialog', size: 'large' },
392
+ filterByTk: '1',
393
+ },
394
+ ],
395
+ });
396
+ });
397
+ });
398
+
399
+ it('should reject malformed RunJS openView route params', async () => {
400
+ const wrongViewToken = encodeOpenViewRouteState('other-popup', { mode: 'dialog', size: 'large' });
401
+ if (!wrongViewToken) {
402
+ throw new Error('Expected openView route state token.');
403
+ }
404
+ const engine = new FlowEngine();
405
+
406
+ render(
407
+ <FlowEngineProvider engine={engine}>
408
+ <TestAdminLayoutHost />
409
+ </FlowEngineProvider>,
410
+ );
411
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
412
+ expect(model).toBeTruthy();
413
+
414
+ [
415
+ '/admin/page-1/sourceid',
416
+ '/admin/page-1/AbCdEfGh/filterbytk/1',
417
+ '/admin/page-1/view/popup/AbCdEfGh/filterbytk/1',
418
+ '/admin/page-1/view/popup/opts/AbCdEfGh/filterbytk/1',
419
+ '/admin/page-1/view/popup/openviewmode/dialog',
420
+ '/admin/page-1/view/popup/openviewsize/large',
421
+ `/admin/page-1/view/popup/opts/${wrongViewToken}/filterbytk/1`,
422
+ ].forEach((pathname) => {
423
+ expect(
424
+ model.resolveLayoutRoute({
425
+ name: getLayoutPageViewRouteName('admin'),
426
+ pathname,
427
+ layoutBasePathname: '/admin',
428
+ }),
429
+ ).toMatchObject({
430
+ type: 'notFound',
431
+ pathname,
432
+ });
433
+ });
434
+ });
435
+
436
+ it('should resolve group id paths as blank root routes', async () => {
437
+ const engine = new FlowEngine();
438
+ engine.context.defineProperty('routeRepository', {
439
+ value: {
440
+ getRouteById: (routeId: string) =>
441
+ routeId === '1'
442
+ ? {
443
+ id: 1,
444
+ type: NocoBaseDesktopRouteType.group,
445
+ }
446
+ : undefined,
447
+ },
448
+ });
449
+
450
+ render(
451
+ <FlowEngineProvider engine={engine}>
452
+ <TestAdminLayoutHost />
453
+ </FlowEngineProvider>,
454
+ );
455
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
456
+ expect(model).toBeTruthy();
457
+
458
+ expect(
459
+ model.resolveLayoutRoute({
460
+ name: getLayoutPageRouteName('admin'),
461
+ pathname: '/admin/1',
462
+ layoutBasePathname: '/admin',
463
+ }),
464
+ ).toMatchObject({
465
+ type: 'root',
466
+ pathname: '/admin/1',
467
+ relativePath: '1',
468
+ });
469
+ });
470
+
471
+ it('should prefer schema uid routes before treating numeric paths as group ids', async () => {
472
+ const engine = new FlowEngine();
473
+ engine.context.defineProperty('routeRepository', {
474
+ value: {
475
+ getRouteBySchemaUid: (pageUid: string) =>
476
+ pageUid === '1'
477
+ ? {
478
+ schemaUid: '1',
479
+ type: NocoBaseDesktopRouteType.flowPage,
480
+ }
481
+ : undefined,
482
+ getRouteById: (routeId: string) =>
483
+ routeId === '1'
484
+ ? {
485
+ id: 1,
486
+ type: NocoBaseDesktopRouteType.group,
487
+ }
488
+ : undefined,
489
+ },
490
+ });
491
+
492
+ render(
493
+ <FlowEngineProvider engine={engine}>
494
+ <TestAdminLayoutHost />
495
+ </FlowEngineProvider>,
496
+ );
497
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
498
+ expect(model).toBeTruthy();
499
+
500
+ expect(
501
+ model.resolveLayoutRoute({
502
+ name: getLayoutPageRouteName('admin'),
503
+ pathname: '/admin/1',
504
+ layoutBasePathname: '/admin',
505
+ }),
506
+ ).toMatchObject({
507
+ type: 'page',
508
+ pageUid: '1',
509
+ });
510
+ });
511
+
216
512
  it('should not consume global routes that belong to nested layouts', async () => {
217
513
  const engine = new FlowEngine();
218
514
  const routeRef = observable.ref({
@@ -313,6 +609,226 @@ describe('AdminLayoutModel runtime', () => {
313
609
  });
314
610
  });
315
611
 
612
+ it('should restore layout route from router context when a route page registers after stale cleanup', async () => {
613
+ const engine = new FlowEngine();
614
+ engine.context.defineProperty('route', {
615
+ value: {
616
+ name: 'admin.page',
617
+ pathname: '/admin/page-1',
618
+ params: { name: 'page-1' },
619
+ },
620
+ });
621
+ engine.context.defineProperty('routeRepository', {
622
+ value: {
623
+ getRouteBySchemaUid: (pageUid: string) => ({ title: pageUid }),
624
+ },
625
+ });
626
+
627
+ render(
628
+ <FlowEngineProvider engine={engine}>
629
+ <TestAdminLayoutHost />
630
+ </FlowEngineProvider>,
631
+ );
632
+
633
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
634
+ expect(model).toBeTruthy();
635
+ const routeLike = {
636
+ name: 'admin.page',
637
+ pathname: '/admin/page-1',
638
+ layoutRouteName: 'admin',
639
+ layoutBasePathname: '/admin',
640
+ };
641
+
642
+ act(() => {
643
+ model.syncLayoutRoute(routeLike);
644
+ model.clearLayoutRoute(routeLike);
645
+ });
646
+
647
+ expect(model.context.layoutRoute).toBeNull();
648
+ const syncRouteSpy = vi.spyOn(model.getCoordinator(), 'syncRoute');
649
+ syncRouteSpy.mockClear();
650
+
651
+ act(() => {
652
+ model.registerRoutePage('page-1', {
653
+ active: true,
654
+ });
655
+ });
656
+
657
+ await waitFor(() => {
658
+ expect(model.context.layoutRoute).toMatchObject({
659
+ type: 'page',
660
+ pageUid: 'page-1',
661
+ pathname: '/admin/page-1',
662
+ });
663
+ });
664
+ expect(model.context.currentRoute.title).toBe('page-1');
665
+ expect(syncRouteSpy).toHaveBeenLastCalledWith(
666
+ expect.objectContaining({
667
+ layoutRouteName: 'admin',
668
+ pageUid: 'page-1',
669
+ pathname: '/admin/page-1',
670
+ layoutBasePathname: '/admin',
671
+ }),
672
+ );
673
+ });
674
+
675
+ it('should restore layout route from a legacy dotted page view route name', async () => {
676
+ const engine = new FlowEngine();
677
+ engine.context.defineProperty('route', {
678
+ value: {
679
+ name: 'admin.page.view',
680
+ pathname: '/admin/page-1/view/popup',
681
+ params: { name: 'page-1' },
682
+ },
683
+ });
684
+ engine.context.defineProperty('routeRepository', {
685
+ value: {
686
+ getRouteBySchemaUid: (pageUid: string) => ({ title: pageUid }),
687
+ },
688
+ });
689
+
690
+ render(
691
+ <FlowEngineProvider engine={engine}>
692
+ <TestAdminLayoutHost />
693
+ </FlowEngineProvider>,
694
+ );
695
+
696
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
697
+ expect(model).toBeTruthy();
698
+
699
+ act(() => {
700
+ model.registerRoutePage('page-1', {
701
+ active: true,
702
+ });
703
+ });
704
+
705
+ expect(model.context.layoutRoute).toMatchObject({
706
+ type: 'page',
707
+ pageUid: 'page-1',
708
+ pathname: '/admin/page-1/view/popup',
709
+ viewStack: [{ viewUid: 'page-1' }, { viewUid: 'popup' }],
710
+ });
711
+ expect(model.context.currentRoute.title).toBe('page-1');
712
+ });
713
+
714
+ it('should not restore layout route from another layout when a route page registers', async () => {
715
+ const engine = new FlowEngine();
716
+ engine.context.defineProperty('route', {
717
+ value: {
718
+ name: 'admin.settings.publicForms.page',
719
+ pathname: '/admin/settings/public-forms/form-1',
720
+ params: { name: 'form-1' },
721
+ layoutRouteName: 'admin.settings.publicForms',
722
+ layoutBasePathname: '/admin/settings/public-forms',
723
+ },
724
+ });
725
+ engine.context.defineProperty('routeRepository', {
726
+ value: {
727
+ getRouteBySchemaUid: (pageUid: string) => ({ title: pageUid }),
728
+ },
729
+ });
730
+
731
+ render(
732
+ <FlowEngineProvider engine={engine}>
733
+ <TestAdminLayoutHost />
734
+ </FlowEngineProvider>,
735
+ );
736
+
737
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
738
+ expect(model).toBeTruthy();
739
+
740
+ act(() => {
741
+ model.registerRoutePage('form-1', {
742
+ active: true,
743
+ });
744
+ });
745
+
746
+ expect(model.context.layoutRoute).toBeNull();
747
+ expect(model.context.currentRoute).toEqual({});
748
+ });
749
+
750
+ it('should not restore layout route from a nested layout route without layoutRouteName', async () => {
751
+ const engine = new FlowEngine();
752
+ engine.context.defineProperty('route', {
753
+ value: {
754
+ name: 'admin.settings.publicForms.page',
755
+ pathname: '/admin/settings/public-forms/form-1',
756
+ params: { name: 'form-1' },
757
+ layoutBasePathname: '/admin/settings/public-forms',
758
+ },
759
+ });
760
+ engine.context.defineProperty('routeRepository', {
761
+ value: {
762
+ getRouteBySchemaUid: (pageUid: string) => ({ title: pageUid }),
763
+ },
764
+ });
765
+
766
+ render(
767
+ <FlowEngineProvider engine={engine}>
768
+ <TestAdminLayoutHost />
769
+ </FlowEngineProvider>,
770
+ );
771
+
772
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
773
+ expect(model).toBeTruthy();
774
+
775
+ act(() => {
776
+ model.registerRoutePage('form-1', {
777
+ active: true,
778
+ });
779
+ });
780
+
781
+ expect(model.context.layoutRoute).toBeNull();
782
+ expect(model.context.currentRoute).toEqual({});
783
+ });
784
+
785
+ it('should ignore stale layout route cleanup after a route page has registered on the same path', async () => {
786
+ const engine = new FlowEngine();
787
+ engine.context.defineProperty('route', {
788
+ value: {
789
+ name: 'admin.page',
790
+ pathname: '/admin/page-1',
791
+ params: { name: 'page-1' },
792
+ },
793
+ });
794
+ engine.context.defineProperty('routeRepository', {
795
+ value: {
796
+ getRouteBySchemaUid: (pageUid: string) => ({ title: pageUid }),
797
+ },
798
+ });
799
+
800
+ render(
801
+ <FlowEngineProvider engine={engine}>
802
+ <TestAdminLayoutHost />
803
+ </FlowEngineProvider>,
804
+ );
805
+
806
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
807
+ expect(model).toBeTruthy();
808
+ const staleRouteLike = {
809
+ name: 'admin.page',
810
+ pathname: '/admin/page-1',
811
+ layoutRouteName: 'admin',
812
+ layoutBasePathname: '/admin',
813
+ };
814
+
815
+ act(() => {
816
+ model.syncLayoutRoute(staleRouteLike);
817
+ model.clearLayoutRoute(staleRouteLike);
818
+ model.registerRoutePage('page-1', {
819
+ active: true,
820
+ });
821
+ model.clearLayoutRoute(staleRouteLike);
822
+ });
823
+
824
+ expect(model.context.layoutRoute).toMatchObject({
825
+ type: 'page',
826
+ pageUid: 'page-1',
827
+ pathname: '/admin/page-1',
828
+ });
829
+ expect(model.context.currentRoute.title).toBe('page-1');
830
+ });
831
+
316
832
  it('should keep pageActive in sync after non-active route page updates', async () => {
317
833
  const engine = new FlowEngine();
318
834
  engine.context.defineProperty('routeRepository', {