@nocobase/flow-engine 2.0.0-alpha.4 → 2.0.0-alpha.40
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/lib/BlockScopedFlowEngine.d.ts +23 -0
- package/lib/BlockScopedFlowEngine.js +90 -0
- package/lib/FlowContextProvider.d.ts +2 -2
- package/lib/FlowContextProvider.js +3 -3
- package/lib/FlowDefinition.d.ts +4 -2
- package/lib/JSRunner.js +3 -0
- package/lib/ViewScopedFlowEngine.d.ts +1 -1
- package/lib/ViewScopedFlowEngine.js +12 -0
- package/lib/acl/Acl.d.ts +1 -0
- package/lib/acl/Acl.js +11 -0
- package/lib/components/FieldModelRenderer.js +14 -6
- package/lib/components/FieldSkeleton.d.ts +10 -0
- package/lib/components/FieldSkeleton.js +64 -0
- package/lib/components/FlowContextSelector.js +7 -2
- package/lib/components/FlowModelRenderer.d.ts +3 -6
- package/lib/components/FlowModelRenderer.js +16 -47
- package/lib/components/FormItem.js +5 -1
- package/lib/components/MobilePopup.d.ts +20 -0
- package/lib/components/MobilePopup.js +102 -0
- package/lib/components/MobilePopup.style.d.ts +17 -0
- package/lib/components/MobilePopup.style.js +186 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +3 -1
- package/lib/components/settings/independents/dropdown/FlowsDropdownButton.js +2 -2
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +17 -5
- package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +2 -2
- package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +2 -2
- package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +35 -6
- package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +7 -1
- package/lib/components/variables/VariableInput.js +19 -5
- package/lib/components/variables/VariableTag.js +43 -2
- package/lib/components/variables/types.d.ts +2 -0
- package/lib/data-source/index.d.ts +15 -4
- package/lib/data-source/index.js +42 -13
- package/lib/data-source/sortCollectionsByInherits.d.ts +10 -0
- package/lib/data-source/sortCollectionsByInherits.js +71 -0
- package/lib/emitter.d.ts +6 -0
- package/lib/emitter.js +12 -0
- package/lib/executor/FlowExecutor.d.ts +4 -5
- package/lib/executor/FlowExecutor.js +168 -99
- package/lib/flow-registry/GlobalFlowRegistry.d.ts +1 -0
- package/lib/flow-registry/GlobalFlowRegistry.js +3 -0
- package/lib/flow-registry/InstanceFlowRegistry.d.ts +1 -0
- package/lib/flow-registry/InstanceFlowRegistry.js +3 -0
- package/lib/flowContext.d.ts +37 -5
- package/lib/flowContext.js +215 -87
- package/lib/flowEngine.d.ts +24 -1
- package/lib/flowEngine.js +56 -6
- package/lib/flowSettings.d.ts +2 -1
- package/lib/flowSettings.js +12 -8
- package/lib/hooks/useApplyAutoFlows.js +2 -1
- package/lib/index.d.ts +7 -1
- package/lib/index.js +32 -3
- package/lib/locale/en-US.json +4 -2
- package/lib/locale/index.d.ts +4 -0
- package/lib/locale/zh-CN.json +4 -2
- package/lib/models/CollectionFieldModel.d.ts +3 -0
- package/lib/models/CollectionFieldModel.js +48 -5
- package/lib/models/flowModel.d.ts +30 -29
- package/lib/models/flowModel.js +135 -98
- package/lib/models/forkFlowModel.d.ts +8 -4
- package/lib/models/forkFlowModel.js +38 -8
- package/lib/provider.d.ts +3 -1
- package/lib/provider.js +7 -5
- package/lib/resources/multiRecordResource.js +25 -1
- package/lib/resources/singleRecordResource.js +19 -1
- package/lib/resources/sqlResource.d.ts +1 -0
- package/lib/resources/sqlResource.js +20 -24
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.d.ts +1 -6
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +27 -20
- package/lib/runjs-context/contexts/JSBlockRunJSContext.d.ts +1 -6
- package/lib/runjs-context/contexts/JSBlockRunJSContext.js +46 -33
- package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.d.ts +1 -2
- package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.js +14 -15
- package/lib/runjs-context/contexts/{LinkageRunJSContext.d.ts → JSColumnRunJSContext.d.ts} +6 -3
- package/lib/runjs-context/contexts/JSColumnRunJSContext.js +78 -0
- package/lib/runjs-context/contexts/JSFieldRunJSContext.d.ts +1 -6
- package/lib/runjs-context/contexts/JSFieldRunJSContext.js +28 -24
- package/lib/runjs-context/contexts/JSItemRunJSContext.d.ts +1 -6
- package/lib/runjs-context/contexts/JSItemRunJSContext.js +24 -20
- package/lib/runjs-context/contexts/JSRecordActionRunJSContext.d.ts +1 -2
- package/lib/runjs-context/contexts/JSRecordActionRunJSContext.js +16 -17
- package/lib/runjs-context/contexts/base.d.ts +9 -0
- package/lib/runjs-context/contexts/base.js +183 -0
- package/lib/runjs-context/helpers.d.ts +5 -2
- package/lib/runjs-context/helpers.js +36 -27
- package/lib/runjs-context/registry.d.ts +7 -4
- package/lib/runjs-context/registry.js +10 -42
- package/lib/runjs-context/setup.d.ts +9 -0
- package/lib/runjs-context/setup.js +82 -0
- package/lib/runjs-context/snippets/global/{copy-record-json.snippet.js → api-request.snippet.js} +25 -10
- package/lib/runjs-context/snippets/global/clipboard-copy-text.snippet.js +61 -0
- package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.js → import-esm.snippet.js} +26 -12
- package/lib/runjs-context/snippets/global/message-error.snippet.js +6 -0
- package/lib/runjs-context/snippets/global/message-success.snippet.js +6 -0
- package/lib/runjs-context/snippets/global/notification-open.snippet.d.ts +3 -8
- package/lib/runjs-context/snippets/global/notification-open.snippet.js +8 -1
- package/lib/runjs-context/snippets/global/open-view-dialog.snippet.js +10 -3
- package/lib/runjs-context/snippets/global/open-view-drawer.snippet.js +10 -3
- package/lib/runjs-context/snippets/global/query-selector.snippet.js +53 -0
- package/lib/runjs-context/snippets/global/require-amd.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/global/{requireAsync.snippet.js → require-amd.snippet.js} +16 -13
- package/lib/runjs-context/snippets/global/window-open.snippet.d.ts +3 -8
- package/lib/runjs-context/snippets/global/window-open.snippet.js +8 -1
- package/lib/runjs-context/snippets/index.d.ts +14 -3
- package/lib/runjs-context/snippets/index.js +161 -40
- package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.js +11 -2
- package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +64 -0
- package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +99 -0
- package/lib/runjs-context/snippets/{libs → scene/block}/echarts-init.snippet.js +24 -7
- package/lib/runjs-context/snippets/scene/block/render-button-handler.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsblock → block}/render-button-handler.snippet.js +17 -9
- package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +57 -0
- package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.js +71 -0
- package/lib/runjs-context/snippets/scene/block/render-react-jsx.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.js → block/render-react-jsx.snippet.js} +26 -13
- package/lib/runjs-context/snippets/scene/block/render-react.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsblock → block}/render-react.snippet.js +18 -17
- package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +95 -0
- package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +84 -0
- package/lib/runjs-context/snippets/scene/block/resource-example.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +60 -0
- package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +283 -0
- package/lib/runjs-context/snippets/scene/block/vue-component.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +124 -0
- package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsfield → detail}/color-by-value.snippet.js +13 -3
- package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/{global → scene/detail}/copy-to-clipboard.snippet.js +28 -6
- package/lib/runjs-context/snippets/scene/detail/format-number.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsfield → detail}/format-number.snippet.js +13 -3
- package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsfield → detail}/innerHTML-value.snippet.js +13 -3
- package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +82 -0
- package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +80 -0
- package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +74 -0
- package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.js +63 -0
- package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +81 -0
- package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.js +64 -0
- package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.js +74 -0
- package/lib/runjs-context/snippets/scene/form/render-basic.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.js +11 -2
- package/lib/runjs-context/snippets/scene/form/set-disabled.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.js +12 -3
- package/lib/runjs-context/snippets/scene/form/set-field-value.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.js +12 -3
- package/lib/runjs-context/snippets/scene/form/set-required.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.js +12 -3
- package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.js +67 -0
- package/lib/runjs-context/snippets/scene/form/toggle-visible.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.js +12 -3
- package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +64 -0
- package/lib/runjs-context/snippets/scene/table/collection-selected-count.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.js +11 -2
- package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +79 -0
- package/lib/runjs-context/snippets/scene/table/destroy-selected.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/{global/log-json-record.snippet.js → scene/table/destroy-selected.snippet.js} +24 -11
- package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.js +64 -0
- package/lib/runjs-context/snippets/scene/table/iterate-selected-rows.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.js +11 -2
- package/lib/runjs-context/snippets/types.d.ts +9 -1
- package/lib/scheduler/ModelOperationScheduler.d.ts +51 -0
- package/lib/scheduler/ModelOperationScheduler.js +262 -0
- package/lib/types.d.ts +28 -3
- package/lib/types.js +4 -3
- package/lib/utils/associationObjectVariable.d.ts +32 -0
- package/lib/utils/associationObjectVariable.js +157 -0
- package/lib/utils/buildSettingsViewInputArgs.d.ts +19 -0
- package/lib/utils/buildSettingsViewInputArgs.js +75 -0
- package/lib/utils/createEphemeralContext.d.ts +13 -0
- package/lib/utils/createEphemeralContext.js +140 -0
- package/lib/utils/flows.d.ts +10 -0
- package/lib/{runjs-context/snippets/global/api-request-post.snippet.js → utils/flows.js} +21 -15
- package/lib/utils/index.d.ts +7 -2
- package/lib/utils/index.js +19 -2
- package/lib/utils/jsxTransform.d.ts +15 -0
- package/lib/utils/jsxTransform.js +68 -0
- package/lib/utils/params-resolvers.js +3 -3
- package/lib/utils/parsePathnameToViewParams.d.ts +1 -1
- package/lib/utils/parsePathnameToViewParams.js +41 -5
- package/lib/utils/pruneFilter.d.ts +21 -0
- package/lib/{runjs-context/snippets/global/try-catch-async.snippet.js → utils/pruneFilter.js} +24 -16
- package/lib/utils/safeGlobals.d.ts +5 -3
- package/lib/utils/safeGlobals.js +40 -0
- package/lib/utils/schema-utils.js +2 -2
- package/lib/utils/serverContextParams.d.ts +1 -0
- package/lib/utils/translation.d.ts +4 -1
- package/lib/utils/translation.js +6 -2
- package/lib/utils/variablesParams.d.ts +9 -5
- package/lib/utils/variablesParams.js +47 -36
- package/lib/views/DrawerComponent.js +21 -2
- package/lib/views/PageComponent.js +2 -1
- package/lib/views/ViewNavigation.d.ts +3 -9
- package/lib/views/ViewNavigation.js +16 -2
- package/lib/views/createViewMeta.d.ts +29 -1
- package/lib/views/createViewMeta.js +338 -72
- package/lib/views/index.d.ts +1 -0
- package/lib/views/index.js +3 -0
- package/lib/views/useDialog.d.ts +8 -8
- package/lib/views/useDialog.js +8 -7
- package/lib/views/useDrawer.d.ts +8 -8
- package/lib/views/useDrawer.js +43 -28
- package/lib/views/usePage.d.ts +8 -8
- package/lib/views/usePage.js +8 -7
- package/package.json +6 -3
- package/src/BlockScopedFlowEngine.ts +86 -0
- package/src/FlowContextProvider.tsx +4 -2
- package/src/JSRunner.ts +3 -0
- package/src/ViewScopedFlowEngine.ts +15 -1
- package/src/__tests__/JSRunner.test.ts +62 -53
- package/src/__tests__/blockScopedFlowEngine.test.ts +154 -0
- package/src/__tests__/createViewMeta.popup.test.ts +132 -0
- package/src/__tests__/flow-engine.test.ts +3 -0
- package/src/__tests__/flowContext.test.ts +52 -0
- package/src/__tests__/flowContextCreateJSRunner.test.ts +163 -0
- package/src/__tests__/flowEngine.saveModel.test.ts +4 -0
- package/src/__tests__/flowRunJSContextDefine.test.ts +508 -0
- package/src/__tests__/flowSettings.open.test.tsx +2 -0
- package/src/__tests__/flowSettings.test.ts +2 -0
- package/src/__tests__/globalFlowRegistry.test.ts +1 -1
- package/src/__tests__/modelOperationScheduler.test.ts +346 -0
- package/src/__tests__/objectVariable.test.ts +464 -0
- package/src/__tests__/runjsContext.test.ts +216 -35
- package/src/__tests__/runjsContextImplementations.test.ts +217 -0
- package/src/__tests__/runjsContextRuntime.test.ts +269 -0
- package/src/__tests__/runjsEdgeCases.test.ts +281 -0
- package/src/__tests__/runjsLocales.test.ts +36 -0
- package/src/__tests__/runjsRuntimeFeatures.test.ts +449 -0
- package/src/__tests__/runjsSnippets.test.ts +140 -0
- package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
- package/src/acl/Acl.tsx +14 -0
- package/src/acl/__tests__/Acl.test.tsx +43 -0
- package/src/components/DynamicFlowsEditor.tsx +3 -4
- package/src/components/FieldModelRenderer.tsx +20 -7
- package/src/components/FieldSkeleton.tsx +27 -0
- package/src/components/FlowContextSelector.tsx +6 -2
- package/src/components/FlowModelRenderer.tsx +33 -81
- package/src/components/FormItem.tsx +8 -1
- package/src/components/MobilePopup.style.ts +220 -0
- package/src/components/MobilePopup.tsx +86 -0
- package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +6 -6
- package/src/components/index.ts +1 -0
- package/src/components/settings/independents/dropdown/FlowsDropdownButton.tsx +1 -1
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +17 -4
- package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +1 -1
- package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +39 -10
- package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +14 -1
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +425 -0
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +5 -7
- package/src/components/variables/VariableInput.tsx +25 -5
- package/src/components/variables/VariableTag.tsx +54 -2
- package/src/components/variables/types.ts +2 -0
- package/src/data-source/__tests__/sortCollectionsByInherits.test.ts +125 -0
- package/src/data-source/index.ts +42 -11
- package/src/data-source/sortCollectionsByInherits.ts +61 -0
- package/src/emitter.ts +14 -0
- package/src/executor/FlowExecutor.ts +213 -119
- package/src/executor/__tests__/ctx-defs-injection.test.ts +197 -0
- package/src/executor/__tests__/flowExecutor.test.ts +151 -5
- package/src/flow-registry/GlobalFlowRegistry.ts +1 -0
- package/src/flow-registry/InstanceFlowRegistry.ts +1 -0
- package/src/flow-registry/__tests__/globalFlowRegistry.test.ts +54 -0
- package/src/flowContext.ts +290 -104
- package/src/flowEngine.ts +74 -6
- package/src/flowSettings.ts +9 -4
- package/src/hooks/useApplyAutoFlows.ts +3 -1
- package/src/index.ts +12 -1
- package/src/locale/en-US.json +4 -2
- package/src/locale/zh-CN.json +4 -2
- package/src/models/CollectionFieldModel.tsx +56 -7
- package/src/models/__tests__/dispatchEvent.behavior.test.ts +169 -0
- package/src/models/__tests__/flowModel.getFlows.sort.test.ts +33 -9
- package/src/models/__tests__/flowModel.scheduleModelOperation.test.tsx +129 -0
- package/src/models/__tests__/flowModel.test.ts +234 -111
- package/src/models/__tests__/forkFlowModel.test.ts +40 -7
- package/src/models/flowModel.tsx +180 -132
- package/src/models/forkFlowModel.ts +48 -8
- package/src/provider.tsx +10 -7
- package/src/resources/multiRecordResource.ts +28 -1
- package/src/resources/singleRecordResource.ts +19 -1
- package/src/resources/sqlResource.ts +20 -25
- package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +28 -21
- package/src/runjs-context/contexts/JSBlockRunJSContext.ts +46 -34
- package/src/runjs-context/contexts/JSCollectionActionRunJSContext.ts +15 -16
- package/src/runjs-context/contexts/JSColumnRunJSContext.ts +58 -0
- package/src/runjs-context/contexts/JSFieldRunJSContext.ts +30 -25
- package/src/runjs-context/contexts/JSItemRunJSContext.ts +25 -21
- package/src/runjs-context/contexts/JSRecordActionRunJSContext.ts +17 -18
- package/src/runjs-context/contexts/base.ts +171 -0
- package/src/runjs-context/helpers.ts +32 -30
- package/src/runjs-context/registry.ts +16 -47
- package/src/runjs-context/setup.ts +51 -0
- package/src/runjs-context/snippets/global/api-request.snippet.ts +38 -0
- package/src/runjs-context/snippets/global/clipboard-copy-text.snippet.ts +42 -0
- package/src/runjs-context/snippets/global/import-esm.snippet.ts +39 -0
- package/src/runjs-context/snippets/global/message-error.snippet.ts +6 -0
- package/src/runjs-context/snippets/global/message-success.snippet.ts +6 -0
- package/src/runjs-context/snippets/global/notification-open.snippet.ts +11 -1
- package/src/runjs-context/snippets/global/open-view-dialog.snippet.ts +10 -3
- package/src/runjs-context/snippets/global/open-view-drawer.snippet.ts +10 -3
- package/src/runjs-context/snippets/global/query-selector.snippet.ts +34 -0
- package/src/runjs-context/snippets/global/require-amd.snippet.ts +30 -0
- package/src/runjs-context/snippets/global/window-open.snippet.ts +11 -1
- package/src/runjs-context/snippets/index.ts +177 -39
- package/src/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.ts +14 -2
- package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +45 -0
- package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +80 -0
- package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +44 -0
- package/src/runjs-context/snippets/scene/block/render-button-handler.snippet.ts +35 -0
- package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +38 -0
- package/src/runjs-context/snippets/scene/block/render-info-card.snippet.ts +52 -0
- package/src/runjs-context/snippets/scene/block/render-react-jsx.snippet.ts +39 -0
- package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +38 -0
- package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +76 -0
- package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +65 -0
- package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +46 -0
- package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +264 -0
- package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +105 -0
- package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +33 -0
- package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +45 -0
- package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +32 -0
- package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +31 -0
- package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +63 -0
- package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +61 -0
- package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +55 -0
- package/src/runjs-context/snippets/scene/form/calculate-total.snippet.ts +44 -0
- package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +62 -0
- package/src/runjs-context/snippets/scene/form/conditional-required.snippet.ts +45 -0
- package/src/runjs-context/snippets/scene/form/copy-field-values.snippet.ts +55 -0
- package/src/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.ts +14 -2
- package/src/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.ts +15 -3
- package/src/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.ts +15 -3
- package/src/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.ts +15 -3
- package/src/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.ts +48 -0
- package/src/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.ts +15 -3
- package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +45 -0
- package/src/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.ts +14 -2
- package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +60 -0
- package/src/runjs-context/snippets/scene/table/destroy-selected.snippet.ts +36 -0
- package/src/runjs-context/snippets/scene/table/export-selected-json.snippet.ts +45 -0
- package/src/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.ts +14 -2
- package/src/runjs-context/snippets/types.ts +5 -1
- package/src/scheduler/ModelOperationScheduler.ts +304 -0
- package/src/types.ts +34 -0
- package/src/utils/__tests__/flows.test.ts +65 -0
- package/src/utils/__tests__/jsxTransform.test.ts +38 -0
- package/src/utils/__tests__/parsePathnameToViewParams.test.ts +25 -0
- package/src/utils/__tests__/pruneFilter.test.ts +38 -0
- package/src/utils/__tests__/safeGlobals.test.ts +22 -1
- package/src/utils/associationObjectVariable.ts +180 -0
- package/src/utils/buildSettingsViewInputArgs.ts +72 -0
- package/src/utils/createEphemeralContext.ts +142 -0
- package/src/utils/flows.ts +23 -0
- package/src/utils/index.ts +10 -2
- package/src/utils/jsxTransform.ts +39 -0
- package/src/utils/params-resolvers.ts +2 -2
- package/src/utils/parsePathnameToViewParams.ts +50 -6
- package/src/utils/pruneFilter.ts +41 -0
- package/src/utils/safeGlobals.ts +49 -3
- package/src/utils/schema-utils.ts +1 -1
- package/src/utils/serverContextParams.ts +1 -0
- package/src/utils/translation.ts +7 -2
- package/src/utils/variablesParams.ts +50 -38
- package/src/views/DrawerComponent.tsx +21 -2
- package/src/views/PageComponent.tsx +1 -1
- package/src/views/ViewNavigation.ts +20 -12
- package/src/views/__tests__/ViewNavigation.test.ts +10 -0
- package/src/views/createViewMeta.ts +393 -70
- package/src/views/index.tsx +1 -0
- package/src/views/useDialog.tsx +12 -10
- package/src/views/useDrawer.tsx +62 -37
- package/src/views/usePage.tsx +13 -10
- package/lib/runjs-context/contexts/FlowRunJSContext.d.ts +0 -38
- package/lib/runjs-context/contexts/FlowRunJSContext.js +0 -217
- package/lib/runjs-context/contexts/LinkageRunJSContext.js +0 -62
- package/lib/runjs-context/index.d.ts +0 -19
- package/lib/runjs-context/index.js +0 -57
- package/lib/runjs-context/snippets/global/api-request-get.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/global/api-request-get.snippet.js +0 -42
- package/lib/runjs-context/snippets/global/api-request-post.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/global/console-log-ctx.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/global/console-log-ctx.snippet.js +0 -41
- package/lib/runjs-context/snippets/global/requireAsync.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/global/sleep.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/global/sleep.snippet.js +0 -43
- package/lib/runjs-context/snippets/global/try-catch-async.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/libs/echarts-init.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/actions/collection-selected-count.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/actions/iterate-selected-rows.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.js +0 -43
- package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.js +0 -40
- package/lib/runjs-context/snippets/scene/jsblock/add-event-listener.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.js +0 -42
- package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.js +0 -46
- package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.js +0 -41
- package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.js +0 -41
- package/lib/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/render-react.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsfield/color-by-value.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsfield/format-number.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsitem/render-basic.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/linkage/set-disabled.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/linkage/set-field-value.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/linkage/set-required.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/linkage/toggle-visible.snippet.d.ts +0 -15
- package/src/runjs-context/contexts/FlowRunJSContext.ts +0 -190
- package/src/runjs-context/contexts/LinkageRunJSContext.ts +0 -35
- package/src/runjs-context/index.ts +0 -20
- package/src/runjs-context/snippets/global/api-request-get.snippet.ts +0 -20
- package/src/runjs-context/snippets/global/api-request-post.snippet.ts +0 -20
- package/src/runjs-context/snippets/global/console-log-ctx.snippet.ts +0 -19
- package/src/runjs-context/snippets/global/copy-record-json.snippet.ts +0 -21
- package/src/runjs-context/snippets/global/copy-to-clipboard.snippet.ts +0 -21
- package/src/runjs-context/snippets/global/log-json-record.snippet.ts +0 -21
- package/src/runjs-context/snippets/global/requireAsync.snippet.ts +0 -24
- package/src/runjs-context/snippets/global/sleep.snippet.ts +0 -21
- package/src/runjs-context/snippets/global/try-catch-async.snippet.ts +0 -22
- package/src/runjs-context/snippets/global/view-navigation-push.snippet.ts +0 -23
- package/src/runjs-context/snippets/libs/echarts-init.snippet.ts +0 -24
- package/src/runjs-context/snippets/scene/actions/record-id-message.snippet.ts +0 -21
- package/src/runjs-context/snippets/scene/actions/run-action-basic.snippet.ts +0 -18
- package/src/runjs-context/snippets/scene/jsblock/append-style.snippet.ts +0 -20
- package/src/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.ts +0 -24
- package/src/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.ts +0 -19
- package/src/runjs-context/snippets/scene/jsblock/render-basic.snippet.ts +0 -24
- package/src/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.ts +0 -24
- package/src/runjs-context/snippets/scene/jsblock/render-card.snippet.ts +0 -30
- package/src/runjs-context/snippets/scene/jsblock/render-react.snippet.ts +0 -34
- package/src/runjs-context/snippets/scene/jsfield/color-by-value.snippet.ts +0 -20
- package/src/runjs-context/snippets/scene/jsfield/format-number.snippet.ts +0 -19
- package/src/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.ts +0 -18
- /package/lib/runjs-context/snippets/global/{copy-record-json.snippet.d.ts → api-request.snippet.d.ts} +0 -0
- /package/lib/runjs-context/snippets/global/{copy-to-clipboard.snippet.d.ts → clipboard-copy-text.snippet.d.ts} +0 -0
- /package/lib/runjs-context/snippets/global/{log-json-record.snippet.d.ts → import-esm.snippet.d.ts} +0 -0
- /package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.d.ts → query-selector.snippet.d.ts} +0 -0
- /package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.d.ts → block/echarts-init.snippet.d.ts} +0 -0
|
@@ -13,7 +13,6 @@ import { render, cleanup, waitFor } from '@testing-library/react';
|
|
|
13
13
|
import { App, ConfigProvider } from 'antd';
|
|
14
14
|
import { FlowEngine } from '../../flowEngine';
|
|
15
15
|
import { FlowModel, ModelRenderMode } from '../../models/flowModel';
|
|
16
|
-
import { DefaultSettingsIcon } from '../settings/wrappers/contextual/DefaultSettingsIcon';
|
|
17
16
|
import { FlowEngineProvider } from '../../provider';
|
|
18
17
|
import { FlowModelRenderer } from '../FlowModelRenderer';
|
|
19
18
|
|
|
@@ -81,6 +80,7 @@ vi.mock('antd', () => {
|
|
|
81
80
|
Alert,
|
|
82
81
|
Skeleton,
|
|
83
82
|
Spin,
|
|
83
|
+
theme: { useToken: () => ({}) },
|
|
84
84
|
} as any;
|
|
85
85
|
});
|
|
86
86
|
|
|
@@ -124,7 +124,7 @@ describe('Delete problematic model via FlowSettings menu', () => {
|
|
|
124
124
|
<ConfigProvider>
|
|
125
125
|
<App>
|
|
126
126
|
<FlowEngineProvider engine={engine}>
|
|
127
|
-
<FlowModelRenderer model={model} showFlowSettings showErrorFallback
|
|
127
|
+
<FlowModelRenderer model={model} showFlowSettings showErrorFallback />
|
|
128
128
|
</FlowEngineProvider>
|
|
129
129
|
</App>
|
|
130
130
|
</ConfigProvider>,
|
|
@@ -141,7 +141,7 @@ describe('Delete problematic model via FlowSettings menu', () => {
|
|
|
141
141
|
return (
|
|
142
142
|
<div>
|
|
143
143
|
{items.map((m: FlowModel) => (
|
|
144
|
-
<FlowModelRenderer key={m.uid} model={m} showFlowSettings showErrorFallback
|
|
144
|
+
<FlowModelRenderer key={m.uid} model={m} showFlowSettings showErrorFallback />
|
|
145
145
|
))}
|
|
146
146
|
</div>
|
|
147
147
|
);
|
|
@@ -167,7 +167,7 @@ describe('Delete problematic model via FlowSettings menu', () => {
|
|
|
167
167
|
<ConfigProvider>
|
|
168
168
|
<App>
|
|
169
169
|
<FlowEngineProvider engine={engine}>
|
|
170
|
-
<FlowModelRenderer model={parent}
|
|
170
|
+
<FlowModelRenderer model={parent} />
|
|
171
171
|
</FlowEngineProvider>
|
|
172
172
|
</App>
|
|
173
173
|
</ConfigProvider>,
|
|
@@ -186,7 +186,7 @@ describe('Delete problematic model via FlowSettings menu', () => {
|
|
|
186
186
|
return (
|
|
187
187
|
<div>
|
|
188
188
|
{cells.map((m: FlowModel) => (
|
|
189
|
-
<FlowModelRenderer key={m.uid} model={m} showFlowSettings showErrorFallback
|
|
189
|
+
<FlowModelRenderer key={m.uid} model={m} showFlowSettings showErrorFallback />
|
|
190
190
|
))}
|
|
191
191
|
</div>
|
|
192
192
|
);
|
|
@@ -212,7 +212,7 @@ describe('Delete problematic model via FlowSettings menu', () => {
|
|
|
212
212
|
<ConfigProvider>
|
|
213
213
|
<App>
|
|
214
214
|
<FlowEngineProvider engine={engine}>
|
|
215
|
-
<FlowModelRenderer model={parent}
|
|
215
|
+
<FlowModelRenderer model={parent} />
|
|
216
216
|
</FlowEngineProvider>
|
|
217
217
|
</App>
|
|
218
218
|
</ConfigProvider>,
|
package/src/components/index.ts
CHANGED
|
@@ -132,7 +132,7 @@ const FlowsDropdownButtonWithModel: React.FC<ModelProvidedProps> = observer(
|
|
|
132
132
|
// 如果step使用了action,也获取action的uiSchema
|
|
133
133
|
let actionUiSchema = {};
|
|
134
134
|
if (actionStep.use) {
|
|
135
|
-
const action = model.
|
|
135
|
+
const action = model.getAction?.(actionStep.use);
|
|
136
136
|
if (action && action.uiSchema) {
|
|
137
137
|
actionUiSchema = action.uiSchema;
|
|
138
138
|
}
|
|
@@ -126,7 +126,19 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
126
126
|
message.success(t('UID copied to clipboard'));
|
|
127
127
|
} catch (error) {
|
|
128
128
|
console.error(t('Copy failed'), ':', error);
|
|
129
|
-
|
|
129
|
+
// 如果不是 HTTPS 协议,给出更具体的提示:HTTP 下剪贴板 API 不可用
|
|
130
|
+
const isHttps = typeof window !== 'undefined' && window.location?.protocol === 'https:';
|
|
131
|
+
if (!isHttps) {
|
|
132
|
+
message.error(
|
|
133
|
+
t(
|
|
134
|
+
'Copy failed under HTTP. Clipboard API is unavailable on non-HTTPS pages. Please copy [{{uid}}] manually.',
|
|
135
|
+
{ uid },
|
|
136
|
+
),
|
|
137
|
+
);
|
|
138
|
+
return;
|
|
139
|
+
} else {
|
|
140
|
+
message.error(t('Copy failed, please copy [{{uid}}] manually.', { uid }));
|
|
141
|
+
}
|
|
130
142
|
}
|
|
131
143
|
},
|
|
132
144
|
[message, t],
|
|
@@ -255,7 +267,8 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
255
267
|
const getModelConfigurableFlowsAndSteps = useCallback(
|
|
256
268
|
async (targetModel: FlowModel, modelKey?: string): Promise<FlowInfo[]> => {
|
|
257
269
|
try {
|
|
258
|
-
|
|
270
|
+
// 仅使用静态流(类级全局注册的 flows),排除实例动态流
|
|
271
|
+
const flows = (targetModel.constructor as typeof FlowModel).globalFlowRegistry.getFlows();
|
|
259
272
|
|
|
260
273
|
const flowsArray = Array.from(flows.values());
|
|
261
274
|
|
|
@@ -278,9 +291,9 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
278
291
|
let stepTitle = actionStep.title;
|
|
279
292
|
if (actionStep.use) {
|
|
280
293
|
try {
|
|
281
|
-
const action = targetModel.
|
|
294
|
+
const action = targetModel.getAction?.(actionStep.use);
|
|
282
295
|
hasActionUiSchema = action && action.uiSchema != null;
|
|
283
|
-
stepTitle = stepTitle || action
|
|
296
|
+
stepTitle = stepTitle || action?.title;
|
|
284
297
|
} catch (error) {
|
|
285
298
|
console.warn(t('Failed to get action {{action}}', { action: actionStep.use }), ':', error);
|
|
286
299
|
}
|
|
@@ -162,7 +162,7 @@ const FlowsContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|
|
162
162
|
// 如果step使用了action,也获取action的uiSchema
|
|
163
163
|
let actionUiSchema = {};
|
|
164
164
|
if (actionStep.use) {
|
|
165
|
-
const action = model.
|
|
165
|
+
const action = model.getAction?.(actionStep.use);
|
|
166
166
|
if (action && action.uiSchema) {
|
|
167
167
|
actionUiSchema = action.uiSchema;
|
|
168
168
|
}
|
|
@@ -80,8 +80,29 @@ const renderToolbarItems = (
|
|
|
80
80
|
});
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
+
// Width in pixels per toolbar item (icon width + spacing)
|
|
84
|
+
const TOOLBAR_ITEM_WIDTH = 19;
|
|
85
|
+
|
|
86
|
+
const toolbarPositionToCSS = {
|
|
87
|
+
inside: `
|
|
88
|
+
top: 2px;
|
|
89
|
+
`,
|
|
90
|
+
above: `
|
|
91
|
+
top: 0px;
|
|
92
|
+
transform: translateY(-100%);
|
|
93
|
+
padding-bottom: 0px;
|
|
94
|
+
margin-bottom: -2px;
|
|
95
|
+
`,
|
|
96
|
+
below: `
|
|
97
|
+
top: 0px;
|
|
98
|
+
transform: translateY(100%);
|
|
99
|
+
padding-top: 2px;
|
|
100
|
+
margin-top: -2px;
|
|
101
|
+
`,
|
|
102
|
+
};
|
|
103
|
+
|
|
83
104
|
// 使用与 NocoBase 一致的悬浮工具栏样式
|
|
84
|
-
const floatContainerStyles = ({ showBackground, showBorder, ctx, toolbarPosition }) => css`
|
|
105
|
+
const floatContainerStyles = ({ showBackground, showBorder, ctx, toolbarPosition = 'inside', toolbarCount }) => css`
|
|
85
106
|
position: relative;
|
|
86
107
|
display: inline;
|
|
87
108
|
|
|
@@ -117,6 +138,7 @@ const floatContainerStyles = ({ showBackground, showBorder, ctx, toolbarPosition
|
|
|
117
138
|
border: ${showBorder ? '2px solid var(--colorBorderSettingsHover)' : ''};
|
|
118
139
|
border-radius: ${ctx.themeToken.borderRadiusLG}px;
|
|
119
140
|
pointer-events: none;
|
|
141
|
+
min-width: ${TOOLBAR_ITEM_WIDTH * toolbarCount}px;
|
|
120
142
|
|
|
121
143
|
&.nb-in-template {
|
|
122
144
|
background: var(--colorTemplateBgSettingsHover);
|
|
@@ -147,10 +169,7 @@ const floatContainerStyles = ({ showBackground, showBorder, ctx, toolbarPosition
|
|
|
147
169
|
display: none; // 防止遮挡其它 icons
|
|
148
170
|
position: absolute;
|
|
149
171
|
right: 2px;
|
|
150
|
-
|
|
151
|
-
${toolbarPosition === 'above' ? 'transform: translateY(-100%);' : ''}
|
|
152
|
-
${toolbarPosition === 'above' ? 'padding-bottom: 2px;' : ''}
|
|
153
|
-
${toolbarPosition === 'above' ? 'margin-bottom: -2px;' : ''}
|
|
172
|
+
${toolbarPositionToCSS[toolbarPosition] || ''}
|
|
154
173
|
line-height: 16px;
|
|
155
174
|
pointer-events: all;
|
|
156
175
|
|
|
@@ -297,7 +316,7 @@ interface ModelProvidedProps {
|
|
|
297
316
|
/**
|
|
298
317
|
* @default 'inside'
|
|
299
318
|
*/
|
|
300
|
-
toolbarPosition?: 'inside' | 'above';
|
|
319
|
+
toolbarPosition?: 'inside' | 'above' | 'below';
|
|
301
320
|
}
|
|
302
321
|
|
|
303
322
|
interface ModelByIdProps {
|
|
@@ -332,7 +351,7 @@ interface ModelByIdProps {
|
|
|
332
351
|
/**
|
|
333
352
|
* @default 'inside'
|
|
334
353
|
*/
|
|
335
|
-
toolbarPosition?: 'inside' | 'above';
|
|
354
|
+
toolbarPosition?: 'inside' | 'above' | 'below';
|
|
336
355
|
}
|
|
337
356
|
|
|
338
357
|
type FlowsFloatContextMenuProps = ModelProvidedProps | ModelByIdProps;
|
|
@@ -565,9 +584,13 @@ const FlowsFloatContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|
|
565
584
|
return (
|
|
566
585
|
<div
|
|
567
586
|
ref={containerRef}
|
|
568
|
-
className={`${floatContainerStyles({
|
|
569
|
-
|
|
570
|
-
|
|
587
|
+
className={`${floatContainerStyles({
|
|
588
|
+
showBackground,
|
|
589
|
+
showBorder,
|
|
590
|
+
ctx: model.context,
|
|
591
|
+
toolbarPosition,
|
|
592
|
+
toolbarCount: getToolbarCount(flowEngine, extraToolbarItems),
|
|
593
|
+
})} ${hideMenu ? 'hide-parent-menu' : ''} ${hasButton ? 'has-button-child' : ''} ${className || ''}`}
|
|
571
594
|
style={containerStyle}
|
|
572
595
|
data-has-float-menu="true"
|
|
573
596
|
onMouseMove={handleChildHover}
|
|
@@ -653,3 +676,9 @@ const FlowsFloatContextMenuWithModelById: React.FC<ModelByIdProps> = observer(
|
|
|
653
676
|
);
|
|
654
677
|
|
|
655
678
|
export { FlowsFloatContextMenu };
|
|
679
|
+
|
|
680
|
+
function getToolbarCount(flowEngine, extraToolbarItems) {
|
|
681
|
+
const toolbarItems = flowEngine?.flowSettings?.getToolbarItems?.() || [];
|
|
682
|
+
const allToolbarItems = [...toolbarItems, ...(extraToolbarItems || [])];
|
|
683
|
+
return allToolbarItems.length;
|
|
684
|
+
}
|
|
@@ -14,7 +14,14 @@ import { Button, Space } from 'antd';
|
|
|
14
14
|
import React, { useEffect } from 'react';
|
|
15
15
|
import { FlowSettingsContextProvider, useFlowSettingsContext } from '../../../../hooks/useFlowSettingsContext';
|
|
16
16
|
import { StepSettingsDialogProps } from '../../../../types';
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
compileUiSchema,
|
|
19
|
+
FlowExitException,
|
|
20
|
+
getT,
|
|
21
|
+
resolveDefaultParams,
|
|
22
|
+
resolveStepUiSchema,
|
|
23
|
+
buildSettingsViewInputArgs,
|
|
24
|
+
} from '../../../../utils';
|
|
18
25
|
|
|
19
26
|
const SchemaField = createSchemaField();
|
|
20
27
|
|
|
@@ -135,6 +142,12 @@ const openStepSettingsDialog = async ({
|
|
|
135
142
|
width: dialogWidth,
|
|
136
143
|
destroyOnClose: true,
|
|
137
144
|
...toJS(uiModeProps),
|
|
145
|
+
// 透传 navigation,便于变量元信息根据真实视图栈推断父级弹窗
|
|
146
|
+
inputArgs: buildSettingsViewInputArgs(
|
|
147
|
+
model as any,
|
|
148
|
+
{ ...(toJS(uiModeProps)?.inputArgs || {}), __isSettingsPopup: true },
|
|
149
|
+
{ navigationOverride: ctx?.view?.navigation },
|
|
150
|
+
),
|
|
138
151
|
onClose: () => {
|
|
139
152
|
if (cleanup) {
|
|
140
153
|
cleanup();
|
|
@@ -0,0 +1,425 @@
|
|
|
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 React from 'react';
|
|
11
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
12
|
+
import { render, cleanup, waitFor } from '@testing-library/react';
|
|
13
|
+
import { App, ConfigProvider } from 'antd';
|
|
14
|
+
|
|
15
|
+
import { FlowEngine } from '../../../../../flowEngine';
|
|
16
|
+
import { FlowModel } from '../../../../../models/flowModel';
|
|
17
|
+
import { DefaultSettingsIcon } from '../DefaultSettingsIcon';
|
|
18
|
+
|
|
19
|
+
// ---- Mock antd to capture Dropdown menu props ----
|
|
20
|
+
const dropdownMenus: any[] = [];
|
|
21
|
+
vi.mock('antd', async (importOriginal) => {
|
|
22
|
+
const Dropdown = (props: any) => {
|
|
23
|
+
(globalThis as any).__lastDropdownMenu = props.menu;
|
|
24
|
+
dropdownMenus.push(props.menu);
|
|
25
|
+
return React.createElement('span', { 'data-testid': 'dropdown' }, props.children);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const App = Object.assign(({ children }: any) => React.createElement(React.Fragment, null, children), {
|
|
29
|
+
useApp: () => ({ message: { success: () => {}, error: () => {}, info: () => {} } }),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const ConfigProvider = ({ children }: any) => React.createElement(React.Fragment, null, children);
|
|
33
|
+
const Modal = {
|
|
34
|
+
confirm: (opts: any) => {
|
|
35
|
+
if (opts && typeof opts.onOk === 'function') return opts.onOk();
|
|
36
|
+
},
|
|
37
|
+
error: vi.fn(),
|
|
38
|
+
};
|
|
39
|
+
const Typography = {
|
|
40
|
+
Paragraph: ({ children }: any) => React.createElement('p', null, children ?? 'Paragraph'),
|
|
41
|
+
Text: ({ children }: any) => React.createElement('span', null, children ?? 'Text'),
|
|
42
|
+
};
|
|
43
|
+
const Collapse = Object.assign((props: any) => React.createElement('div', null, props.children ?? 'Collapse'), {
|
|
44
|
+
Panel: (props: any) => React.createElement('div', null, props.children ?? 'Panel'),
|
|
45
|
+
});
|
|
46
|
+
const Space = ({ children }: any) => React.createElement('div', null, children);
|
|
47
|
+
const FormItem = (props: any) => React.createElement('div', null, props.children ?? 'FormItem');
|
|
48
|
+
const Form = Object.assign((props: any) => React.createElement('form', null, props.children ?? 'Form'), {
|
|
49
|
+
Item: FormItem,
|
|
50
|
+
useForm: () => [{ setFieldsValue: (_: any) => {} }],
|
|
51
|
+
});
|
|
52
|
+
const Input: any = (props: any) => React.createElement('input', props);
|
|
53
|
+
Input.TextArea = (props: any) => React.createElement('textarea', props);
|
|
54
|
+
const InputNumber = (props: any) => React.createElement('input', { ...props, type: 'number' });
|
|
55
|
+
const Select = (props: any) => React.createElement('select', props);
|
|
56
|
+
const Switch = (props: any) => React.createElement('input', { ...props, type: 'checkbox' });
|
|
57
|
+
const Alert = (props: any) => React.createElement('div', { role: 'alert' }, props.message ?? 'Alert');
|
|
58
|
+
const Button = (props: any) => React.createElement('button', props, props.children ?? 'Button');
|
|
59
|
+
const Result = (props: any) => React.createElement('div', null, props.children ?? 'Result');
|
|
60
|
+
|
|
61
|
+
// Keep other components from original mock/default
|
|
62
|
+
return {
|
|
63
|
+
Dropdown,
|
|
64
|
+
App,
|
|
65
|
+
ConfigProvider,
|
|
66
|
+
Modal,
|
|
67
|
+
Typography,
|
|
68
|
+
Collapse,
|
|
69
|
+
Space,
|
|
70
|
+
Form,
|
|
71
|
+
Input,
|
|
72
|
+
InputNumber,
|
|
73
|
+
Select,
|
|
74
|
+
Switch,
|
|
75
|
+
Alert,
|
|
76
|
+
Button,
|
|
77
|
+
Result,
|
|
78
|
+
theme: { useToken: () => ({}) },
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('DefaultSettingsIcon - only static flows are shown', () => {
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
dropdownMenus.length = 0;
|
|
85
|
+
(globalThis as any).__lastDropdownMenu = undefined;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
afterEach(() => {
|
|
89
|
+
cleanup();
|
|
90
|
+
vi.clearAllMocks();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('excludes instance (dynamic) flows from the settings menu', async () => {
|
|
94
|
+
class TestFlowModel extends FlowModel {}
|
|
95
|
+
|
|
96
|
+
const engine = new FlowEngine();
|
|
97
|
+
const model = new TestFlowModel({ uid: 'model-static-only', flowEngine: engine });
|
|
98
|
+
|
|
99
|
+
// register one static flow with a visible step
|
|
100
|
+
TestFlowModel.registerFlow({
|
|
101
|
+
key: 'static1',
|
|
102
|
+
title: 'Static Flow',
|
|
103
|
+
steps: {
|
|
104
|
+
general: {
|
|
105
|
+
title: 'General',
|
|
106
|
+
uiSchema: {
|
|
107
|
+
field: { type: 'string', 'x-component': 'Input' },
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// add a dynamic (instance) flow which should NOT appear in menu
|
|
114
|
+
model.flowRegistry.addFlow('dyn1', {
|
|
115
|
+
title: 'Dynamic Flow',
|
|
116
|
+
steps: {
|
|
117
|
+
general: {
|
|
118
|
+
title: 'General (Dyn)',
|
|
119
|
+
uiSchema: {
|
|
120
|
+
field: { type: 'string', 'x-component': 'Input' },
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
render(
|
|
127
|
+
React.createElement(
|
|
128
|
+
ConfigProvider as any,
|
|
129
|
+
null,
|
|
130
|
+
React.createElement(
|
|
131
|
+
App as any,
|
|
132
|
+
null,
|
|
133
|
+
React.createElement(DefaultSettingsIcon as any, {
|
|
134
|
+
model,
|
|
135
|
+
// 关闭常用操作,避免干扰断言
|
|
136
|
+
showDeleteButton: false,
|
|
137
|
+
showCopyUidButton: false,
|
|
138
|
+
}),
|
|
139
|
+
),
|
|
140
|
+
),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// 等待菜单内出现静态流分组,确保异步加载完成
|
|
144
|
+
await waitFor(() => {
|
|
145
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
146
|
+
expect(menu).toBeTruthy();
|
|
147
|
+
const items = (menu?.items || []) as any[];
|
|
148
|
+
const groupLabels = items.filter((it) => it.type === 'group').map((it) => String(it.label));
|
|
149
|
+
expect(groupLabels).toContain('Static Flow');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
153
|
+
const items = (menu?.items || []) as any[];
|
|
154
|
+
|
|
155
|
+
// groups for flows are labeled with flow.title; ensure static group exists, dynamic group不存在
|
|
156
|
+
const groupLabels = items.filter((it) => it.type === 'group').map((it) => String(it.label));
|
|
157
|
+
expect(groupLabels).toContain('Static Flow');
|
|
158
|
+
expect(groupLabels).not.toContain('Dynamic Flow');
|
|
159
|
+
|
|
160
|
+
// 静态流的 step 存在(key: `${flowKey}:${stepKey}`),动态流 step 不存在
|
|
161
|
+
expect(items.some((it) => String(it.key || '').startsWith('static1:'))).toBe(true);
|
|
162
|
+
expect(items.some((it) => String(it.key || '').startsWith('dyn1:'))).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('filters out steps with hideInSettings and keeps visible ones', async () => {
|
|
166
|
+
class TestFlowModel extends FlowModel {}
|
|
167
|
+
const engine = new FlowEngine();
|
|
168
|
+
const model = new TestFlowModel({ uid: 'm-hide', flowEngine: engine });
|
|
169
|
+
|
|
170
|
+
TestFlowModel.registerFlow({
|
|
171
|
+
key: 'flowA',
|
|
172
|
+
title: 'Flow A',
|
|
173
|
+
steps: {
|
|
174
|
+
hidden: { title: 'Hidden', hideInSettings: true, uiSchema: { a: { type: 'string', 'x-component': 'Input' } } },
|
|
175
|
+
visible: { title: 'Visible', uiSchema: { b: { type: 'string', 'x-component': 'Input' } } },
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
render(
|
|
180
|
+
React.createElement(
|
|
181
|
+
ConfigProvider as any,
|
|
182
|
+
null,
|
|
183
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
184
|
+
),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
await waitFor(() => {
|
|
188
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
189
|
+
const items = (menu?.items || []) as any[];
|
|
190
|
+
expect(items.some((it) => String(it.key || '') === 'flowA:visible')).toBe(true);
|
|
191
|
+
expect(items.some((it) => String(it.key || '') === 'flowA:hidden')).toBe(false);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('includes step when uiSchema provided by action (step.use)', async () => {
|
|
196
|
+
class TestFlowModel extends FlowModel {}
|
|
197
|
+
const engine = new FlowEngine();
|
|
198
|
+
const model = new TestFlowModel({ uid: 'm-action', flowEngine: engine });
|
|
199
|
+
|
|
200
|
+
// Step has no uiSchema but uses an action that provides uiSchema
|
|
201
|
+
TestFlowModel.registerFlow({
|
|
202
|
+
key: 'flowB',
|
|
203
|
+
title: 'Flow B',
|
|
204
|
+
steps: {
|
|
205
|
+
useAction: { title: 'Use Action', use: 'act' },
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Stub getAction to provide uiSchema
|
|
210
|
+
(model as any).getAction = vi.fn().mockReturnValue({
|
|
211
|
+
name: 'act',
|
|
212
|
+
title: 'Action Title',
|
|
213
|
+
uiSchema: { x: { type: 'string', 'x-component': 'Input' } },
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
render(
|
|
217
|
+
React.createElement(
|
|
218
|
+
ConfigProvider as any,
|
|
219
|
+
null,
|
|
220
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
221
|
+
),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
await waitFor(() => {
|
|
225
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
226
|
+
const items = (menu?.items || []) as any[];
|
|
227
|
+
expect(items.some((it) => String(it.key || '') === 'flowB:useAction')).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('clicking a step item opens flow settings with correct args', async () => {
|
|
232
|
+
class TestFlowModel extends FlowModel {}
|
|
233
|
+
const engine = new FlowEngine();
|
|
234
|
+
const model = new TestFlowModel({ uid: 'm-open', flowEngine: engine });
|
|
235
|
+
const openSpy = vi.spyOn(model, 'openFlowSettings').mockResolvedValue(undefined as any);
|
|
236
|
+
|
|
237
|
+
TestFlowModel.registerFlow({
|
|
238
|
+
key: 'flowC',
|
|
239
|
+
title: 'Flow C',
|
|
240
|
+
steps: {
|
|
241
|
+
general: { title: 'General', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } },
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
render(
|
|
246
|
+
React.createElement(
|
|
247
|
+
ConfigProvider as any,
|
|
248
|
+
null,
|
|
249
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
250
|
+
),
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
await waitFor(() => {
|
|
254
|
+
expect((globalThis as any).__lastDropdownMenu).toBeTruthy();
|
|
255
|
+
});
|
|
256
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
257
|
+
menu.onClick?.({ key: 'flowC:general' });
|
|
258
|
+
expect(openSpy).toHaveBeenCalledWith({ flowKey: 'flowC', stepKey: 'general' });
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('copy UID action writes model uid to clipboard', async () => {
|
|
262
|
+
class TestFlowModel extends FlowModel {}
|
|
263
|
+
const engine = new FlowEngine();
|
|
264
|
+
const model = new TestFlowModel({ uid: 'm-copy', flowEngine: engine });
|
|
265
|
+
|
|
266
|
+
TestFlowModel.registerFlow({
|
|
267
|
+
key: 'flowD',
|
|
268
|
+
title: 'Flow D',
|
|
269
|
+
steps: { s: { title: 'S', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } } },
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// mock clipboard
|
|
273
|
+
Object.defineProperty(window.navigator, 'clipboard', {
|
|
274
|
+
value: { writeText: vi.fn().mockResolvedValue(undefined) },
|
|
275
|
+
configurable: true,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
render(
|
|
279
|
+
React.createElement(
|
|
280
|
+
ConfigProvider as any,
|
|
281
|
+
null,
|
|
282
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
283
|
+
),
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
await waitFor(() => {
|
|
287
|
+
expect((globalThis as any).__lastDropdownMenu).toBeTruthy();
|
|
288
|
+
});
|
|
289
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
290
|
+
menu.onClick?.({ key: 'copy-uid' });
|
|
291
|
+
expect((navigator as any).clipboard.writeText).toHaveBeenCalledWith('m-copy');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('delete action calls model.destroy()', async () => {
|
|
295
|
+
class TestFlowModel extends FlowModel {}
|
|
296
|
+
const engine = new FlowEngine();
|
|
297
|
+
const model = new TestFlowModel({ uid: 'm-del', flowEngine: engine });
|
|
298
|
+
const destroySpy = vi.spyOn(model, 'destroy').mockResolvedValue(undefined as any);
|
|
299
|
+
|
|
300
|
+
TestFlowModel.registerFlow({
|
|
301
|
+
key: 'flowE',
|
|
302
|
+
title: 'Flow E',
|
|
303
|
+
steps: { s: { title: 'S', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } } },
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
render(
|
|
307
|
+
React.createElement(
|
|
308
|
+
ConfigProvider as any,
|
|
309
|
+
null,
|
|
310
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
311
|
+
),
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
await waitFor(() => {
|
|
315
|
+
expect((globalThis as any).__lastDropdownMenu).toBeTruthy();
|
|
316
|
+
});
|
|
317
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
318
|
+
menu.onClick?.({ key: 'delete' });
|
|
319
|
+
expect(destroySpy).toHaveBeenCalled();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('shows sub-model steps with modelKey when flattenSubMenus=false and menuLevels=2', async () => {
|
|
323
|
+
class Parent extends FlowModel {}
|
|
324
|
+
class Child extends FlowModel {}
|
|
325
|
+
const engine = new FlowEngine();
|
|
326
|
+
const parent = new Parent({ uid: 'parent-1', flowEngine: engine });
|
|
327
|
+
const child = new Child({ uid: 'child-1', flowEngine: engine });
|
|
328
|
+
|
|
329
|
+
// child static flow
|
|
330
|
+
Child.registerFlow({
|
|
331
|
+
key: 'childFlow',
|
|
332
|
+
title: 'Child Flow',
|
|
333
|
+
steps: { cstep: { title: 'C', uiSchema: { x: { type: 'string', 'x-component': 'Input' } } } },
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
parent.addSubModel('items', child);
|
|
337
|
+
|
|
338
|
+
render(
|
|
339
|
+
React.createElement(
|
|
340
|
+
ConfigProvider as any,
|
|
341
|
+
null,
|
|
342
|
+
React.createElement(
|
|
343
|
+
App as any,
|
|
344
|
+
null,
|
|
345
|
+
React.createElement(DefaultSettingsIcon as any, {
|
|
346
|
+
model: parent,
|
|
347
|
+
menuLevels: 2,
|
|
348
|
+
flattenSubMenus: false,
|
|
349
|
+
}),
|
|
350
|
+
),
|
|
351
|
+
),
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
await waitFor(() => {
|
|
355
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
356
|
+
expect(menu).toBeTruthy();
|
|
357
|
+
const items = (menu?.items || []) as any[];
|
|
358
|
+
const subMenu = items.find((it) => Array.isArray(it?.children));
|
|
359
|
+
expect(subMenu).toBeTruthy();
|
|
360
|
+
expect(subMenu!.children.some((it: any) => String(it.key).startsWith('items[0]:childFlow:cstep'))).toBe(true);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('adds "Copy popup UID" for popupSettings flow (current model and sub-model)', async () => {
|
|
365
|
+
class Parent extends FlowModel {}
|
|
366
|
+
class Child extends FlowModel {}
|
|
367
|
+
const engine = new FlowEngine();
|
|
368
|
+
const parent = new Parent({ uid: 'parent-2', flowEngine: engine });
|
|
369
|
+
const child = new Child({ uid: 'child-2', flowEngine: engine });
|
|
370
|
+
|
|
371
|
+
// current model popupSettings
|
|
372
|
+
Parent.registerFlow({
|
|
373
|
+
key: 'popupSettings',
|
|
374
|
+
title: 'Popup',
|
|
375
|
+
steps: { stage: { title: 'Stage', uiSchema: { a: { type: 'string', 'x-component': 'Input' } } } },
|
|
376
|
+
});
|
|
377
|
+
// sub model popupSettings
|
|
378
|
+
Child.registerFlow({
|
|
379
|
+
key: 'popupSettings',
|
|
380
|
+
title: 'Popup Child',
|
|
381
|
+
steps: { stage: { title: 'Stage', uiSchema: { a: { type: 'string', 'x-component': 'Input' } } } },
|
|
382
|
+
});
|
|
383
|
+
parent.addSubModel('items', child);
|
|
384
|
+
|
|
385
|
+
// mock clipboard
|
|
386
|
+
Object.defineProperty(window.navigator, 'clipboard', {
|
|
387
|
+
value: { writeText: vi.fn().mockResolvedValue(undefined) },
|
|
388
|
+
configurable: true,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
render(
|
|
392
|
+
React.createElement(
|
|
393
|
+
ConfigProvider as any,
|
|
394
|
+
null,
|
|
395
|
+
React.createElement(
|
|
396
|
+
App as any,
|
|
397
|
+
null,
|
|
398
|
+
React.createElement(DefaultSettingsIcon as any, {
|
|
399
|
+
model: parent,
|
|
400
|
+
menuLevels: 2,
|
|
401
|
+
flattenSubMenus: true,
|
|
402
|
+
}),
|
|
403
|
+
),
|
|
404
|
+
),
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
// 等待“Copy popup UID”对应的菜单项出现,避免异步时序导致的偶发失败
|
|
408
|
+
await waitFor(() => {
|
|
409
|
+
const m = (globalThis as any).__lastDropdownMenu;
|
|
410
|
+
const is = (m?.items || []) as any[];
|
|
411
|
+
const current = is.find((it) => String(it.key) === 'copy-pop-uid:popupSettings:stage');
|
|
412
|
+
const sub = is.find((it) => String(it.key).startsWith('copy-pop-uid:items[0]:popupSettings:stage'));
|
|
413
|
+
expect(current).toBeTruthy();
|
|
414
|
+
expect(sub).toBeTruthy();
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// click and verify clipboard(直接使用最新的 menu)
|
|
418
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
419
|
+
menu.onClick?.({ key: 'copy-pop-uid:popupSettings:stage' });
|
|
420
|
+
expect((navigator as any).clipboard.writeText).toHaveBeenCalledWith('parent-2');
|
|
421
|
+
|
|
422
|
+
menu.onClick?.({ key: 'copy-pop-uid:items[0]:popupSettings:stage' });
|
|
423
|
+
expect((navigator as any).clipboard.writeText).toHaveBeenCalledWith('child-2');
|
|
424
|
+
});
|
|
425
|
+
});
|