fix: record picker cannot select data when editing (#2798)

* fix: record picker cannot select data when editing

* refactor: rename file name

* fix: just run the lazy load once

* test: add test

* test: fix test

* chore: better comment

---------

Co-authored-by: Rain <958414905@qq.com>
This commit is contained in:
katherinehhh 2023-10-11 19:28:17 +08:00 committed by GitHub
parent 2dc964d4f1
commit 72367a9f85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 453 additions and 86 deletions

View File

@ -0,0 +1,367 @@
import { expect, test } from '@nocobase/test/client';
const config = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
n7k4be0tpgt: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
properties: {
ai6ojrzwkiv: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
amsy67ggjr3: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
ajob701dt18: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'users:list',
'x-decorator-props': {
collection: 'users',
resource: 'users',
action: 'list',
params: {
pageSize: 20,
},
rowKey: 'id',
showIndex: true,
dragSort: false,
disableTemplate: false,
},
'x-designer': 'TableBlockDesigner',
'x-component': 'CardItem',
'x-filter-targets': [],
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'TableActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 'var(--nb-spacing)',
},
},
'x-uid': 'farfrcvsydq',
'x-async': false,
'x-index': 1,
},
t1o3f4r1j4q: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-initializer': 'TableColumnInitializers',
'x-component': 'TableV2',
'x-component-props': {
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
useProps: '{{ useTableBlockProps }}',
},
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Actions") }}',
'x-action-column': 'actions',
'x-decorator': 'TableV2.Column.ActionBar',
'x-component': 'TableV2.Column',
'x-designer': 'TableV2.ActionColumnDesigner',
'x-initializer': 'TableActionColumnInitializers',
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'DndContext',
'x-component': 'Space',
'x-component-props': {
split: '|',
},
properties: {
ezur2k7gz8k: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Edit") }}',
'x-action': 'update',
'x-designer': 'Action.Designer',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'drawer',
icon: 'EditOutlined',
},
'x-decorator': 'ACLActionProvider',
'x-designer-props': {
linkageAction: true,
},
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Edit record") }}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'TabPaneInitializers',
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Edit")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'RecordBlockInitializers',
properties: {
dv88ljcl0qw: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
'548w29v01eb': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
q83tzsynghl: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-acl-action-props': {
skipScopeCheck: false,
},
'x-acl-action': 'users:update',
'x-decorator': 'FormBlockProvider',
'x-decorator-props': {
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
action: 'get',
resource: 'users',
collection: 'users',
},
'x-designer': 'FormV2.Designer',
'x-component': 'CardItem',
'x-component-props': {},
properties: {
'8kzqujhwsyl': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'FormV2',
'x-component-props': {
useProps: '{{ useFormBlockProps }}',
},
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'FormItemInitializers',
properties: {
'3i87cpmv87x': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
am7wls8qu12: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
roles: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-designer': 'FormItem.Designer',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-collection-field': 'users.roles',
'x-component-props': {},
'x-uid': 'e196r19yspx',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ato3s57xtbt',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'dx3bqu24t50',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'rjbjrnn88sq',
'x-async': false,
'x-index': 1,
},
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'UpdateFormActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
'x-uid': 'zh8o2drtd9h',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'ytafkst3w0s',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ioqb8wteiwr',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'iavf0r5sl2n',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '994dlhgh1iz',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'k390f3n1rz7',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xm264vyzmkf',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'asl2rgfrz7p',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xfre469n0yi',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'gdjgqvxv6b1',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'fnctj4uc8xy',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '2dc4d3mlw3k',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '51i2scjmis1',
'x-async': false,
'x-index': 2,
},
},
'x-uid': '75owqo6visj',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'r4bvdbzinup',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'h066j2xouwn',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'qrkik5iy790',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '3vzzy7tipgq',
'x-async': true,
'x-index': 1,
},
};
// fix https://nocobase.height.app/T-2200
test('BUG: should be possible to change the value of the association field normally', async ({ page, mockPage }) => {
await mockPage(config).goto();
await page.getByTestId('table-block').getByText('Edit').click();
await expect(page.getByTestId('antd-select').getByText('AdminMemberRoot')).toBeVisible();
await page.getByTestId('antd-select').getByText('AdminMemberRoot').click();
await page.getByRole('option', { name: 'Member' }).click();
// 点击空白处,关闭下拉框。如不关闭,会挡住按钮,导致鼠标 hover 无效
await page
.getByTestId('action-drawer')
.locator('div')
.filter({ hasText: 'EditAdd tabRoles:AdminRoot Configure fieldsConfigure actionsAdd block' })
.nth(2)
.click();
await expect(page.getByTestId('antd-select').getByText('AdminRoot')).toBeVisible();
await page.getByTestId('configure-fields-button-of-form-item-users').hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.waitForTimeout(200);
await expect(page.getByTestId('antd-select').getByText('AdminRoot')).toBeVisible();
});

View File

@ -27,8 +27,8 @@ import { HTMLEncode } from '../input/shared';
import { useColorFields } from '../table-v2/Table.Column.Designer';
import { FilterFormDesigner } from './FormItem.FilterFormDesigner';
import { useEnsureOperatorsValid } from './SchemaSettingOptions';
import useLazyLoadDisplayAssociationFieldsOfForm from './hooks/useLazyLoadAssociationFieldOfForm';
import useLazyLoadAssociationFieldOfForm from './hooks/useLazyLoadAssociationFieldOfSubForm';
import useLazyLoadAssociationFieldOfForm from './hooks/useLazyLoadAssociationFieldOfForm';
import useLazyLoadDisplayAssociationFieldsOfForm from './hooks/useLazyLoadDisplayAssociationFieldsOfForm';
import useParseDefaultValue from './hooks/useParseDefaultValue';
export const FormItem: any = observer(

View File

@ -1,53 +1,66 @@
import { Field } from '@formily/core';
import { useField, useFieldSchema, useForm } from '@formily/react';
import { useField, useFieldSchema } from '@formily/react';
import _ from 'lodash';
import { useEffect } from 'react';
import { useFormBlockType, useRecord } from '../../../..';
import { useCollection, useCollectionManager } from '../../../../collection-manager';
import { useFlag } from '../../../../flag-provider';
import { useVariables } from '../../../../variables';
import { transformVariableValue } from '../../../../variables/utils/transformVariableValue';
/**
* Form
* Form
*
* - Display association fields
* -
* - record
* -
*/
const useLazyLoadDisplayAssociationFieldsOfForm = () => {
const useLazyLoadAssociationFieldOfForm = () => {
const { name } = useCollection();
const { getCollectionJoinField } = useCollectionManager();
const form = useForm();
const record = useRecord();
const fieldSchema = useFieldSchema();
const variables = useVariables();
const field = useField<Field>();
const { isInAssignFieldValues } = useFlag() || {};
const { type: formBlockType } = useFormBlockType();
const schemaName = fieldSchema.name.toString();
useEffect(() => {
// 在数据表管理页面,也存在 `a.b` 之类的 schema name其 collectionName 为 fields所以在这里排除一下 `name === 'fields'` 的情况
if (!schemaName.includes('.') || !variables || name === 'fields') {
if (isInAssignFieldValues || formBlockType === 'create') {
return;
}
const formVariable = {
name: '$nForm',
ctx: form.values,
collectionName: name,
};
const variableString = `{{ $nForm.${schemaName} }}`;
const collectionField = getCollectionJoinField(`${name}.${schemaName}`);
if (!collectionField) {
return console.error(new Error(`useLazyLoadAssociationField: ${schemaName} not found in collection ${name}`));
return;
}
const cloneRecord = { ...record };
delete cloneRecord['__parent'];
if (_.isEmpty(cloneRecord) || !variables || record[schemaName] != null) {
return;
}
// 通过模拟一个 record 变量,用解析变量的方法去获取关系字段的值
const recordVariable = {
name: '$nRecord',
ctx: cloneRecord,
collectionName: name,
};
const variableString = `{{ $nRecord.${schemaName} }}`;
variables
.parseVariable(variableString, formVariable)
.parseVariable(variableString, recordVariable)
.then((value) => {
field.value = transformVariableValue(value, { targetCollectionField: collectionField });
})
.catch((err) => {
console.error(err);
});
}, [schemaName.includes('.') ? form.values[schemaName.split('.')[0]] : null]);
}, []);
};
export default useLazyLoadDisplayAssociationFieldsOfForm;
export default useLazyLoadAssociationFieldOfForm;

View File

@ -1,66 +0,0 @@
import { Field } from '@formily/core';
import { useField, useFieldSchema } from '@formily/react';
import _ from 'lodash';
import { useEffect } from 'react';
import { useFormBlockType, useRecord } from '../../../..';
import { useCollection, useCollectionManager } from '../../../../collection-manager';
import { useFlag } from '../../../../flag-provider';
import { useVariables } from '../../../../variables';
import { transformVariableValue } from '../../../../variables/utils/transformVariableValue';
/**
* Form
*
* - record
* -
*/
const useLazyLoadAssociationFieldOfForm = () => {
const { name } = useCollection();
const { getCollectionJoinField } = useCollectionManager();
const record = useRecord();
const fieldSchema = useFieldSchema();
const variables = useVariables();
const field = useField<Field>();
const { isInAssignFieldValues } = useFlag() || {};
const { type: formBlockType } = useFormBlockType();
const schemaName = fieldSchema.name.toString();
useEffect(() => {
if (isInAssignFieldValues || formBlockType === 'create') {
return;
}
const collectionField = getCollectionJoinField(`${name}.${schemaName}`);
if (!collectionField) {
return;
}
const cloneRecord = { ...record };
delete cloneRecord['__parent'];
if (_.isEmpty(cloneRecord) || !variables || record[schemaName] != null) {
return;
}
// 通过模拟一个 record 变量,用解析变量的方法去获取关系字段的值
const recordVariable = {
name: '$nRecord',
ctx: cloneRecord,
collectionName: name,
};
const variableString = `{{ $nRecord.${schemaName} }}`;
variables
.parseVariable(variableString, recordVariable)
.then((value) => {
field.value = transformVariableValue(value, { targetCollectionField: collectionField });
})
.catch((err) => {
console.error(err);
});
}, [field, name, record, schemaName, isInAssignFieldValues]);
};
export default useLazyLoadAssociationFieldOfForm;

View File

@ -0,0 +1,53 @@
import { Field } from '@formily/core';
import { useField, useFieldSchema, useForm } from '@formily/react';
import { useEffect } from 'react';
import { useCollection, useCollectionManager } from '../../../../collection-manager';
import { useVariables } from '../../../../variables';
import { transformVariableValue } from '../../../../variables/utils/transformVariableValue';
/**
* Form
*
* - Display association fields
* -
*/
const useLazyLoadDisplayAssociationFieldsOfForm = () => {
const { name } = useCollection();
const { getCollectionJoinField } = useCollectionManager();
const form = useForm();
const fieldSchema = useFieldSchema();
const variables = useVariables();
const field = useField<Field>();
const schemaName = fieldSchema.name.toString();
useEffect(() => {
// 在数据表管理页面,也存在 `a.b` 之类的 schema name其 collectionName 为 fields所以在这里排除一下 `name === 'fields'` 的情况
if (!schemaName.includes('.') || !variables || name === 'fields') {
return;
}
const formVariable = {
name: '$nForm',
ctx: form.values,
collectionName: name,
};
const variableString = `{{ $nForm.${schemaName} }}`;
const collectionField = getCollectionJoinField(`${name}.${schemaName}`);
if (!collectionField) {
return console.error(new Error(`useLazyLoadAssociationField: ${schemaName} not found in collection ${name}`));
}
variables
.parseVariable(variableString, formVariable)
.then((value) => {
field.value = transformVariableValue(value, { targetCollectionField: collectionField });
})
.catch((err) => {
console.error(err);
});
}, [schemaName.includes('.') ? form.values[schemaName.split('.')[0]] : null]);
};
export default useLazyLoadDisplayAssociationFieldsOfForm;