diff --git a/packages/core/client/src/index.ts b/packages/core/client/src/index.ts index f6dbc4f821..0a03e4b657 100644 --- a/packages/core/client/src/index.ts +++ b/packages/core/client/src/index.ts @@ -71,5 +71,7 @@ export * from './modules/blocks/data-blocks/table-selector'; export * from './modules/blocks/index'; export * from './modules/blocks/useParentRecordCommon'; export { OpenModeProvider, useOpenModeContext } from './modules/popup/OpenModeProvider'; +export { PopupContextProvider } from './modules/popup/PopupContextProvider'; +export { usePopupUtils } from './modules/popup/usePopupUtils'; export { VariablePopupRecordProvider } from './modules/variable/variablesProvider/VariablePopupRecordProvider'; diff --git a/packages/core/client/src/modules/actions/add-child/CreateChildInitializer.tsx b/packages/core/client/src/modules/actions/add-child/CreateChildInitializer.tsx index 06b7cf539e..2a35408ab7 100644 --- a/packages/core/client/src/modules/actions/add-child/CreateChildInitializer.tsx +++ b/packages/core/client/src/modules/actions/add-child/CreateChildInitializer.tsx @@ -8,13 +8,13 @@ */ import React from 'react'; -import { usePagePopup } from '../../../schema-component/antd/page/pagePopupUtils'; +import { usePopupUtils } from '../../../schema-component/antd/page/pagePopupUtils'; import { CONTEXT_SCHEMA_KEY } from '../../../schema-component/antd/page/usePopupContextInActionOrAssociationField'; import { ActionInitializerItem } from '../../../schema-initializer/items/ActionInitializerItem'; import { useOpenModeContext } from '../../popup/OpenModeProvider'; export const CreateChildInitializer = (props) => { const { defaultOpenMode } = useOpenModeContext(); - const { getPopupContext } = usePagePopup(); + const { getPopupContext } = usePopupUtils(); const schema = { type: 'void', title: '{{ t("Add child") }}', diff --git a/packages/core/client/src/modules/actions/add-new/CreateActionInitializer.tsx b/packages/core/client/src/modules/actions/add-new/CreateActionInitializer.tsx index 2713434cf2..26253c66b3 100644 --- a/packages/core/client/src/modules/actions/add-new/CreateActionInitializer.tsx +++ b/packages/core/client/src/modules/actions/add-new/CreateActionInitializer.tsx @@ -9,14 +9,14 @@ import React from 'react'; import { useSchemaInitializerItem } from '../../../application'; -import { usePagePopup } from '../../../schema-component/antd/page/pagePopupUtils'; +import { usePopupUtils } from '../../../schema-component/antd/page/pagePopupUtils'; import { CONTEXT_SCHEMA_KEY } from '../../../schema-component/antd/page/usePopupContextInActionOrAssociationField'; import { ActionInitializerItem } from '../../../schema-initializer/items/ActionInitializerItem'; import { useOpenModeContext } from '../../popup/OpenModeProvider'; export const CreateActionInitializer = () => { const { defaultOpenMode } = useOpenModeContext(); - const { getPopupContext } = usePagePopup(); + const { getPopupContext } = usePopupUtils(); const schema = { type: 'void', 'x-action': 'create', diff --git a/packages/core/client/src/modules/actions/view-edit-popup/PopupActionInitializer.tsx b/packages/core/client/src/modules/actions/view-edit-popup/PopupActionInitializer.tsx index c908c742c8..bd10a7a3b6 100644 --- a/packages/core/client/src/modules/actions/view-edit-popup/PopupActionInitializer.tsx +++ b/packages/core/client/src/modules/actions/view-edit-popup/PopupActionInitializer.tsx @@ -9,14 +9,14 @@ import React from 'react'; import { useSchemaInitializerItem } from '../../../application'; -import { usePagePopup } from '../../../schema-component/antd/page/pagePopupUtils'; +import { usePopupUtils } from '../../../schema-component/antd/page/pagePopupUtils'; import { CONTEXT_SCHEMA_KEY } from '../../../schema-component/antd/page/usePopupContextInActionOrAssociationField'; import { BlockInitializer } from '../../../schema-initializer/items'; import { useOpenModeContext } from '../../popup/OpenModeProvider'; export const PopupActionInitializer = (props) => { const { defaultOpenMode } = useOpenModeContext(); - const { getPopupContext } = usePagePopup(); + const { getPopupContext } = usePopupUtils(); const schema = { type: 'void', title: '{{ t("Popup") }}', diff --git a/packages/core/client/src/modules/actions/view-edit-popup/UpdateActionInitializer.tsx b/packages/core/client/src/modules/actions/view-edit-popup/UpdateActionInitializer.tsx index 2d395d846b..7de0c0e3a8 100644 --- a/packages/core/client/src/modules/actions/view-edit-popup/UpdateActionInitializer.tsx +++ b/packages/core/client/src/modules/actions/view-edit-popup/UpdateActionInitializer.tsx @@ -8,14 +8,14 @@ */ import React from 'react'; -import { usePagePopup } from '../../../schema-component/antd/page/pagePopupUtils'; +import { usePopupUtils } from '../../../schema-component/antd/page/pagePopupUtils'; import { CONTEXT_SCHEMA_KEY } from '../../../schema-component/antd/page/usePopupContextInActionOrAssociationField'; import { ActionInitializerItem } from '../../../schema-initializer/items/ActionInitializerItem'; import { useOpenModeContext } from '../../popup/OpenModeProvider'; export const UpdateActionInitializer = (props) => { const { defaultOpenMode } = useOpenModeContext(); - const { getPopupContext } = usePagePopup(); + const { getPopupContext } = usePopupUtils(); const schema = { type: 'void', title: '{{ t("Edit") }}', diff --git a/packages/core/client/src/modules/actions/view-edit-popup/ViewActionInitializer.tsx b/packages/core/client/src/modules/actions/view-edit-popup/ViewActionInitializer.tsx index 3324842450..7009bab5fc 100644 --- a/packages/core/client/src/modules/actions/view-edit-popup/ViewActionInitializer.tsx +++ b/packages/core/client/src/modules/actions/view-edit-popup/ViewActionInitializer.tsx @@ -8,14 +8,14 @@ */ import React from 'react'; -import { usePagePopup } from '../../../schema-component/antd/page/pagePopupUtils'; +import { usePopupUtils } from '../../../schema-component/antd/page/pagePopupUtils'; import { CONTEXT_SCHEMA_KEY } from '../../../schema-component/antd/page/usePopupContextInActionOrAssociationField'; import { ActionInitializerItem } from '../../../schema-initializer/items/ActionInitializerItem'; import { useOpenModeContext } from '../../popup/OpenModeProvider'; export const ViewActionInitializer = (props) => { const { defaultOpenMode } = useOpenModeContext(); - const { getPopupContext } = usePagePopup(); + const { getPopupContext } = usePopupUtils(); const schema = { type: 'void', title: '{{ t("View") }}', diff --git a/packages/core/client/src/modules/popup/PopupContextProvider.tsx b/packages/core/client/src/modules/popup/PopupContextProvider.tsx new file mode 100644 index 0000000000..4aead69568 --- /dev/null +++ b/packages/core/client/src/modules/popup/PopupContextProvider.tsx @@ -0,0 +1,49 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { useFieldSchema } from '@formily/react'; +import React, { useCallback, useContext, useState } from 'react'; +import { ActionContextProvider } from '../../schema-component'; +import { PopupVisibleProvider, PopupVisibleProviderContext } from '../../schema-component/antd/page/PagePopups'; + +/** + * provider the context for popup to work + * @param props + * @returns + */ +export const PopupContextProvider: React.FC = (props) => { + const [visible, setVisible] = useState(false); + const { visible: visibleWithURL, setVisible: setVisibleWithURL } = useContext(PopupVisibleProviderContext) || { + visible: false, + setVisible: () => {}, + }; + const fieldSchema = useFieldSchema(); + const _setVisible = useCallback( + (value: boolean): void => { + setVisible?.(value); + setVisibleWithURL?.(value); + }, + [setVisibleWithURL], + ); + const openMode = fieldSchema['x-component-props']?.['openMode'] || 'drawer'; + const openSize = fieldSchema['x-component-props']?.['openSize']; + + return ( + + + {props.children} + + + ); +}; diff --git a/packages/core/client/src/modules/popup/usePopupUtils.ts b/packages/core/client/src/modules/popup/usePopupUtils.ts new file mode 100644 index 0000000000..2322a81f60 --- /dev/null +++ b/packages/core/client/src/modules/popup/usePopupUtils.ts @@ -0,0 +1,10 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +export { usePopupUtils } from '../../schema-component/antd/page/pagePopupUtils'; diff --git a/packages/core/client/src/schema-component/antd/action/Action.tsx b/packages/core/client/src/schema-component/antd/action/Action.tsx index 3c61383c0e..072f8f17f2 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.tsx @@ -29,7 +29,7 @@ import { SortableItem } from '../../common'; import { useCompile, useComponent, useDesigner } from '../../hooks'; import { useProps } from '../../hooks/useProps'; import { PopupVisibleProvider } from '../page/PagePopups'; -import { usePagePopup } from '../page/pagePopupUtils'; +import { usePopupUtils } from '../page/pagePopupUtils'; import { usePopupSettings } from '../page/PopupSettingsProvider'; import ActionContainer from './Action.Container'; import { ActionDesigner } from './Action.Designer'; @@ -77,7 +77,7 @@ export const Action: ComposedAction = withDynamicSchemaProps( const aclCtx = useACLActionParamsContext(); const { wrapSSR, componentCls, hashId } = useStyles(); const { t } = useTranslation(); - const { visibleWithURL, setVisibleWithURL } = usePagePopup(); + const { visibleWithURL, setVisibleWithURL } = usePopupUtils(); const [visible, setVisible] = useState(false); const [formValueChanged, setFormValueChanged] = useState(false); const { setSubmitted: setParentSubmitted } = useActionContext(); @@ -307,7 +307,7 @@ function RenderButton({ }) { const { t } = useTranslation(); const { isPopupVisibleControlledByURL } = usePopupSettings(); - const { openPopup } = usePagePopup(); + const { openPopup } = usePopupUtils(); const handleButtonClick = useCallback( (e: React.MouseEvent, checkPortal = true) => { diff --git a/packages/core/client/src/schema-component/antd/association-field/InternalTag.tsx b/packages/core/client/src/schema-component/antd/association-field/InternalTag.tsx index c27fa099ed..562c59a8ba 100644 --- a/packages/core/client/src/schema-component/antd/association-field/InternalTag.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/InternalTag.tsx @@ -15,7 +15,7 @@ import { useCollectionManager_deprecated } from '../../../collection-manager'; import { useCollectionRecordData } from '../../../data-source/collection-record/CollectionRecordProvider'; import { useCompile } from '../../hooks'; import { useActionContext } from '../action'; -import { usePagePopup } from '../page/pagePopupUtils'; +import { usePopupUtils } from '../page/pagePopupUtils'; import { transformNestedData } from './InternalCascadeSelect'; import { ButtonListProps, ReadPrettyInternalViewer, isObject } from './InternalViewer'; import { useAssociationFieldContext, useFieldNames, useInsertSchema } from './hooks'; @@ -47,7 +47,7 @@ const ButtonTabList: React.FC = (props) => { const { getCollection } = useCollectionManager_deprecated(); const targetCollection = getCollection(collectionField?.target); const isTreeCollection = targetCollection?.template === 'tree'; - const { openPopup } = usePagePopup(); + const { openPopup } = usePopupUtils(); const recordData = useCollectionRecordData(); const renderRecords = () => diff --git a/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx b/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx index 7210190994..87427f3660 100644 --- a/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx @@ -19,7 +19,7 @@ import { useCompile } from '../../hooks'; import { ActionContextProvider, useActionContext } from '../action'; import { EllipsisWithTooltip } from '../input/EllipsisWithTooltip'; import { PopupVisibleProvider } from '../page/PagePopups'; -import { usePagePopup } from '../page/pagePopupUtils'; +import { usePopupUtils } from '../page/pagePopupUtils'; import { useAssociationFieldContext, useFieldNames, useInsertSchema } from './hooks'; import { transformNestedData } from './InternalCascadeSelect'; import schema from './schema'; @@ -62,7 +62,7 @@ const ButtonLinkList: FC = (props) => { const isTreeCollection = targetCollection?.template === 'tree'; const ellipsisWithTooltipRef = useRef(); const getLabelUiSchema = useLabelUiSchemaV2(); - const { openPopup } = usePagePopup(); + const { openPopup } = usePopupUtils(); const recordData = useCollectionRecordData(); const renderRecords = () => @@ -143,7 +143,7 @@ export const ReadPrettyInternalViewer: React.FC = observer( const [visible, setVisible] = useState(false); const { options: collectionField } = useAssociationFieldContext(); const ellipsisWithTooltipRef = useRef(); - const { visibleWithURL, setVisibleWithURL } = usePagePopup(); + const { visibleWithURL, setVisibleWithURL } = usePopupUtils(); const [btnHover, setBtnHover] = useState(!!visibleWithURL); const { defaultOpenMode } = useOpenModeContext(); diff --git a/packages/core/client/src/schema-component/antd/page/PagePopups.tsx b/packages/core/client/src/schema-component/antd/page/PagePopups.tsx index c394dcba89..96c59bd0b6 100644 --- a/packages/core/client/src/schema-component/antd/page/PagePopups.tsx +++ b/packages/core/client/src/schema-component/antd/page/PagePopups.tsx @@ -7,7 +7,8 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { ISchema } from '@formily/json-schema'; +import { ISchema, Schema } from '@formily/json-schema'; +import { useFieldSchema } from '@formily/react'; import { uid } from '@formily/shared'; import { Result } from 'antd'; import _ from 'lodash'; @@ -21,7 +22,7 @@ import { SchemaComponent } from '../../core'; import { TabsContextProvider } from '../tabs/context'; import { usePopupSettings } from './PopupSettingsProvider'; import { deleteRandomNestedSchemaKey, getRandomNestedSchemaKey } from './nestedSchemaKeyStorage'; -import { PopupParams, getPopupParamsFromPath, getStoredPopupContext, usePagePopup } from './pagePopupUtils'; +import { PopupParams, getPopupParamsFromPath, getStoredPopupContext, usePopupUtils } from './pagePopupUtils'; import { PopupContext, getPopupContextFromActionOrAssociationFieldSchema, @@ -86,7 +87,7 @@ const PopupParamsProvider: FC> = (props) => { const PopupTabsPropsProvider: FC = ({ children }) => { const { params } = useCurrentPopupContext(); - const { changeTab } = usePagePopup(); + const { changeTab } = usePopupUtils(); const onChange = useCallback( (key: string) => { changeTab(key); @@ -114,7 +115,7 @@ const PagePopupsItemProvider: FC<{ */ currentLevel: number; }> = ({ params, context, currentLevel, children }) => { - const { closePopup } = usePagePopup(); + const { closePopup } = usePopupUtils(); const [visible, _setVisible] = useState(true); const setVisible = (visible: boolean) => { if (!visible) { @@ -183,7 +184,17 @@ const PagePopupsItemProvider: FC<{ * @param props * @param parentSchema */ -export const insertChildToParentSchema = (childSchema: ISchema, props: PopupProps, parentSchema: ISchema) => { +export const insertChildToParentSchema = ({ + childSchema, + props, + parentSchema, + getPopupSchema = (currentSchema) => _.get(currentSchema.properties, Object.keys(currentSchema.properties)[0]), +}: { + childSchema: ISchema; + props: PopupProps; + parentSchema: ISchema; + getPopupSchema?: (currentSchema: ISchema) => ISchema; +}) => { const { params, context, currentLevel } = props; const componentSchema = { @@ -203,7 +214,7 @@ export const insertChildToParentSchema = (childSchema: ISchema, props: PopupProp const nestedPopupKey = getRandomNestedSchemaKey(params.popupuid); if (parentSchema.properties) { - const popupSchema = _.get(parentSchema.properties, Object.keys(parentSchema.properties)[0]); + const popupSchema = getPopupSchema(parentSchema); if (_.isEmpty(_.get(popupSchema, `properties.${nestedPopupKey}`))) { _.set(popupSchema, `properties.${nestedPopupKey}`, componentSchema); } @@ -211,11 +222,13 @@ export const insertChildToParentSchema = (childSchema: ISchema, props: PopupProp }; export const PagePopups = (props: { paramsList?: PopupParams[] }) => { + const fieldSchema = useFieldSchema(); const location = useLocation(); const popupParams = props.paramsList || getPopupParamsFromPath(getPopupPath(location)); const { requestSchema } = useRequestSchema(); const [rootSchema, setRootSchema] = useState(null); const popupPropsRef = useRef([]); + const { savePopupSchemaToSchema, getPopupSchemaFromSchema } = usePopupUtils(); useEffect(() => { const run = async () => { @@ -223,13 +236,23 @@ export const PagePopups = (props: { paramsList?: PopupParams[] }) => { (params) => getStoredPopupContext(params.popupuid)?.schema || requestSchema(params.popupuid), ); const schemas = await Promise.all(waitList); - const clonedSchemas = schemas.map((schema) => { + const clonedSchemas = schemas.map((schema, index) => { if (_.isEmpty(schema)) { return get404Schema(); } - const result = _.cloneDeep(_.omit(schema, 'parent')); + const params = popupParams[index]; + + if (params.puid) { + const popupSchema = findSchemaByUid(params.puid, fieldSchema.root); + if (popupSchema) { + savePopupSchemaToSchema(_.omit(popupSchema, 'parent'), schema); + } + } + + const result = _.cloneDeep(_.omit(schema, 'parent')) as Schema; result['x-read-pretty'] = true; + return result; }); popupPropsRef.current = clonedSchemas.map((schema, index, items) => { @@ -254,12 +277,22 @@ export const PagePopups = (props: { paramsList?: PopupParams[] }) => { }); const rootSchema = clonedSchemas[0]; for (let i = 1; i < clonedSchemas.length; i++) { - insertChildToParentSchema(clonedSchemas[i], popupPropsRef.current[i], clonedSchemas[i - 1]); + insertChildToParentSchema({ + childSchema: clonedSchemas[i], + props: popupPropsRef.current[i], + parentSchema: clonedSchemas[i - 1], + getPopupSchema: (currentSchema) => { + return ( + getPopupSchemaFromSchema(currentSchema) || + _.get(currentSchema.properties, Object.keys(currentSchema.properties)[0]) + ); + }, + }); } setRootSchema(rootSchema); }; run(); - }, [popupParams, requestSchema]); + }, [fieldSchema.root, getPopupSchemaFromSchema, popupParams, requestSchema, savePopupSchemaToSchema]); const components = useMemo(() => ({ PagePopupsItemProvider }), []); @@ -406,3 +439,17 @@ function get404Schema() { 'x-read-pretty': true, }; } + +function findSchemaByUid(uid: string, rootSchema: Schema, resultRef: { value: Schema } = { value: null }) { + resultRef = resultRef || { + value: null, + }; + rootSchema.mapProperties((schema) => { + if (schema['x-uid'] === uid) { + resultRef.value = schema; + } else { + findSchemaByUid(uid, schema, resultRef); + } + }); + return resultRef.value; +} diff --git a/packages/core/client/src/schema-component/antd/page/__tests__/PagePopups.test.tsx b/packages/core/client/src/schema-component/antd/page/__tests__/PagePopups.test.tsx index a48a4b0300..ae0bc02995 100644 --- a/packages/core/client/src/schema-component/antd/page/__tests__/PagePopups.test.tsx +++ b/packages/core/client/src/schema-component/antd/page/__tests__/PagePopups.test.tsx @@ -41,7 +41,7 @@ describe('insertToPopupSchema', () => { }, }; - insertChildToParentSchema(childSchema, { params, context }, parentSchema); + insertChildToParentSchema({ childSchema, props: { params, context } as any, parentSchema }); expect(parentSchema).toEqual({ type: 'object', diff --git a/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx b/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx index 1914f7f261..6e73c22aa1 100644 --- a/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx +++ b/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx @@ -37,6 +37,8 @@ export interface PopupParams { tab?: string; /** collection name */ collection?: string; + /** popup uid */ + puid?: string; } export interface PopupContextStorage extends PopupContext { @@ -103,9 +105,11 @@ export const getPopupParamsFromPath = _.memoize((path: string) => { }); export const getPopupPathFromParams = (params: PopupParams) => { - const { popupuid: popupUid, tab, filterbytk, sourceid, collection } = params; + const { popupuid: popupUid, tab, filterbytk, sourceid, collection, puid } = params; const popupPath = [ popupUid, + puid && 'puid', + puid, collection && 'collection', collection, filterbytk && 'filterbytk', @@ -123,7 +127,7 @@ export const getPopupPathFromParams = (params: PopupParams) => { * Note: use this hook in a plugin is not recommended * @returns */ -export const usePagePopup = () => { +export const usePopupUtils = () => { const navigate = useNavigateNoUpdate(); const location = useLocationNoUpdate(); const fieldSchema = useFieldSchema(); @@ -153,16 +157,23 @@ export const usePagePopup = () => { recordData, sourceId, collection: _collection, + puid, }: { + /** + * this is the schema uid of the button that triggers the popup, while puid is the schema uid of the popup, they are different; + */ popupUid: string; recordData: Record; sourceId: string; tabKey?: string; collection?: string; + /** popup uid */ + puid?: string; }) => { const filterByTK = cm.getFilterByTK(association || collection, recordData); return getPopupPathFromParams({ popupuid: popupUid, + puid, collection: _collection, filterbytk: filterByTK, sourceid: sourceId, @@ -187,11 +198,14 @@ export const usePagePopup = () => { recordData, parentRecordData, collectionNameUsedInURL, + popupUidUsedInURL, }: { recordData?: Record; parentRecordData?: Record; /** if this value exists, it will be saved in the URL */ collectionNameUsedInURL?: string; + /** if this value exists, it will be saved in the URL */ + popupUidUsedInURL?: string; } = {}) => { if (!isPopupVisibleControlledByURL()) { return setVisibleFromAction?.(true); @@ -205,6 +219,7 @@ export const usePagePopup = () => { recordData, sourceId, collection: collectionNameUsedInURL, + puid: popupUidUsedInURL, }); let url = location.pathname; if (_.last(url) === '/') { @@ -283,6 +298,14 @@ export const usePagePopup = () => { [getNewPathname, navigate, popupParams?.popupuid, record?.data, location], ); + const savePopupSchemaToSchema = useCallback((popupSchema: ISchema, targetSchema: ISchema) => { + targetSchema['__popup'] = popupSchema; + }, []); + + const getPopupSchemaFromSchema = useCallback((schema: ISchema) => { + return schema['__popup']; + }, []); + return { /** * used to open popup by changing the url @@ -292,10 +315,32 @@ export const usePagePopup = () => { * used to close popup by changing the url */ closePopup, + savePopupSchemaToSchema, + getPopupSchemaFromSchema, + /** + * @deprecated + * TODO: remove this + */ visibleWithURL: visible, + /** + * @deprecated + * TODO: remove this + */ setVisibleWithURL: setVisible, + /** + * @deprecated + * TODO: remove this + */ popupParams, + /** + * @deprecated + * TODO: remove this + */ changeTab, + /** + * @deprecated + * TODO: remove this + */ getPopupContext, }; }; diff --git a/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx b/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx index 5631ddeb9d..0fc08b9b60 100644 --- a/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx +++ b/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx @@ -19,7 +19,7 @@ import { useTreeParentRecord } from '../../modules/blocks/data-blocks/table/Tree import { useRecord } from '../../record-provider'; import { useCompile } from '../../schema-component'; import { linkageAction } from '../../schema-component/antd/action/utils'; -import { usePagePopup } from '../../schema-component/antd/page/pagePopupUtils'; +import { usePopupUtils } from '../../schema-component/antd/page/pagePopupUtils'; import { parseVariables } from '../../schema-component/common/utils/uitls'; import { useLocalVariables, useVariables } from '../../variables'; @@ -70,7 +70,7 @@ const InternalCreateRecordAction = (props: any, ref) => { const values = useRecord(); const variables = useVariables(); const localVariables = useLocalVariables({ currentForm: { values } as any }); - const { openPopup } = usePagePopup(); + const { openPopup } = usePopupUtils(); const treeRecordData = useTreeParentRecord(); const cm = useCollectionManager(); diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.tsx index 3d588b6e89..8f22c7672c 100644 --- a/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.tsx +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.tsx @@ -9,19 +9,21 @@ import { css } from '@emotion/css'; import { FormLayout } from '@formily/antd-v5'; +import { createForm } from '@formily/core'; import { observer, RecursionField, useFieldSchema } from '@formily/react'; import { - ActionContextProvider, DndContext, - RecordProvider, + FormProvider, + PopupContextProvider, useCollection, - useCollectionParentRecordData, + useCollectionRecordData, + usePopupUtils, VariablePopupRecordProvider, } from '@nocobase/client'; +import { Schema } from '@nocobase/utils'; import { Card } from 'antd'; -import React, { useCallback, useContext, useMemo, useState } from 'react'; +import React, { useCallback, useContext, useMemo } from 'react'; import { KanbanCardContext } from './context'; -import { useKanbanTranslation } from './locale'; const cardCss = css` .ant-card-body { @@ -72,23 +74,27 @@ MemorizedRecursionField.displayName = 'MemorizedRecursionField'; export const KanbanCard: any = observer( () => { - const { t } = useKanbanTranslation(); const collection = useCollection(); - const { setDisableCardDrag, cardViewerSchema, card, cardField, columnIndex, cardIndex } = - useContext(KanbanCardContext); - const parentRecordData = useCollectionParentRecordData(); + const { setDisableCardDrag } = useContext(KanbanCardContext) || {}; const fieldSchema = useFieldSchema(); - const [visible, setVisible] = useState(false); - const handleCardClick = useCallback((e: React.MouseEvent) => { - const targetElement = e.target as Element; // 将事件目标转换为Element类型 - const currentTargetElement = e.currentTarget as Element; - if (currentTargetElement.contains(targetElement)) { - setVisible(true); - e.stopPropagation(); - } else { - e.stopPropagation(); - } - }, []); + const { openPopup, getPopupSchemaFromSchema } = usePopupUtils(); + const recordData = useCollectionRecordData(); + const popupSchema = getPopupSchemaFromSchema(fieldSchema) || getPopupSchemaFromParent(fieldSchema); + const handleCardClick = useCallback( + (e: React.MouseEvent) => { + const targetElement = e.target as Element; // 将事件目标转换为Element类型 + const currentTargetElement = e.currentTarget as Element; + if (currentTargetElement.contains(targetElement)) { + openPopup({ + popupUidUsedInURL: popupSchema?.['x-uid'], + }); + e.stopPropagation(); + } else { + e.stopPropagation(); + } + }, + [openPopup, popupSchema], + ); const cardStyle = useMemo(() => { return { cursor: 'pointer', @@ -96,51 +102,70 @@ export const KanbanCard: any = observer( }; }, []); + const form = useMemo(() => { + return createForm({ + values: recordData, + }); + }, [recordData]); + const onDragStart = useCallback(() => { - setDisableCardDrag(true); - }, []); + setDisableCardDrag?.(true); + }, [setDisableCardDrag]); const onDragEnd = useCallback(() => { - setDisableCardDrag(false); - }, []); + setDisableCardDrag?.(false); + }, [setDisableCardDrag]); - const actionContextValue = useMemo(() => { + // if not wrapped, only Tab component's content will be rendered, Drawer component's content will not be rendered + const wrappedPopupSchema = useMemo(() => { return { - openMode: fieldSchema['x-component-props']?.['openMode'] || 'drawer', - openSize: fieldSchema['x-component-props']?.['openSize'], - visible, - setVisible, + type: 'void', + properties: { + drawer: popupSchema, + }, }; - }, [fieldSchema, visible]); - - const basePath = useMemo( - () => cardField.address.concat(`${columnIndex}.cards.${cardIndex}`), - [cardField, columnIndex, cardIndex], - ); - const cardViewerBasePath = useMemo( - () => cardField.address.concat(`${columnIndex}.cardViewer.${cardIndex}`), - [cardField, columnIndex, cardIndex], - ); + }, [popupSchema]); return ( <> - + + + - {cardViewerSchema && ( - - - - - - - - )} + + + + + ); }, { displayName: 'KanbanCard' }, ); + +function getPopupSchemaFromParent(fieldSchema: Schema) { + if (fieldSchema.parent?.properties?.cardViewer?.properties?.drawer) { + return fieldSchema.parent.properties.cardViewer.properties.drawer; + } + + const cardSchema = findSchemaByUid(fieldSchema['x-uid'], fieldSchema.root); + return cardSchema.parent.properties.cardViewer.properties.drawer; +} + +function findSchemaByUid(uid: string, rootSchema: Schema, resultRef: { value: Schema } = { value: null }) { + resultRef = resultRef || { + value: null, + }; + rootSchema.mapProperties((schema) => { + if (schema['x-uid'] === uid) { + resultRef.value = schema; + } else { + findSchemaByUid(uid, schema, resultRef); + } + }); + return resultRef.value; +} diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.tsx index c1d1b87c0b..c956136572 100644 --- a/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.tsx +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.tsx @@ -136,7 +136,7 @@ export const Kanban: any = withDynamicSchemaProps( {title} )} - renderCard={(card, { column, dragging }) => { + renderCard={(card, { column }) => { const columnIndex = dataSource?.indexOf(column); const cardIndex = column?.cards?.indexOf(card); const { ref, inView } = useInView({ @@ -144,24 +144,18 @@ export const Kanban: any = withDynamicSchemaProps( triggerOnce: true, initialInView: lastDraggedCard.current && lastDraggedCard.current === card[primaryKey], }); + return ( schemas.card && (
{inView ? ( - + ) : ( diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/templates.ts b/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/templates.ts new file mode 100644 index 0000000000..10db2b360b --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/templates.ts @@ -0,0 +1,755 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +export const kanbanURL = { + collections: [ + { + name: 'kanban', + fields: [ + { + name: 'select', + interface: 'select', + uiSchema: { + enum: [ + { + value: 'option1', + label: 'Option1', + color: 'red', + }, + { + value: 'option2', + label: 'Option2', + color: 'green', + }, + { + value: 'option3', + label: 'Option3', + color: 'blue', + }, + ], + type: 'string', + 'x-component': 'Select', + title: 'Single select', + }, + }, + { + name: 'sort', + interface: 'sort', + scopeKey: 'select', + }, + ], + }, + ], + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + eehnoq75dyp: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + properties: { + qy13k7twlgr: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.0-alpha', + properties: { + z2zx32gyeno: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.0-alpha', + properties: { + '6h3p53u5ek7': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action': 'kanban:list', + 'x-decorator': 'KanbanBlockProvider', + 'x-decorator-props': { + collection: 'kanban', + dataSource: 'main', + action: 'list', + groupField: 'select', + sortField: 'sort', + params: { + paginate: false, + sort: ['sort'], + }, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:kanban', + 'x-component': 'CardItem', + 'x-app-version': '1.3.0-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'kanban:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.3.0-alpha', + 'x-uid': 'zb0mo93az5z', + 'x-async': false, + 'x-index': 1, + }, + ms2kyy843uc: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-component': 'Kanban', + 'x-use-component-props': 'useKanbanBlockProps', + 'x-app-version': '1.3.0-alpha', + properties: { + card: { + 'x-uid': '1y1b9kliqui', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-read-pretty': true, + 'x-label-disabled': true, + 'x-decorator': 'BlockItem', + 'x-component': 'Kanban.Card', + 'x-component-props': { + openMode: 'drawer', + }, + 'x-designer': 'Kanban.Card.Designer', + 'x-app-version': '1.3.0-alpha', + 'x-action-context': { + dataSource: 'main', + collection: 'kanban', + }, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-component-props': { + dndContext: false, + }, + 'x-app-version': '1.3.0-alpha', + properties: { + '8evl3dcvt86': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + '36yop5rgpyi': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + select: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'kanban.select', + 'x-component-props': { + style: { + width: '100%', + }, + }, + 'x-read-pretty': true, + 'x-uid': '8a20gaw8qdh', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'pzvcxvven6y', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'xd4gdx5j0qm', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'zx1rf8qfane', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + cardViewer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View") }}', + 'x-designer': 'Action.Designer', + 'x-component': 'Kanban.CardViewer', + 'x-action': 'view', + 'x-component-props': { + openMode: 'drawer', + }, + 'x-app-version': '1.3.0-alpha', + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + 'x-app-version': '1.3.0-alpha', + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + 'x-app-version': '1.3.0-alpha', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': '1.3.0-alpha', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:common:addBlock', + 'x-app-version': '1.3.0-alpha', + properties: { + '8bv7l5hiazu': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.0-alpha', + properties: { + torszg5y5zd: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.0-alpha', + properties: { + d4p7lp86m03: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action': 'kanban:get', + 'x-decorator': 'DetailsBlockProvider', + 'x-use-decorator-props': 'useDetailsDecoratorProps', + 'x-decorator-props': { + dataSource: 'main', + collection: 'kanban', + readPretty: true, + action: 'get', + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:details', + 'x-component': 'CardItem', + 'x-app-version': '1.3.0-alpha', + properties: { + '1oxl6q1ktkh': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Details', + 'x-read-pretty': true, + 'x-use-component-props': 'useDetailsProps', + 'x-app-version': '1.3.0-alpha', + properties: { + r27t49cfoo5: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'details:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + 'x-app-version': '1.3.0-alpha', + properties: { + uya6ulpeavl: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Edit") }}', + 'x-action': 'update', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:edit', + 'x-component': 'Action', + 'x-component-props': { + openMode: 'drawer', + icon: 'EditOutlined', + type: 'primary', + }, + 'x-action-context': { + dataSource: 'main', + collection: 'kanban', + }, + 'x-decorator': 'ACLActionProvider', + 'x-app-version': '1.3.0-alpha', + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Edit record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + 'x-app-version': '1.3.0-alpha', + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + 'x-app-version': '1.3.0-alpha', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Edit")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': '1.3.0-alpha', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:common:addBlock', + 'x-app-version': '1.3.0-alpha', + properties: { + etzmlia85gh: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.0-alpha', + properties: { + '6dn14wki0e5': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': + '1.3.0-alpha', + properties: { + cruqx5ulnow: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-acl-action-props': { + skipScopeCheck: false, + }, + 'x-acl-action': + 'kanban:update', + 'x-decorator': + 'FormBlockProvider', + 'x-use-decorator-props': + 'useEditFormBlockDecoratorProps', + 'x-decorator-props': { + action: 'get', + dataSource: 'main', + collection: 'kanban', + }, + 'x-toolbar': + 'BlockSchemaToolbar', + 'x-settings': + 'blockSettings:editForm', + 'x-component': + 'CardItem', + 'x-app-version': + '1.3.0-alpha', + properties: { + y0q5ei7mma2: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'FormV2', + 'x-use-component-props': + 'useEditFormBlockProps', + 'x-app-version': + '1.3.0-alpha', + properties: { + grid: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid', + 'x-initializer': + 'form:configureFields', + 'x-app-version': + '1.3.0-alpha', + properties: { + rjbvqpp7xmu: { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.3.0-alpha', + properties: + { + cxiykvzwfv1: + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.3.0-alpha', + properties: + { + select: + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'string', + 'x-toolbar': + 'FormItemSchemaToolbar', + 'x-settings': + 'fieldSettings:FormItem', + 'x-component': + 'CollectionField', + 'x-decorator': + 'FormItem', + 'x-collection-field': + 'kanban.select', + 'x-component-props': + { + style: + { + width: + '100%', + }, + }, + 'x-app-version': + '1.3.0-alpha', + 'x-uid': + 'eo2xh4adfaz', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'walnc8ivgzp', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '3san45vkk32', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'opiixrhoo0z', + 'x-async': + false, + 'x-index': 1, + }, + '5j12oi9pvf3': { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-initializer': + 'editForm:configureActions', + 'x-component': + 'ActionBar', + 'x-component-props': + { + layout: + 'one-column', + }, + 'x-app-version': + '1.3.0-alpha', + properties: { + m9e7qxjys7o: { + _isJSONSchemaObject: + true, + version: + '2.0', + title: + '{{ t("Submit") }}', + 'x-action': + 'submit', + 'x-component': + 'Action', + 'x-use-component-props': + 'useUpdateActionProps', + 'x-toolbar': + 'ActionSchemaToolbar', + 'x-settings': + 'actionSettings:updateSubmit', + 'x-component-props': + { + type: 'primary', + htmlType: + 'submit', + }, + 'x-action-settings': + { + triggerWorkflows: + [], + }, + type: 'void', + 'x-app-version': + '1.3.0-alpha', + 'x-uid': + '5sc8g3215j4', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '2v1zlgnh0lp', + 'x-async': + false, + 'x-index': 2, + }, + }, + 'x-uid': + 'u5viek4cakp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'z38sbx5yocb', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'y0wdbxagcre', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'dn2spxvle3x', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fsmyjuyc0mc', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'x27vohz57u1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'uxbmlytgowr', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'p108r8s8pjh', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'detjnjixd9n', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'i9622c7n8nv', + 'x-async': false, + 'x-index': 1, + }, + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'details:configureFields', + 'x-app-version': '1.3.0-alpha', + properties: { + cylu3xjywvu: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.0-alpha', + properties: { + z1eb85u2n4n: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.0-alpha', + properties: { + select: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'kanban.select', + 'x-component-props': { + style: { + width: '100%', + }, + }, + 'x-app-version': '1.3.0-alpha', + 'x-uid': 'mfz4c6l4ft9', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'lt4bp9emb4z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ny6e5q4fhww', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'yfunnpaf97c', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'pegqhin3s88', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'wpafritzh4d', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '2vjqy76fds6', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'avlvzb0yl0s', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'm5x3kz75nm2', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '6wb8grmdenj', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'uybfp4br16z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4dwpi8dug9d', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'x0dusy43fhl', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '6vpzr0nsxjs', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'f1eqli5bc07', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4exwrzlac4i', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'edzxlnh6nx4', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 's95fpawrrle', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '57xhoz3sfzq', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/url.test.ts b/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/url.test.ts new file mode 100644 index 0000000000..6ae5dbb165 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/url.test.ts @@ -0,0 +1,57 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { expect, test } from '@nocobase/test/e2e'; +import { kanbanURL } from './templates'; + +test.describe('kanban: open popup via URL', () => { + test('basic', async ({ page, mockPage, mockRecords }) => { + const nocoPage = await mockPage(kanbanURL).waitForInit(); + await mockRecords('kanban', [{ select: 'option1' }, { select: 'option2' }, { select: 'option3' }]); + await nocoPage.goto(); + + // 1. click a card to open a popup + await page.getByLabel('block-item-CollectionField-').filter({ hasText: 'option1' }).click(); + await expect( + page.getByTestId('drawer-Action.Container-kanban-View record').getByLabel('block-item-CollectionField-'), + ).toHaveText('Single select:Option1'); + + // 2. then reload the page, the popup should be opened + await page.reload(); + await expect( + page.getByTestId('drawer-Action.Container-kanban-View record').getByLabel('block-item-CollectionField-'), + ).toHaveText('Single select:Option1'); + + // 3. click the `Edit` button to open another popup + await page.getByLabel('action-Action-Edit-update-').click(); + await expect( + page.getByTestId('drawer-Action.Container-kanban-Edit record').getByLabel('block-item-CollectionField-'), + ).toHaveText('Single select:Option1'); + + // 4. then reload the page, the second popup should be opened + await page.reload(); + await expect( + page.getByTestId('drawer-Action.Container-kanban-Edit record').getByLabel('block-item-CollectionField-'), + ).toHaveText('Single select:Option1'); + + // 5. change the `Option` then close, the previous popup's content should be updated + await page.getByTestId('select-single').click(); + await page.getByRole('option', { name: 'Option2' }).click(); + await page.getByLabel('action-Action-Submit-submit-').click(); + await page.mouse.move(300, 0); + + await expect(page.getByLabel('block-item-CollectionField-kanban-details-kanban.select-Single select')).toHaveText( + 'Single select:Option2', + ); + + // close the first popup + // await page.locator('.ant-drawer-mask').click(); + // await expect(page.getByLabel('block-item-CollectionField-').filter({ hasText: 'option2' })).toHaveCount(2); + }); +});