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 69be2cb74b..f7673cc1aa 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 @@ -7,7 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { useFieldSchema } from '@formily/react'; +import { observer, useFieldSchema } from '@formily/react'; import { toArr } from '@formily/shared'; import React, { Fragment, useRef } from 'react'; import { useDesignable } from '../../'; @@ -33,72 +33,77 @@ const toValue = (value, placeholder) => { return value; }; -const ButtonTabList: React.FC = (props) => { - const fieldSchema = useFieldSchema(); - const { enableLink, tagColorField } = fieldSchema['x-component-props']; - const fieldNames = useFieldNames({ fieldNames: props.fieldNames }); - const insertViewer = useInsertSchema('Viewer'); - const { options: collectionField } = useAssociationFieldContext(); - const compile = useCompile(); - const { designable } = useDesignable(); - const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label'); - const { snapshot } = useActionContext(); - const ellipsisWithTooltipRef = useRef(); - const { getCollection } = useCollectionManager_deprecated(); - const targetCollection = getCollection(collectionField?.target); - const isTreeCollection = targetCollection?.template === 'tree'; - const { openPopup } = usePopupUtils(); - const recordData = useCollectionRecordData(); +const ButtonTabList: React.FC = observer( + (props) => { + const fieldSchema = useFieldSchema(); + const { enableLink, tagColorField } = fieldSchema['x-component-props']; + const fieldNames = useFieldNames({ fieldNames: props.fieldNames }); + const insertViewer = useInsertSchema('Viewer'); + const { options: collectionField } = useAssociationFieldContext(); + const compile = useCompile(); + const { designable } = useDesignable(); + const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label'); + const { snapshot } = useActionContext(); + const ellipsisWithTooltipRef = useRef(); + const { getCollection } = useCollectionManager_deprecated(); + const targetCollection = getCollection(collectionField?.target); + const isTreeCollection = targetCollection?.template === 'tree'; + const { openPopup } = usePopupUtils(); + const recordData = useCollectionRecordData(); - const renderRecords = () => - toArr(props.value).map((record, index, arr) => { - const value = record?.[fieldNames?.label || 'label']; - const label = isTreeCollection - ? transformNestedData(record) - .map((o) => o?.[fieldNames?.label || 'label']) - .join(' / ') - : isObject(value) - ? JSON.stringify(value) - : value; - const val = toValue(compile(label), 'N/A'); - const text = getTabFormatValue(compile(labelUiSchema), val, record[tagColorField]); - return ( - - - {snapshot ? ( - text - ) : enableLink !== false ? ( - { - props.setBtnHover(true); - }} - onClick={(e) => { - props.setBtnHover(true); - e.stopPropagation(); - e.preventDefault(); - if (designable) { - insertViewer(schema.Viewer); - } - openPopup({ - recordData: record, - parentRecordData: recordData, - }); - ellipsisWithTooltipRef?.current?.setPopoverVisible(false); - }} - > - {text} - - ) : ( - text - )} - - {index < arr.length - 1 ? , : null} - - ); - }); + const renderRecords = () => + toArr(props.value).map((record, index, arr) => { + const value = record?.[fieldNames?.label || 'label']; + const label = isTreeCollection + ? transformNestedData(record) + .map((o) => o?.[fieldNames?.label || 'label']) + .join(' / ') + : isObject(value) + ? JSON.stringify(value) + : value; + const val = toValue(compile(label), 'N/A'); + const text = getTabFormatValue(compile(labelUiSchema), val, record[tagColorField]); + return ( + + + {snapshot ? ( + text + ) : enableLink !== false ? ( + { + props.setBtnHover(true); + }} + onClick={(e) => { + props.setBtnHover(true); + e.stopPropagation(); + e.preventDefault(); + if (designable) { + insertViewer(schema.Viewer); + } + openPopup({ + recordData: record, + parentRecordData: recordData, + }); + ellipsisWithTooltipRef?.current?.setPopoverVisible(false); + }} + > + {text} + + ) : ( + text + )} + + {index < arr.length - 1 ? , : null} + + ); + }); - return <>{renderRecords()}; -}; + return <>{renderRecords()}; + }, + { + displayName: 'ButtonTabList', + }, +); export const ReadPrettyInternalTag: React.FC = (props: any) => { return ; diff --git a/packages/core/client/src/schema-component/antd/association-field/__e2e__/tag.test.ts b/packages/core/client/src/schema-component/antd/association-field/__e2e__/tag.test.ts new file mode 100644 index 0000000000..806335d119 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/association-field/__e2e__/tag.test.ts @@ -0,0 +1,31 @@ +/** + * 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 { shouldRefreshBlockImmediatelyAfterDataChanges } from './template'; + +test.describe('tag in details block', () => { + test('should refresh block immediately after data changes', async ({ page, mockPage, mockRecord }) => { + const nocoPage = await mockPage(shouldRefreshBlockImmediatelyAfterDataChanges).waitForInit(); + await mockRecord('test', { manyToOne: 'admin' }); + await nocoPage.goto(); + + // 1. 一开始字段值显示的是 ‘admin’ + await expect(page.getByLabel('block-item-CollectionField-').getByText('admin')).toBeVisible(); + + // 2. 修改数据后,字段值应立即刷新 + await page.getByLabel('action-Action-Edit-update-').click(); + await page.getByTestId('select-object-single').click(); + await page.getByRole('option', { name: 'member' }).click(); + await page.getByLabel('action-Action-Submit-submit-').click(); + + await page.waitForTimeout(1000); + await expect(page.getByLabel('block-item-CollectionField-').getByText('member')).toBeVisible(); + }); +}); diff --git a/packages/core/client/src/schema-component/antd/association-field/__e2e__/template.ts b/packages/core/client/src/schema-component/antd/association-field/__e2e__/template.ts new file mode 100644 index 0000000000..f5d18634bc --- /dev/null +++ b/packages/core/client/src/schema-component/antd/association-field/__e2e__/template.ts @@ -0,0 +1,448 @@ +/** + * 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 shouldRefreshBlockImmediatelyAfterDataChanges = { + collections: [ + { + name: 'test', + fields: [ + { + name: 'manyToOne', + interface: 'm2o', + uiSchema: { + 'x-component': 'AssociationField', + 'x-component-props': { + multiple: false, + }, + title: 'manyToOne', + }, + target: 'roles', + targetKey: 'name', + }, + ], + }, + ], + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + 'x-app-version': '1.6.0-alpha.2', + properties: { + r86ehhl96yp: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + 'x-app-version': '1.6.0-alpha.2', + properties: { + '7mc3c7g2ztk': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.6.0-alpha.2', + properties: { + zsdtdzso928: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.6.0-alpha.2', + properties: { + qx97se01bc9: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action': 'test:view', + 'x-decorator': 'DetailsBlockProvider', + 'x-use-decorator-props': 'useDetailsWithPaginationDecoratorProps', + 'x-decorator-props': { + dataSource: 'main', + collection: 'test', + readPretty: true, + action: 'list', + params: { + pageSize: 1, + }, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:detailsWithPagination', + 'x-component': 'CardItem', + 'x-app-version': '1.6.0-alpha.2', + properties: { + '5bw8wirh3ii': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Details', + 'x-read-pretty': true, + 'x-use-component-props': 'useDetailsWithPaginationProps', + 'x-app-version': '1.6.0-alpha.2', + properties: { + ouvwqvlrs0c: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'details:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + 'x-app-version': '1.6.0-alpha.2', + properties: { + q7ovvwwcw7f: { + _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: 'test', + }, + 'x-decorator': 'ACLActionProvider', + 'x-app-version': '1.6.0-alpha.2', + 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.6.0-alpha.2', + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + 'x-app-version': '1.6.0-alpha.2', + 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.6.0-alpha.2', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:common:addBlock', + 'x-app-version': '1.6.0-alpha.2', + properties: { + sude45f40gf: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.6.0-alpha.2', + properties: { + ah0uni8z97n: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.6.0-alpha.2', + properties: { + kc0w161zdlt: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action-props': { + skipScopeCheck: false, + }, + 'x-acl-action': 'test:update', + 'x-decorator': 'FormBlockProvider', + 'x-use-decorator-props': 'useEditFormBlockDecoratorProps', + 'x-decorator-props': { + action: 'get', + dataSource: 'main', + collection: 'test', + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:editForm', + 'x-component': 'CardItem', + 'x-app-version': '1.6.0-alpha.2', + properties: { + xhnj06ts6qh: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'FormV2', + 'x-use-component-props': 'useEditFormBlockProps', + 'x-app-version': '1.6.0-alpha.2', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'form:configureFields', + 'x-app-version': '1.6.0-alpha.2', + properties: { + hxmmg8dre28: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.6.0-alpha.2', + properties: { + eohagwrc2g9: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.6.0-alpha.2', + properties: { + manyToOne: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'test.manyToOne', + 'x-component-props': { + fieldNames: { + value: 'name', + label: 'name', + }, + }, + 'x-app-version': '1.6.0-alpha.2', + 'x-uid': 'b6jge6icuzy', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'f3t4dx8n0g7', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'kqqx2s52nvb', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '61qq15cmpm0', + 'x-async': false, + 'x-index': 1, + }, + '798dy80zzpf': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'editForm:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + }, + 'x-app-version': '1.6.0-alpha.2', + properties: { + n6p8c9gaol3: { + _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.6.0-alpha.2', + 'x-uid': 'ykons2vq6xq', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'xaavi2ewome', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '059x400jqrs', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '6ntyxjo1ixr', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'l384eqm0848', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'xxsxrdn4oad', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'rg85la28jjx', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'pxoxj6frur4', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vdutlbkiqi4', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'x53treofune', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'epym7rg939z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7nw6uf8r2xm', + '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.6.0-alpha.2', + properties: { + dbgh1fs4wg1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.6.0-alpha.2', + properties: { + wpgxiamxdt3: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.6.0-alpha.2', + properties: { + manyToOne: { + 'x-uid': '45xqlylaeie', + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'test.manyToOne', + 'x-component-props': { + fieldNames: { + value: 'name', + label: 'name', + }, + mode: 'Tag', + }, + 'x-app-version': '1.6.0-alpha.2', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'cv8tffpkdkb', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fzm9py3yc7r', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '817m2t37tnq', + 'x-async': false, + 'x-index': 2, + }, + pagination: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Pagination', + 'x-use-component-props': 'useDetailsPaginationProps', + 'x-app-version': '1.6.0-alpha.2', + 'x-uid': 'ukp3q4sxgnb', + 'x-async': false, + 'x-index': 3, + }, + }, + 'x-uid': '6mjcax9yfg9', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '539xf5hfzax', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'wsk1gdy6leb', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'a0uip1nhxx5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3wmg81ih321', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4glrwic98l2', + 'x-async': true, + 'x-index': 1, + }, +};