From 5fb6962ec5c23e49e6a5bec9a4d7fd7ce052d1b1 Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Thu, 7 Nov 2024 21:18:59 +0800 Subject: [PATCH] fix(defaultValue): fix errors in special cases (#5607) * fix(defaultValue): fix errors in special cases * test: add e2e test * chore: make unit tests pass * chore: make e2e more stable * chore: fix e2e --- .../table/__e2e__/subTable.test.ts | 65 ++++ .../table/__e2e__/templatesOfBug.ts | 315 ++++++++++++++++++ .../__tests__/hooks/useSpecialCase.test.ts | 6 +- .../form-item/hooks/useParseDefaultValue.ts | 9 +- .../antd/form-item/hooks/useSpecialCase.ts | 4 +- 5 files changed, 390 insertions(+), 9 deletions(-) create mode 100644 packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/subTable.test.ts diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/subTable.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/subTable.test.ts new file mode 100644 index 0000000000..d40d6003ae --- /dev/null +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/subTable.test.ts @@ -0,0 +1,65 @@ +/** + * 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 { subTableDefaultValue } from './templatesOfBug'; + +test.describe('subTable', () => { + test('defaultValue', async ({ page, mockPage }) => { + // Default values have been set as: + // staff: {{ $user }} + // timeStart: 2024-11-07 + // timeEnd: {{ $iteration.timeStart }} + await mockPage(subTableDefaultValue).goto(); + + // 1. Click "Add new" to add a row, default values should display correctly + await page.getByRole('button', { name: 'Add new' }).click(); + await expect(page.getByTestId('select-object-single').getByText('Super Admin')).toBeVisible(); + await expect( + page.getByLabel('block-item-CollectionField-group-form-group.timeStart-').getByPlaceholder('Select date'), + ).toHaveValue('2024-11-07'); + await expect( + page.getByLabel('block-item-CollectionField-group-form-group.timeEnd-').getByPlaceholder('Select date'), + ).toHaveValue('2024-11-07'); + + // 2. Click "Add new" again to add another row, default values should display correctly + await page.getByRole('button', { name: 'Add new' }).click(); + await expect(page.getByTestId('select-object-single').getByText('Super Admin').nth(1)).toBeVisible(); + await expect( + page.getByLabel('block-item-CollectionField-group-form-group.timeStart-').nth(1).getByPlaceholder('Select date'), + ).toHaveValue('2024-11-07'); + await expect( + page.getByLabel('block-item-CollectionField-group-form-group.timeEnd-').nth(1).getByPlaceholder('Select date'), + ).toHaveValue('2024-11-07'); + + // 3. After modifying timeStart in both first and second rows, their corresponding timeEnd should stay synchronized + await page.getByPlaceholder('Select date').first().click(); + await page.getByRole('cell', { name: '8', exact: true }).click(); + await page.getByPlaceholder('Select date').nth(2).click(); + await page.getByRole('cell', { name: '8', exact: true }).click(); + + // First row + await expect( + page.getByLabel('block-item-CollectionField-group-form-group.timeStart-').nth(0).getByPlaceholder('Select date'), + ).toHaveValue('2024-11-08'); + await expect( + page.getByLabel('block-item-CollectionField-group-form-group.timeEnd-').nth(0).getByPlaceholder('Select date'), + ).toHaveValue('2024-11-08'); + await expect(page.getByTestId('select-object-single').getByText('Super Admin').nth(0)).toBeVisible(); + + // Second row + await expect( + page.getByLabel('block-item-CollectionField-group-form-group.timeStart-').nth(1).getByPlaceholder('Select date'), + ).toHaveValue('2024-11-08'); + await expect( + page.getByLabel('block-item-CollectionField-group-form-group.timeEnd-').nth(1).getByPlaceholder('Select date'), + ).toHaveValue('2024-11-08'); + await expect(page.getByTestId('select-object-single').getByText('Super Admin').nth(1)).toBeVisible(); + }); +}); diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/templatesOfBug.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/templatesOfBug.ts index 72ba397b28..2216aa5c42 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/templatesOfBug.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/templatesOfBug.ts @@ -7926,3 +7926,318 @@ export const differentURL_DifferentPopupContent = { 'x-index': 1, }, }; +export const subTableDefaultValue = { + collections: [ + { + name: 'people', + fields: [ + { + name: 'group', + interface: 'o2m', + target: 'group', + targetKey: 'id', + }, + ], + }, + { + name: 'group', + fields: [ + { + name: 'staff', + interface: 'm2o', + target: 'users', + targetKey: 'id', + }, + { + name: 'timeStart', + interface: 'datetime', + }, + { + name: 'timeEnd', + interface: 'datetime', + }, + ], + }, + ], + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + gxs8yrx2r0h: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + properties: { + '823g8sd0un5': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.46-beta', + properties: { + s8gra6scztq: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.46-beta', + properties: { + k0hmoift98n: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + 'x-acl-action': 'people:create', + 'x-decorator': 'FormBlockProvider', + 'x-use-decorator-props': 'useCreateFormBlockDecoratorProps', + 'x-decorator-props': { + dataSource: 'main', + collection: 'people', + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:createForm', + 'x-component': 'CardItem', + 'x-app-version': '1.3.46-beta', + properties: { + qj7mcan7daw: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'FormV2', + 'x-use-component-props': 'useCreateFormBlockProps', + 'x-app-version': '1.3.46-beta', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'form:configureFields', + 'x-app-version': '1.3.46-beta', + properties: { + zx4nzvdjpzq: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.46-beta', + properties: { + osnr82axoai: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.46-beta', + properties: { + group: { + 'x-uid': '001ix5hsr0t', + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'people.group', + 'x-component-props': { + fieldNames: { + label: 'id', + value: 'id', + }, + mode: 'SubTable', + }, + 'x-app-version': '1.3.46-beta', + default: null, + properties: { + '2go92pz3q7s': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'AssociationField.SubTable', + 'x-initializer': 'table:configureColumns', + 'x-initializer-props': { + action: false, + }, + 'x-index': 1, + 'x-app-version': '1.3.46-beta', + properties: { + '9e59ms847h0': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableV2.Column.Decorator', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-settings': 'fieldSettings:TableColumn', + 'x-component': 'TableV2.Column', + 'x-app-version': '1.3.46-beta', + properties: { + staff: { + 'x-uid': 'qfin4x5otua', + _isJSONSchemaObject: true, + version: '2.0', + 'x-collection-field': 'group.staff', + 'x-component': 'CollectionField', + 'x-component-props': { + fieldNames: { + label: 'nickname', + value: 'id', + }, + ellipsis: true, + size: 'small', + }, + 'x-decorator': 'FormItem', + 'x-decorator-props': { + labelStyle: { + display: 'none', + }, + }, + 'x-app-version': '1.3.46-beta', + default: '{{$user}}', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'gg09b3sv8p7', + 'x-async': false, + 'x-index': 1, + }, + kd80mzx281w: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableV2.Column.Decorator', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-settings': 'fieldSettings:TableColumn', + 'x-component': 'TableV2.Column', + 'x-app-version': '1.3.46-beta', + properties: { + timeStart: { + 'x-uid': 'sm3t7czuvu1', + _isJSONSchemaObject: true, + version: '2.0', + 'x-collection-field': 'group.timeStart', + 'x-component': 'CollectionField', + 'x-component-props': {}, + 'x-decorator': 'FormItem', + 'x-decorator-props': { + labelStyle: { + display: 'none', + }, + }, + 'x-app-version': '1.3.46-beta', + default: '2024-11-06T16:00:00.000Z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ojxw1dbkoi0', + 'x-async': false, + 'x-index': 2, + }, + st8bezxhvax: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableV2.Column.Decorator', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-settings': 'fieldSettings:TableColumn', + 'x-component': 'TableV2.Column', + 'x-app-version': '1.3.46-beta', + properties: { + timeEnd: { + 'x-uid': '0nks9kfq38u', + _isJSONSchemaObject: true, + version: '2.0', + 'x-collection-field': 'group.timeEnd', + 'x-component': 'CollectionField', + 'x-component-props': {}, + 'x-decorator': 'FormItem', + 'x-decorator-props': { + labelStyle: { + display: 'none', + }, + }, + 'x-app-version': '1.3.46-beta', + default: '{{$iteration.timeStart}}', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8lynj1uxlbv', + 'x-async': false, + 'x-index': 3, + }, + }, + 'x-uid': 'bsd9l4pl5by', + 'x-async': false, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 't9bhhahpuln', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'm94ti0kcqqh', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '9po3njajeii', + 'x-async': false, + 'x-index': 1, + }, + gr2fjadfkef: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'createForm:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + }, + 'x-app-version': '1.3.46-beta', + 'x-uid': 'v5zhsmloels', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'o9fcw2oo6vi', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7361xvr7amv', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'an5bhgxdio8', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '0hixaaou1xh', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'nfot2l93mkp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3lfdt8s5cay', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/core/client/src/schema-component/antd/form-item/__tests__/hooks/useSpecialCase.test.ts b/packages/core/client/src/schema-component/antd/form-item/__tests__/hooks/useSpecialCase.test.ts index 4c741aef8e..0020c1e56f 100644 --- a/packages/core/client/src/schema-component/antd/form-item/__tests__/hooks/useSpecialCase.test.ts +++ b/packages/core/client/src/schema-component/antd/form-item/__tests__/hooks/useSpecialCase.test.ts @@ -72,7 +72,7 @@ describe('isSpecialCaseField', () => { type: 'hasOne', }; const fieldSchema: any = { - default: '', + default: '{{ $context }}', }; const getCollectionField = vi.fn().mockReturnValue(null); @@ -92,7 +92,7 @@ describe('isSpecialCaseField', () => { }, }; const fieldSchema: any = { - default: '', + default: '{{ $context }}', parent: parentFieldSchema, }; @@ -116,7 +116,7 @@ describe('isSpecialCaseField', () => { }, }; const fieldSchema: any = { - default: '', + default: '{{ $context }}', parent: parentFieldSchema, }; const getCollectionField = vi.fn().mockReturnValue({ diff --git a/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts b/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts index 703b2a43b8..10c5a79152 100644 --- a/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts +++ b/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts @@ -16,8 +16,9 @@ import { useCallback, useEffect } from 'react'; import { useRecordIndex } from '../../../../../src/record-provider'; import { useOperators } from '../../../../block-provider/CollectOperators'; import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider'; -import { InheritanceCollectionMixin, useCollection_deprecated } from '../../../../collection-manager'; +import { InheritanceCollectionMixin } from '../../../../collection-manager'; import { useCollectionRecord } from '../../../../data-source/collection-record/CollectionRecordProvider'; +import { useCollection } from '../../../../data-source/collection/CollectionProvider'; import { DataSourceManager } from '../../../../data-source/data-source/DataSourceManager'; import { useDataSourceManager } from '../../../../data-source/data-source/DataSourceManagerProvider'; import { useFlag } from '../../../../flag-provider'; @@ -40,7 +41,7 @@ const useParseDefaultValue = () => { const record = useCollectionRecord(); const { isInAssignFieldValues, isInSetDefaultValueDialog, isInFormDataTemplate, isInSubTable, isInSubForm } = useFlag() || {}; - const { getField } = useCollection_deprecated(); + const collection = useCollection(); const { isSpecialCase, setDefaultValue } = useSpecialCase(); const index = useRecordIndex(); const { type, form } = useFormBlockContext(); @@ -92,7 +93,7 @@ const useParseDefaultValue = () => { } field.loading = true; - const collectionField = !fieldSchema.name.toString().includes('.') && getField(fieldSchema.name); + const collectionField = !fieldSchema.name.toString().includes('.') && collection?.getField(fieldSchema.name); if (process.env.NODE_ENV !== 'production') { if (!collectionField) { @@ -191,7 +192,7 @@ const useParseDefaultValue = () => { // 解决子表格(或子表单)中新增一行数据时,默认值不生效的问题 field.setValue(fieldSchema.default); } - }, [fieldSchema.default, localVariables, type, getOperator, dm]); + }, [fieldSchema.default, localVariables, type, getOperator, dm, collection]); }; export default useParseDefaultValue; diff --git a/packages/core/client/src/schema-component/antd/form-item/hooks/useSpecialCase.ts b/packages/core/client/src/schema-component/antd/form-item/hooks/useSpecialCase.ts index 4cff8dd9ac..1327f0d2f1 100644 --- a/packages/core/client/src/schema-component/antd/form-item/hooks/useSpecialCase.ts +++ b/packages/core/client/src/schema-component/antd/form-item/hooks/useSpecialCase.ts @@ -92,8 +92,8 @@ export function isSpecialCaseField({ fieldSchema: Schema; getCollectionField: (name: string) => CollectionFieldOptions_deprecated; }) { - // 排除掉“当前对象”这个变量 - if (fieldSchema.default.includes('$iteration')) { + // 只针对“表格选中记录”变量有效 + if (!fieldSchema.default || !fieldSchema.default.includes('$context')) { return false; }