@nocobase/client-v2 2.2.0-alpha.1 → 2.2.0-alpha.10
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.
- package/es/APIClient.d.ts +1 -3
- package/es/BaseApplication.d.ts +3 -0
- package/es/PluginSettingsManager.d.ts +33 -0
- package/es/RouteRepository.d.ts +29 -6
- package/es/RouterManager.d.ts +15 -0
- package/es/authRedirect.d.ts +1 -0
- package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
- package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
- package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
- package/es/components/category-tabs/index.d.ts +9 -0
- package/es/components/form/JsonTextArea.d.ts +2 -1
- package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
- package/es/components/form/TypedVariableInput.d.ts +31 -5
- package/es/components/form/VariableJsonTextArea.d.ts +19 -0
- package/es/components/form/filter/CollectionFilter.d.ts +2 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
- package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
- package/es/components/form/filter/index.d.ts +2 -0
- package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
- package/es/components/form/index.d.ts +1 -0
- package/es/components/index.d.ts +2 -0
- package/es/entry-actions/EntryActionManager.d.ts +24 -0
- package/es/entry-actions/index.d.ts +9 -0
- package/es/flow/actions/afterSuccess.d.ts +8 -1
- package/es/flow/actions/index.d.ts +2 -2
- package/es/flow/actions/linkageRules.d.ts +2 -0
- package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
- package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +3 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
- package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +5 -0
- package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
- package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
- package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
- package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
- package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
- package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
- package/es/flow/components/FieldAssignValueInput.d.ts +9 -0
- package/es/flow/components/FlowRoute.d.ts +2 -2
- package/es/flow/components/RunJSValueEditor.d.ts +1 -0
- package/es/flow/components/filter/FilterGroup.d.ts +1 -0
- package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
- package/es/flow/index.d.ts +1 -0
- package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
- package/es/flow/models/base/ActionModelCore.d.ts +2 -0
- package/es/flow/models/base/BlockGridModel.d.ts +3 -0
- package/es/flow/models/base/GridModel.d.ts +1 -1
- package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
- package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
- package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
- package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
- package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
- package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
- package/es/flow/models/blocks/form/submitHandler.d.ts +1 -1
- package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
- package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +30 -0
- package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
- package/es/flow/models/blocks/js-block/JSBlock.d.ts +1 -0
- package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +1 -0
- package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +1 -0
- package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +1 -1
- package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +2 -1
- package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +6 -4
- package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +2 -0
- package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +6 -0
- package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
- package/es/flow/resolveViewParamsToViewList.d.ts +1 -1
- package/es/flow/routeTransientInputArgs.d.ts +14 -0
- package/es/flow/utils/dataScopeRowSnapshot.d.ts +59 -0
- package/es/flow/utils/dateTimeDisplayProps.d.ts +49 -0
- package/es/flow/utils/formValueDeps.d.ts +32 -0
- package/es/flow-compat/FieldValidation.d.ts +2 -1
- package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
- package/es/flow-compat/routeTypes.d.ts +1 -0
- package/es/index.d.ts +8 -1
- package/es/index.mjs +367 -181
- package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
- package/es/utils/index.d.ts +1 -0
- package/es/utils/markdownSanitize.d.ts +11 -0
- package/lib/index.js +367 -181
- package/lib/locale/languageCodes.js +3 -1
- package/package.json +9 -7
- package/src/APIClient.ts +1 -10
- package/src/Application.tsx +4 -0
- package/src/BaseApplication.tsx +13 -6
- package/src/PluginSettingsManager.ts +53 -0
- package/src/RouteRepository.ts +151 -24
- package/src/RouterManager.tsx +142 -1
- package/src/__tests__/PluginSettingsManager.test.ts +13 -0
- package/src/__tests__/RouteRepository.test.ts +239 -0
- package/src/__tests__/RouterManager.test.ts +125 -0
- package/src/__tests__/app.test.tsx +73 -0
- package/src/__tests__/authRedirect.test.ts +17 -0
- package/src/__tests__/browserChecker.test.ts +61 -0
- package/src/__tests__/exports.test.ts +16 -0
- package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +234 -2
- package/src/__tests__/plugin-manager.test.tsx +44 -2
- package/src/__tests__/settings-center.test.tsx +40 -1
- package/src/authRedirect.ts +38 -0
- package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
- package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
- package/src/collection-manager/field-configure.ts +1 -1
- package/src/collection-manager/field-validation.ts +1 -1
- package/src/collection-manager/interfaces/id.ts +1 -1
- package/src/collection-manager/interfaces/m2m.tsx +2 -2
- package/src/collection-manager/interfaces/m2o.tsx +2 -2
- package/src/collection-manager/interfaces/nanoid.ts +1 -1
- package/src/collection-manager/interfaces/o2m.tsx +2 -2
- package/src/collection-manager/interfaces/obo.tsx +2 -2
- package/src/collection-manager/interfaces/oho.tsx +2 -2
- package/src/collection-manager/interfaces/properties/index.ts +2 -2
- package/src/collection-manager/interfaces/uuid.ts +1 -1
- package/src/components/AppComponents.tsx +19 -3
- package/src/components/README.md +7 -1
- package/src/components/README.zh-CN.md +6 -1
- package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
- package/src/components/category-tabs/index.ts +10 -0
- package/src/components/form/JsonTextArea.tsx +25 -11
- package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
- package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
- package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
- package/src/components/form/TypedVariableInput.tsx +452 -101
- package/src/components/form/VariableJsonTextArea.tsx +175 -0
- package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
- package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
- package/src/components/form/filter/CollectionFilter.tsx +4 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
- package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
- package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
- package/src/components/form/filter/index.ts +2 -0
- package/src/components/form/filter/useFilterActionProps.ts +37 -6
- package/src/components/form/index.tsx +1 -0
- package/src/components/index.ts +2 -0
- package/src/entry-actions/EntryActionManager.ts +76 -0
- package/src/entry-actions/index.ts +10 -0
- package/src/flow/FlowPage.tsx +38 -3
- package/src/flow/__tests__/FlowPage.test.tsx +201 -27
- package/src/flow/__tests__/FlowRoute.test.tsx +1181 -80
- package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
- package/src/flow/__tests__/getKey.test.ts +7 -0
- package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +34 -0
- package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +115 -0
- package/src/flow/actions/__tests__/afterSuccess.test.ts +73 -0
- package/src/flow/actions/__tests__/dataScopeFilter.test.ts +120 -0
- package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +1124 -0
- package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
- package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
- package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
- package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
- package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
- package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +164 -0
- package/src/flow/actions/__tests__/subFormFieldLinkageRules.inputArgs.test.ts +2 -2
- package/src/flow/actions/afterSuccess.tsx +142 -3
- package/src/flow/actions/customVariable.tsx +1 -2
- package/src/flow/actions/dataScopeFilter.ts +20 -1
- package/src/flow/actions/dateTimeFormat.tsx +42 -28
- package/src/flow/actions/index.ts +3 -1
- package/src/flow/actions/linkageRules.tsx +209 -47
- package/src/flow/actions/linkageRulesFormValueRefresh.ts +22 -130
- package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
- package/src/flow/actions/openView.tsx +59 -5
- package/src/flow/actions/runjs.tsx +2 -14
- package/src/flow/actions/validation.tsx +62 -30
- package/src/flow/admin-shell/BaseLayoutModel.tsx +149 -3
- package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +187 -42
- package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1020 -110
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +74 -14
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +486 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +64 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
- package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +23 -9
- package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
- package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
- package/src/flow/admin-shell/admin-layout/HelpLite.tsx +1 -3
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +435 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +518 -2
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +214 -2
- package/src/flow/admin-shell/admin-layout/index.ts +1 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
- package/src/flow/admin-shell/admin-layout/useApplications.tsx +81 -12
- package/src/flow/common/Markdown/Display.tsx +14 -3
- package/src/flow/common/Markdown/Edit.tsx +19 -7
- package/src/flow/common/Markdown/style.ts +4 -0
- package/src/flow/components/BlockItemCard.tsx +2 -2
- package/src/flow/components/DefaultValue.tsx +1 -2
- package/src/flow/components/DynamicFlowsIcon.tsx +447 -126
- package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
- package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
- package/src/flow/components/FieldAssignValueInput.tsx +91 -20
- package/src/flow/components/FlowRoute.tsx +256 -53
- package/src/flow/components/RunJSValueEditor.tsx +9 -1
- package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +148 -2
- package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
- package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
- package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
- package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
- package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
- package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
- package/src/flow/components/fieldAssignOptions.ts +155 -27
- package/src/flow/components/filter/FilterGroup.tsx +33 -5
- package/src/flow/components/filter/VariableFilterItem.tsx +158 -10
- package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +70 -1
- package/src/flow/flows/editMarkdownFlow.tsx +1 -1
- package/src/flow/getViewDiffAndUpdateHidden.tsx +1 -0
- package/src/flow/index.ts +4 -1
- package/src/flow/internal/components/Markdown/util.ts +2 -1
- package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
- package/src/flow/internal/utils/operatorSchemaHelper.ts +15 -1
- package/src/flow/models/actions/JSActionModel.tsx +2 -7
- package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
- package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
- package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
- package/src/flow/models/base/ActionModel.tsx +21 -8
- package/src/flow/models/base/ActionModelCore.tsx +11 -4
- package/src/flow/models/base/BlockGridModel.tsx +26 -0
- package/src/flow/models/base/CollectionBlockModel.tsx +6 -2
- package/src/flow/models/base/GridModel.tsx +38 -7
- package/src/flow/models/base/PageModel/PageModel.tsx +387 -50
- package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
- package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +817 -13
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
- package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
- package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
- package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +37 -0
- package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
- package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
- package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
- package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +59 -6
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
- package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +143 -4
- package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +1 -1
- package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +56 -1
- package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
- package/src/flow/models/blocks/form/FormActionModel.tsx +1 -1
- package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
- package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
- package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
- package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
- package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +54 -1
- package/src/flow/models/blocks/form/submitHandler.ts +17 -3
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +940 -75
- package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +348 -13
- package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
- package/src/flow/models/blocks/js-block/JSBlock.tsx +225 -9
- package/src/flow/models/blocks/js-block/__tests__/JSBlockModel.test.tsx +150 -0
- package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +38 -14
- package/src/flow/models/blocks/table/TableBlockModel.tsx +42 -19
- package/src/flow/models/blocks/table/TableColumnModel.tsx +72 -6
- package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +111 -1
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.dragSort.test.ts +127 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +1 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +243 -1
- package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +28 -17
- package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +13 -6
- package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +15 -2
- package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +31 -7
- package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +61 -17
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +141 -19
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +197 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
- package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
- package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +23 -0
- package/src/flow/models/fields/ClickableFieldModel.tsx +5 -0
- package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
- package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
- package/src/flow/models/fields/DisplayDateTimeFieldModel.tsx +29 -1
- package/src/flow/models/fields/InputFieldModel.tsx +40 -2
- package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
- package/src/flow/models/fields/JSFieldModel.tsx +2 -7
- package/src/flow/models/fields/JSItemModel.tsx +2 -14
- package/src/flow/models/fields/SelectFieldModel.tsx +6 -4
- package/src/flow/models/fields/TextareaFieldModel.tsx +72 -3
- package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
- package/src/flow/models/fields/__tests__/DisplayDateTimeFieldModel.test.tsx +96 -0
- package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +117 -1
- package/src/flow/models/fields/__tests__/SelectFieldModel.test.tsx +43 -0
- package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +131 -0
- package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
- package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
- package/src/flow/models/topbar/TopbarActionModel.tsx +168 -10
- package/src/flow/resolveViewParamsToViewList.tsx +11 -3
- package/src/flow/routeTransientInputArgs.ts +27 -0
- package/src/flow/utils/__tests__/dateTimeFormat.test.ts +300 -0
- package/src/flow/utils/dataScopeFormValueClear.ts +219 -81
- package/src/flow/utils/dataScopeRowSnapshot.ts +616 -0
- package/src/flow/utils/dateTimeDisplayProps.ts +135 -0
- package/src/flow/utils/formValueDeps.ts +170 -0
- package/src/flow-compat/FieldValidation.tsx +122 -60
- package/src/flow-compat/Popover.tsx +43 -4
- package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
- package/src/flow-compat/fieldValidationConstants.ts +1 -1
- package/src/flow-compat/routeTypes.ts +1 -0
- package/src/index.ts +14 -1
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
- package/src/locale/languageCodes.ts +3 -1
- package/src/nocobase-buildin-plugin/index.tsx +63 -15
- package/src/settings-center/AdminSettingsLayout.tsx +10 -2
- package/src/settings-center/SystemSettingsPage.tsx +1 -2
- package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
- package/src/settings-center/plugin-manager/index.tsx +3 -0
- package/src/settings-center/utils.tsx +0 -6
- package/src/utils/getRouteRuntimeVersion.ts +185 -0
- package/src/utils/index.tsx +1 -0
- package/src/utils/markdownSanitize.ts +88 -0
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
FLOW_SETTINGS_PREFERENCE_STORAGE_KEY,
|
|
43
43
|
readFlowSettingsPreference,
|
|
44
44
|
} from './flowSettingsPreference';
|
|
45
|
+
import { joinAdminLayoutRoutePath, type AdminLayoutRoutePathLike } from './resolveAdminRouteRuntimeTarget';
|
|
45
46
|
import { useAppListRender } from './AppListRender';
|
|
46
47
|
|
|
47
48
|
const className1 = css`
|
|
@@ -284,13 +285,44 @@ function SetIsMobileLayout(props: { isMobile: boolean; children: any; model?: Ad
|
|
|
284
285
|
const flowEngine = useFlowEngine();
|
|
285
286
|
const adminLayoutModel = props.model || flowEngine.getModel<AdminLayoutModel>(ADMIN_LAYOUT_MODEL_UID);
|
|
286
287
|
|
|
287
|
-
|
|
288
|
+
useLayoutEffect(() => {
|
|
288
289
|
adminLayoutModel?.setIsMobileLayout(props.isMobile);
|
|
289
290
|
}, [adminLayoutModel, props.isMobile]);
|
|
290
291
|
|
|
291
292
|
return props.children;
|
|
292
293
|
}
|
|
293
294
|
|
|
295
|
+
function AdminLayoutContentWithMobileState(props: {
|
|
296
|
+
isMobile: boolean;
|
|
297
|
+
model?: AdminLayoutModel;
|
|
298
|
+
designable?: boolean;
|
|
299
|
+
layout?: AdminLayoutModel['layout'];
|
|
300
|
+
onContentElementChange?: (element: HTMLDivElement | null) => void;
|
|
301
|
+
}) {
|
|
302
|
+
const modelRef = useRef(props.model);
|
|
303
|
+
const isMobileRef = useRef(props.isMobile);
|
|
304
|
+
const onContentElementChangeRef = useRef(props.onContentElementChange);
|
|
305
|
+
|
|
306
|
+
modelRef.current = props.model;
|
|
307
|
+
isMobileRef.current = props.isMobile;
|
|
308
|
+
onContentElementChangeRef.current = props.onContentElementChange;
|
|
309
|
+
|
|
310
|
+
const handleContentElementChange = useCallback((element: HTMLDivElement | null) => {
|
|
311
|
+
if (element) {
|
|
312
|
+
modelRef.current?.setIsMobileLayout(isMobileRef.current);
|
|
313
|
+
}
|
|
314
|
+
onContentElementChangeRef.current?.(element);
|
|
315
|
+
}, []);
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<AdminLayoutContent
|
|
319
|
+
designable={props.designable}
|
|
320
|
+
layout={props.layout}
|
|
321
|
+
onContentElementChange={handleContentElementChange}
|
|
322
|
+
/>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
294
326
|
const DesignerButtonMenuItem: FC<{ item: AdminLayoutMenuNode; fallbackParentRoute?: NocoBaseDesktopRoute }> = (
|
|
295
327
|
props,
|
|
296
328
|
) => {
|
|
@@ -314,25 +346,37 @@ const DesignerButtonMenuItem: FC<{ item: AdminLayoutMenuNode; fallbackParentRout
|
|
|
314
346
|
);
|
|
315
347
|
};
|
|
316
348
|
|
|
317
|
-
const matchesRoutePath = (
|
|
349
|
+
const matchesRoutePath = (
|
|
350
|
+
route: NocoBaseDesktopRoute | undefined,
|
|
351
|
+
pathname: string,
|
|
352
|
+
layout?: AdminLayoutRoutePathLike | null,
|
|
353
|
+
): boolean => {
|
|
318
354
|
if (!route) {
|
|
319
355
|
return false;
|
|
320
356
|
}
|
|
321
357
|
|
|
322
358
|
const candidates = [
|
|
323
|
-
route.id != null ?
|
|
324
|
-
route.schemaUid ?
|
|
359
|
+
route.id != null ? joinAdminLayoutRoutePath(layout, route.id) : null,
|
|
360
|
+
route.schemaUid ? joinAdminLayoutRoutePath(layout, route.schemaUid) : null,
|
|
325
361
|
].filter(Boolean) as string[];
|
|
326
362
|
|
|
327
363
|
if (candidates.some((candidate) => pathname === candidate || pathname.startsWith(`${candidate}/`))) {
|
|
328
364
|
return true;
|
|
329
365
|
}
|
|
330
366
|
|
|
331
|
-
return Array.isArray(route.children)
|
|
367
|
+
return Array.isArray(route.children)
|
|
368
|
+
? route.children.some((child) => matchesRoutePath(child, pathname, layout))
|
|
369
|
+
: false;
|
|
332
370
|
};
|
|
333
371
|
|
|
334
|
-
const findSelectedTopGroupRoute = (
|
|
335
|
-
|
|
372
|
+
const findSelectedTopGroupRoute = (
|
|
373
|
+
routes: NocoBaseDesktopRoute[],
|
|
374
|
+
pathname: string,
|
|
375
|
+
layout?: AdminLayoutRoutePathLike | null,
|
|
376
|
+
) => {
|
|
377
|
+
return routes.find(
|
|
378
|
+
(route) => route.type === NocoBaseDesktopRouteType.group && matchesRoutePath(route, pathname, layout),
|
|
379
|
+
);
|
|
336
380
|
};
|
|
337
381
|
|
|
338
382
|
const renderMenuNodeWithModel = (
|
|
@@ -376,6 +420,11 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
376
420
|
const { token } = antdTheme.useToken();
|
|
377
421
|
const customToken = token as CustomToken;
|
|
378
422
|
const isMobileLayout = !!adminLayoutModel?.isMobileLayout;
|
|
423
|
+
const adminLayoutRoutePath = adminLayoutModel?.layout?.routePath;
|
|
424
|
+
const adminLayoutRoutePathLike = useMemo<AdminLayoutRoutePathLike | undefined>(
|
|
425
|
+
() => (adminLayoutRoutePath ? { routePath: adminLayoutRoutePath } : undefined),
|
|
426
|
+
[adminLayoutRoutePath],
|
|
427
|
+
);
|
|
379
428
|
const menuRouteRefreshVersion = adminLayoutModel?.menuRouteRefreshVersion || 0;
|
|
380
429
|
const isMobileSider = isMobileLayout;
|
|
381
430
|
const [collapsed, setCollapsed] = useState(isMobileSider);
|
|
@@ -392,8 +441,8 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
392
441
|
);
|
|
393
442
|
const designable = !isMobileSider && preferredFlowSettingsEnabled;
|
|
394
443
|
const { styles } = useHeaderStyle();
|
|
395
|
-
const { appList } = useApplications();
|
|
396
|
-
const appListRender = useAppListRender();
|
|
444
|
+
const { appList, appSwitcherModel } = useApplications(adminLayoutModel);
|
|
445
|
+
const appListRender = useAppListRender(appSwitcherModel);
|
|
397
446
|
const flowSettingsSyncRef = useRef(0);
|
|
398
447
|
const desiredFlowSettingsEnabledRef = useRef(false);
|
|
399
448
|
const handleLayoutContentElementChange = useCallback(
|
|
@@ -403,8 +452,8 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
403
452
|
[adminLayoutModel],
|
|
404
453
|
);
|
|
405
454
|
const selectedTopGroupRoute = useMemo(
|
|
406
|
-
() => findSelectedTopGroupRoute(allAccessRoutes, location.pathname),
|
|
407
|
-
[allAccessRoutes, location.pathname],
|
|
455
|
+
() => findSelectedTopGroupRoute(allAccessRoutes, location.pathname, adminLayoutRoutePathLike),
|
|
456
|
+
[adminLayoutRoutePathLike, allAccessRoutes, location.pathname],
|
|
408
457
|
);
|
|
409
458
|
|
|
410
459
|
const handleMenuDragEnd = useCallback(
|
|
@@ -458,6 +507,7 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
458
507
|
|
|
459
508
|
useEffect(() => {
|
|
460
509
|
const routeRepository = flowEngine.context.routeRepository;
|
|
510
|
+
const deactivateLayout = routeRepository?.activateLayout?.(adminLayoutModel?.layout);
|
|
461
511
|
const subscriber = () => {
|
|
462
512
|
const updatedRoutes = routeRepository?.listAccessible() || [];
|
|
463
513
|
setAllAccessRoutes(updatedRoutes);
|
|
@@ -470,8 +520,9 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
470
520
|
|
|
471
521
|
return () => {
|
|
472
522
|
routeRepository?.unsubscribe(subscriber);
|
|
523
|
+
deactivateLayout?.();
|
|
473
524
|
};
|
|
474
|
-
}, [flowEngine]);
|
|
525
|
+
}, [adminLayoutModel, adminLayoutModel?.layout?.uid, flowEngine]);
|
|
475
526
|
|
|
476
527
|
useEffect(() => {
|
|
477
528
|
if (typeof window === 'undefined') {
|
|
@@ -614,6 +665,10 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
614
665
|
z-index: 2000 !important;
|
|
615
666
|
}
|
|
616
667
|
|
|
668
|
+
.ant-pro-layout-apps-popover .ant-popover-content {
|
|
669
|
+
margin-top: 12px;
|
|
670
|
+
}
|
|
671
|
+
|
|
617
672
|
.ant-pro-layout-apps-icon {
|
|
618
673
|
color: ${customToken.colorTextHeaderMenu || 'rgba(255, 255, 255, 0.85)'};
|
|
619
674
|
}
|
|
@@ -657,7 +712,6 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
657
712
|
<MobileMenuControlContext.Provider value={{ closeMobileMenu }}>
|
|
658
713
|
<DndProvider collisionDetection={menuCollisionDetection} onDragEnd={handleMenuDragEnd}>
|
|
659
714
|
<ProLayout
|
|
660
|
-
key={`admin-layout-menu-${menuRouteRefreshVersion}`}
|
|
661
715
|
{...props}
|
|
662
716
|
contentStyle={contentStyle}
|
|
663
717
|
siderWidth={customToken.siderWidth || 200}
|
|
@@ -697,7 +751,13 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
697
751
|
<SetIsMobileLayout isMobile={isMobile} model={adminLayoutModel}>
|
|
698
752
|
<ConfigProvider theme={isMobile ? mobileTheme : theme}>
|
|
699
753
|
<GlobalStyle />
|
|
700
|
-
<
|
|
754
|
+
<AdminLayoutContentWithMobileState
|
|
755
|
+
isMobile={isMobile}
|
|
756
|
+
model={adminLayoutModel}
|
|
757
|
+
designable={designable}
|
|
758
|
+
layout={adminLayoutModel?.layout}
|
|
759
|
+
onContentElementChange={handleLayoutContentElementChange}
|
|
760
|
+
/>
|
|
701
761
|
</ConfigProvider>
|
|
702
762
|
</SetIsMobileLayout>
|
|
703
763
|
);
|
|
@@ -0,0 +1,486 @@
|
|
|
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 { FlowEngine, FlowEngineProvider } from '@nocobase/flow-engine';
|
|
11
|
+
import { act, render, screen, waitFor } from '@testing-library/react';
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { createMemoryRouter, Outlet, RouterProvider } from 'react-router-dom';
|
|
14
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
15
|
+
import { NocoBaseDesktopRouteType } from '../../../flow-compat';
|
|
16
|
+
import { RouteRepository } from '../../../RouteRepository';
|
|
17
|
+
import { AdminLayoutEntryGuard } from './AdminLayoutEntryGuard';
|
|
18
|
+
import type { AdminLayoutModel } from './AdminLayoutModel';
|
|
19
|
+
|
|
20
|
+
describe('AdminLayoutEntryGuard', () => {
|
|
21
|
+
it('should ensure accessible routes for the active layout before resolving the landing route', async () => {
|
|
22
|
+
const engine = new FlowEngine();
|
|
23
|
+
const request = vi.fn().mockResolvedValue({
|
|
24
|
+
data: {
|
|
25
|
+
data: [],
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
const routeRepository = new RouteRepository({
|
|
29
|
+
api: {
|
|
30
|
+
request,
|
|
31
|
+
resource: vi.fn(),
|
|
32
|
+
},
|
|
33
|
+
} as never);
|
|
34
|
+
routeRepository.setRoutes([], 'admin-layout-model');
|
|
35
|
+
const deactivateLayout = routeRepository.activateLayout({
|
|
36
|
+
uid: 'mobile-layout-model',
|
|
37
|
+
});
|
|
38
|
+
engine.context.defineProperty('routeRepository', {
|
|
39
|
+
value: routeRepository,
|
|
40
|
+
});
|
|
41
|
+
engine.context.defineProperty('app', {
|
|
42
|
+
value: {
|
|
43
|
+
router: {
|
|
44
|
+
getBasename: () => '',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const model = {
|
|
50
|
+
layout: {
|
|
51
|
+
routeName: 'mobile',
|
|
52
|
+
routePath: '/mobile',
|
|
53
|
+
uid: 'mobile-layout-model',
|
|
54
|
+
},
|
|
55
|
+
} as AdminLayoutModel;
|
|
56
|
+
const router = createMemoryRouter(
|
|
57
|
+
[
|
|
58
|
+
{
|
|
59
|
+
path: '/mobile',
|
|
60
|
+
id: 'mobile',
|
|
61
|
+
element: (
|
|
62
|
+
<FlowEngineProvider engine={engine}>
|
|
63
|
+
<AdminLayoutEntryGuard model={model}>
|
|
64
|
+
<div>Mobile layout shell</div>
|
|
65
|
+
</AdminLayoutEntryGuard>
|
|
66
|
+
</FlowEngineProvider>
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
{
|
|
71
|
+
initialEntries: ['/mobile'],
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
render(<RouterProvider router={router} />);
|
|
76
|
+
|
|
77
|
+
await waitFor(() => {
|
|
78
|
+
expect(request).toHaveBeenCalledWith({
|
|
79
|
+
url: '/desktopRoutes:listAccessible',
|
|
80
|
+
params: {
|
|
81
|
+
tree: true,
|
|
82
|
+
sort: 'sort',
|
|
83
|
+
layout: 'mobile-layout-model',
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
expect(screen.getByText('Mobile layout shell')).toBeInTheDocument();
|
|
88
|
+
deactivateLayout();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should activate the guard layout before loading accessible routes', async () => {
|
|
92
|
+
const engine = new FlowEngine();
|
|
93
|
+
const request = vi.fn().mockResolvedValue({
|
|
94
|
+
data: {
|
|
95
|
+
data: [],
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
const routeRepository = new RouteRepository({
|
|
99
|
+
api: {
|
|
100
|
+
request,
|
|
101
|
+
resource: vi.fn(),
|
|
102
|
+
},
|
|
103
|
+
} as never);
|
|
104
|
+
engine.context.defineProperty('routeRepository', {
|
|
105
|
+
value: routeRepository,
|
|
106
|
+
});
|
|
107
|
+
engine.context.defineProperty('app', {
|
|
108
|
+
value: {
|
|
109
|
+
router: {
|
|
110
|
+
getBasename: () => '',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const model = {
|
|
116
|
+
layout: {
|
|
117
|
+
routeName: 'mobile',
|
|
118
|
+
routePath: '/mobile',
|
|
119
|
+
uid: 'mobile-layout-model',
|
|
120
|
+
},
|
|
121
|
+
} as AdminLayoutModel;
|
|
122
|
+
const router = createMemoryRouter(
|
|
123
|
+
[
|
|
124
|
+
{
|
|
125
|
+
path: '/mobile',
|
|
126
|
+
id: 'mobile',
|
|
127
|
+
element: (
|
|
128
|
+
<FlowEngineProvider engine={engine}>
|
|
129
|
+
<AdminLayoutEntryGuard model={model}>
|
|
130
|
+
<div>Mobile layout shell</div>
|
|
131
|
+
</AdminLayoutEntryGuard>
|
|
132
|
+
</FlowEngineProvider>
|
|
133
|
+
),
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
{
|
|
137
|
+
initialEntries: ['/mobile'],
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
render(<RouterProvider router={router} />);
|
|
142
|
+
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
expect(request).toHaveBeenCalledWith({
|
|
145
|
+
url: '/desktopRoutes:listAccessible',
|
|
146
|
+
params: {
|
|
147
|
+
tree: true,
|
|
148
|
+
sort: 'sort',
|
|
149
|
+
layout: 'mobile-layout-model',
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
expect(screen.getByText('Mobile layout shell')).toBeInTheDocument();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should keep guard stable when layout getter returns a new object with the same fields', async () => {
|
|
157
|
+
const engine = new FlowEngine();
|
|
158
|
+
const request = vi.fn().mockResolvedValue({
|
|
159
|
+
data: {
|
|
160
|
+
data: [],
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
const routeRepository = new RouteRepository({
|
|
164
|
+
api: {
|
|
165
|
+
request,
|
|
166
|
+
resource: vi.fn(),
|
|
167
|
+
},
|
|
168
|
+
} as never);
|
|
169
|
+
const activateLayout = vi.spyOn(routeRepository, 'activateLayout');
|
|
170
|
+
engine.context.defineProperty('routeRepository', {
|
|
171
|
+
value: routeRepository,
|
|
172
|
+
});
|
|
173
|
+
engine.context.defineProperty('app', {
|
|
174
|
+
value: {
|
|
175
|
+
router: {
|
|
176
|
+
getBasename: () => '',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const model = {
|
|
182
|
+
get layout() {
|
|
183
|
+
return {
|
|
184
|
+
authCheck: true,
|
|
185
|
+
routeName: 'admin',
|
|
186
|
+
routePath: '/admin',
|
|
187
|
+
uid: 'admin-layout-model',
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
} as AdminLayoutModel;
|
|
191
|
+
let forceRerender = () => {};
|
|
192
|
+
const GuardShell = () => {
|
|
193
|
+
const [, setVersion] = React.useState(0);
|
|
194
|
+
forceRerender = () => setVersion((version) => version + 1);
|
|
195
|
+
return (
|
|
196
|
+
<AdminLayoutEntryGuard model={model}>
|
|
197
|
+
<div>Admin layout shell</div>
|
|
198
|
+
</AdminLayoutEntryGuard>
|
|
199
|
+
);
|
|
200
|
+
};
|
|
201
|
+
const router = createMemoryRouter(
|
|
202
|
+
[
|
|
203
|
+
{
|
|
204
|
+
path: '/admin',
|
|
205
|
+
id: 'admin',
|
|
206
|
+
element: (
|
|
207
|
+
<FlowEngineProvider engine={engine}>
|
|
208
|
+
<GuardShell />
|
|
209
|
+
</FlowEngineProvider>
|
|
210
|
+
),
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
{
|
|
214
|
+
initialEntries: ['/admin'],
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
render(<RouterProvider router={router} />);
|
|
219
|
+
|
|
220
|
+
await waitFor(() => {
|
|
221
|
+
expect(screen.getByText('Admin layout shell')).toBeInTheDocument();
|
|
222
|
+
});
|
|
223
|
+
expect(activateLayout).toHaveBeenCalledTimes(1);
|
|
224
|
+
|
|
225
|
+
await act(async () => {
|
|
226
|
+
forceRerender();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(activateLayout).toHaveBeenCalledTimes(1);
|
|
230
|
+
expect(request).toHaveBeenCalledTimes(1);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should reactivate the guard when stable layout fields change', async () => {
|
|
234
|
+
const engine = new FlowEngine();
|
|
235
|
+
const request = vi.fn().mockResolvedValue({
|
|
236
|
+
data: {
|
|
237
|
+
data: [],
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
const routeRepository = new RouteRepository({
|
|
241
|
+
api: {
|
|
242
|
+
request,
|
|
243
|
+
resource: vi.fn(),
|
|
244
|
+
},
|
|
245
|
+
} as never);
|
|
246
|
+
const activateLayout = vi.spyOn(routeRepository, 'activateLayout');
|
|
247
|
+
engine.context.defineProperty('routeRepository', {
|
|
248
|
+
value: routeRepository,
|
|
249
|
+
});
|
|
250
|
+
engine.context.defineProperty('app', {
|
|
251
|
+
value: {
|
|
252
|
+
router: {
|
|
253
|
+
getBasename: () => '',
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
let layoutUid = 'admin-layout-model';
|
|
259
|
+
const model = {
|
|
260
|
+
get layout() {
|
|
261
|
+
return {
|
|
262
|
+
authCheck: true,
|
|
263
|
+
routeName: 'admin',
|
|
264
|
+
routePath: '/admin',
|
|
265
|
+
uid: layoutUid,
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
} as AdminLayoutModel;
|
|
269
|
+
let forceRerender = () => {};
|
|
270
|
+
const GuardShell = () => {
|
|
271
|
+
const [, setVersion] = React.useState(0);
|
|
272
|
+
forceRerender = () => setVersion((version) => version + 1);
|
|
273
|
+
return (
|
|
274
|
+
<AdminLayoutEntryGuard model={model}>
|
|
275
|
+
<div>Admin layout shell</div>
|
|
276
|
+
</AdminLayoutEntryGuard>
|
|
277
|
+
);
|
|
278
|
+
};
|
|
279
|
+
const router = createMemoryRouter(
|
|
280
|
+
[
|
|
281
|
+
{
|
|
282
|
+
path: '/admin',
|
|
283
|
+
id: 'admin',
|
|
284
|
+
element: (
|
|
285
|
+
<FlowEngineProvider engine={engine}>
|
|
286
|
+
<GuardShell />
|
|
287
|
+
</FlowEngineProvider>
|
|
288
|
+
),
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
{
|
|
292
|
+
initialEntries: ['/admin'],
|
|
293
|
+
},
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
render(<RouterProvider router={router} />);
|
|
297
|
+
|
|
298
|
+
await waitFor(() => {
|
|
299
|
+
expect(screen.getByText('Admin layout shell')).toBeInTheDocument();
|
|
300
|
+
});
|
|
301
|
+
expect(activateLayout).toHaveBeenCalledWith(expect.objectContaining({ uid: 'admin-layout-model' }));
|
|
302
|
+
|
|
303
|
+
await act(async () => {
|
|
304
|
+
layoutUid = 'admin-layout-next';
|
|
305
|
+
forceRerender();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
expect(activateLayout).toHaveBeenCalledTimes(2);
|
|
309
|
+
expect(activateLayout).toHaveBeenLastCalledWith(expect.objectContaining({ uid: 'admin-layout-next' }));
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('should redirect group id paths to the first v2 landing route', async () => {
|
|
313
|
+
const modernWindow = window as Window & { __nocobase_modern_client_prefix__?: string };
|
|
314
|
+
modernWindow.__nocobase_modern_client_prefix__ = 'v2';
|
|
315
|
+
const engine = new FlowEngine();
|
|
316
|
+
const request = vi.fn().mockResolvedValue({
|
|
317
|
+
data: {
|
|
318
|
+
data: [],
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
const routeRepository = new RouteRepository({
|
|
322
|
+
api: {
|
|
323
|
+
request,
|
|
324
|
+
resource: vi.fn(),
|
|
325
|
+
},
|
|
326
|
+
} as never);
|
|
327
|
+
routeRepository.setRoutes([
|
|
328
|
+
{
|
|
329
|
+
id: 1,
|
|
330
|
+
title: 'Group',
|
|
331
|
+
type: NocoBaseDesktopRouteType.group,
|
|
332
|
+
children: [
|
|
333
|
+
{
|
|
334
|
+
id: 11,
|
|
335
|
+
title: 'Flow page',
|
|
336
|
+
schemaUid: 'flow-page-1',
|
|
337
|
+
type: NocoBaseDesktopRouteType.flowPage,
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
]);
|
|
342
|
+
engine.context.defineProperty('routeRepository', {
|
|
343
|
+
value: routeRepository,
|
|
344
|
+
});
|
|
345
|
+
engine.context.defineProperty('app', {
|
|
346
|
+
value: {
|
|
347
|
+
getPublicPath: () => '/apps/demo/v2/',
|
|
348
|
+
router: {
|
|
349
|
+
getBasename: () => '/apps/demo/v2',
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const model = {
|
|
355
|
+
layout: {
|
|
356
|
+
routeName: 'admin',
|
|
357
|
+
routePath: '/admin',
|
|
358
|
+
uid: 'admin-layout-model',
|
|
359
|
+
},
|
|
360
|
+
} as AdminLayoutModel;
|
|
361
|
+
const router = createMemoryRouter(
|
|
362
|
+
[
|
|
363
|
+
{
|
|
364
|
+
path: '/admin',
|
|
365
|
+
id: 'admin',
|
|
366
|
+
element: (
|
|
367
|
+
<FlowEngineProvider engine={engine}>
|
|
368
|
+
<AdminLayoutEntryGuard model={model}>
|
|
369
|
+
<Outlet />
|
|
370
|
+
</AdminLayoutEntryGuard>
|
|
371
|
+
</FlowEngineProvider>
|
|
372
|
+
),
|
|
373
|
+
children: [
|
|
374
|
+
{
|
|
375
|
+
path: ':name',
|
|
376
|
+
id: 'admin.__page',
|
|
377
|
+
element: <div>Admin page shell</div>,
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
{
|
|
383
|
+
initialEntries: ['/admin/1'],
|
|
384
|
+
},
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
render(<RouterProvider router={router} />);
|
|
389
|
+
|
|
390
|
+
await waitFor(() => {
|
|
391
|
+
expect(router.state.location.pathname).toBe('/admin/flow-page-1');
|
|
392
|
+
});
|
|
393
|
+
expect(await screen.findByText('Admin page shell')).toBeInTheDocument();
|
|
394
|
+
} finally {
|
|
395
|
+
delete modernWindow.__nocobase_modern_client_prefix__;
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('should prefer schema uid routes before resolving numeric group ids', async () => {
|
|
400
|
+
const modernWindow = window as Window & { __nocobase_modern_client_prefix__?: string };
|
|
401
|
+
modernWindow.__nocobase_modern_client_prefix__ = 'v2';
|
|
402
|
+
const engine = new FlowEngine();
|
|
403
|
+
const routeRepository = {
|
|
404
|
+
activateLayout: vi.fn(() => vi.fn()),
|
|
405
|
+
ensureAccessibleLoaded: vi.fn().mockResolvedValue([]),
|
|
406
|
+
isAccessibleLoaded: vi.fn(() => true),
|
|
407
|
+
getRouteBySchemaUid: vi.fn((pageUid: string) =>
|
|
408
|
+
pageUid === '1'
|
|
409
|
+
? {
|
|
410
|
+
schemaUid: '1',
|
|
411
|
+
type: NocoBaseDesktopRouteType.flowPage,
|
|
412
|
+
}
|
|
413
|
+
: undefined,
|
|
414
|
+
),
|
|
415
|
+
getRouteById: vi.fn((routeId: string) =>
|
|
416
|
+
routeId === '1'
|
|
417
|
+
? {
|
|
418
|
+
id: 1,
|
|
419
|
+
type: NocoBaseDesktopRouteType.group,
|
|
420
|
+
children: [
|
|
421
|
+
{
|
|
422
|
+
schemaUid: 'flow-page-1',
|
|
423
|
+
type: NocoBaseDesktopRouteType.flowPage,
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
}
|
|
427
|
+
: undefined,
|
|
428
|
+
),
|
|
429
|
+
listAccessible: vi.fn(() => []),
|
|
430
|
+
};
|
|
431
|
+
engine.context.defineProperty('routeRepository', {
|
|
432
|
+
value: routeRepository,
|
|
433
|
+
});
|
|
434
|
+
engine.context.defineProperty('app', {
|
|
435
|
+
value: {
|
|
436
|
+
getPublicPath: () => '/apps/demo/v2/',
|
|
437
|
+
router: {
|
|
438
|
+
getBasename: () => '/apps/demo/v2',
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
const model = {
|
|
444
|
+
layout: {
|
|
445
|
+
routeName: 'admin',
|
|
446
|
+
routePath: '/admin',
|
|
447
|
+
uid: 'admin-layout-model',
|
|
448
|
+
},
|
|
449
|
+
} as AdminLayoutModel;
|
|
450
|
+
const router = createMemoryRouter(
|
|
451
|
+
[
|
|
452
|
+
{
|
|
453
|
+
path: '/admin',
|
|
454
|
+
id: 'admin',
|
|
455
|
+
element: (
|
|
456
|
+
<FlowEngineProvider engine={engine}>
|
|
457
|
+
<AdminLayoutEntryGuard model={model}>
|
|
458
|
+
<Outlet />
|
|
459
|
+
</AdminLayoutEntryGuard>
|
|
460
|
+
</FlowEngineProvider>
|
|
461
|
+
),
|
|
462
|
+
children: [
|
|
463
|
+
{
|
|
464
|
+
path: ':name',
|
|
465
|
+
id: 'admin.__page',
|
|
466
|
+
element: <div>Admin page shell</div>,
|
|
467
|
+
},
|
|
468
|
+
],
|
|
469
|
+
},
|
|
470
|
+
],
|
|
471
|
+
{
|
|
472
|
+
initialEntries: ['/admin/1'],
|
|
473
|
+
},
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
render(<RouterProvider router={router} />);
|
|
478
|
+
|
|
479
|
+
await screen.findByText('Admin page shell');
|
|
480
|
+
expect(router.state.location.pathname).toBe('/admin/1');
|
|
481
|
+
expect(routeRepository.getRouteById).not.toHaveBeenCalled();
|
|
482
|
+
} finally {
|
|
483
|
+
delete modernWindow.__nocobase_modern_client_prefix__;
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
});
|