mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 05:29:26 +08:00
fix(linkage-rules): hidden required fields should not affect form submission (#6709)
* test: add e2e test * fix(linkage-rules): hidden required fields should not affect form submission * chore: fix e2e
This commit is contained in:
parent
ec0e1787d4
commit
386e76661d
@ -306,6 +306,7 @@ test.describe('configure actions column', () => {
|
||||
await page.getByText('Actions', { exact: true }).hover();
|
||||
await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover();
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
await page.mouse.move(500, 0);
|
||||
|
||||
await page.getByText('Actions', { exact: true }).hover();
|
||||
await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover();
|
||||
|
@ -247,6 +247,13 @@ function getSubscriber(
|
||||
// 在 FormItem 中有使用这个属性来判断字段是否被隐藏
|
||||
field.data.hidden = true;
|
||||
|
||||
// 如果字段是必填的,并且被隐藏(保留值)了,那么就需要把 required 设置为 false,否则有可能会导致表单验证失败;
|
||||
// 进而导致点击提交按钮无效的问题。
|
||||
if (field.required) {
|
||||
field.required = false;
|
||||
field.data.prevRequired = true;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
field.setState((state) => {
|
||||
state.display = 'visible';
|
||||
@ -257,6 +264,13 @@ function getSubscriber(
|
||||
field.data = field.data || {};
|
||||
// 在 FormItem 中有使用这个属性来判断字段是否被隐藏
|
||||
field.data.hidden = false;
|
||||
|
||||
// 当“隐藏(保留值)”的字段再次显示时,恢复“必填”的状态
|
||||
if (fieldName === 'display' && lastState?.value === 'visible' && field.data.prevRequired) {
|
||||
delete field.data.prevRequired;
|
||||
field.required = true;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
field.setState((state) => {
|
||||
state[fieldName] = lastState?.value;
|
||||
|
@ -10,6 +10,7 @@
|
||||
import { expect, test } from '@nocobase/test/e2e';
|
||||
import {
|
||||
formFieldDependsOnSubtableFieldsWithLinkageRules,
|
||||
whenARequiredFieldIsSetToHideRetainValueItShouldBeAbleToSubmitTheFormNormally,
|
||||
whenClearingARelationshipFieldTheValueOfTheAssociatedFieldShouldBeCleared,
|
||||
whenSetToHideRetainedValueItShouldNotImpactTheFieldSDefaultValueVariables,
|
||||
} from './template';
|
||||
@ -83,6 +84,28 @@ test.describe('linkage rules', () => {
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('When a required field is set to "Hide (retain value)", it should be able to submit the form normally', async ({
|
||||
mockPage,
|
||||
page,
|
||||
}) => {
|
||||
await mockPage(whenARequiredFieldIsSetToHideRetainValueItShouldBeAbleToSubmitTheFormNormally).goto();
|
||||
|
||||
// 1. 输入昵称
|
||||
await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.nickname-Nickname')
|
||||
.getByRole('textbox')
|
||||
.fill('123456');
|
||||
|
||||
// 2. 点击提交
|
||||
await page.getByLabel('action-Action-Submit-submit-').click();
|
||||
|
||||
// 3. 应该能正常提交,不应该被拦截
|
||||
await page.reload();
|
||||
await expect(
|
||||
page.getByLabel('block-item-CardItem-users-table').getByRole('cell', { name: '123456' }),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('When clearing a relationship field, the value of the associated field should be cleared', async ({
|
||||
page,
|
||||
mockPage,
|
||||
|
@ -2215,3 +2215,376 @@ export const accessControlActionWithTable = {
|
||||
'x-index': 1,
|
||||
},
|
||||
};
|
||||
export const whenARequiredFieldIsSetToHideRetainValueItShouldBeAbleToSubmitTheFormNormally = {
|
||||
tabSchema: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'page:addBlock',
|
||||
properties: {
|
||||
phefo1qp4yu: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-app-version': '1.6.21',
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
e94p5oj6num: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-app-version': '1.6.21',
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
fpwdszedqqt: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-toolbar': 'BlockSchemaToolbar',
|
||||
'x-settings': 'blockSettings:createForm',
|
||||
'x-component': 'CardItem',
|
||||
'x-decorator': 'FormBlockProvider',
|
||||
'x-acl-action': 'users:create',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-decorator-props': {
|
||||
collection: 'users',
|
||||
dataSource: 'main',
|
||||
},
|
||||
'x-acl-action-props': {
|
||||
skipScopeCheck: true,
|
||||
},
|
||||
_isJSONSchemaObject: true,
|
||||
'x-use-decorator-props': 'useCreateFormBlockDecoratorProps',
|
||||
properties: {
|
||||
fy0innr5s9v: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'FormV2',
|
||||
'x-app-version': '1.6.21',
|
||||
_isJSONSchemaObject: true,
|
||||
'x-use-component-props': 'useCreateFormBlockProps',
|
||||
properties: {
|
||||
grid: {
|
||||
type: 'void',
|
||||
'x-uid': '7wwn4d0722h',
|
||||
version: '2.0',
|
||||
'x-component': 'Grid',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-initializer': 'form:configureFields',
|
||||
'x-linkage-rules': [
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
operator: 'hidden',
|
||||
targetFields: ['username'],
|
||||
},
|
||||
],
|
||||
condition: {
|
||||
$and: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
t9ik3qkj7mv: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-app-version': '1.6.21',
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
szfeenblh7q: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-app-version': '1.6.21',
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
username: {
|
||||
type: 'string',
|
||||
version: '2.0',
|
||||
'x-toolbar': 'FormItemSchemaToolbar',
|
||||
'x-settings': 'fieldSettings:FormItem',
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-component-props': {},
|
||||
'x-collection-field': 'users.username',
|
||||
_isJSONSchemaObject: true,
|
||||
'x-uid': 'w7j6pbapyz2',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '4p4e653i4sg',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'ojqn0csar3b',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
ij66m020mn5: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-app-version': '1.6.21',
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
'95nih4mt2lf': {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-app-version': '1.6.21',
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
nickname: {
|
||||
type: 'string',
|
||||
version: '2.0',
|
||||
'x-toolbar': 'FormItemSchemaToolbar',
|
||||
'x-settings': 'fieldSettings:FormItem',
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-component-props': {},
|
||||
'x-collection-field': 'users.nickname',
|
||||
_isJSONSchemaObject: true,
|
||||
'x-uid': 'rpmd8swoor7',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'h0z7u62fwn3',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'b3v741bdvtt',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
o5pser9a4zy: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'ActionBar',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-initializer': 'createForm:configureActions',
|
||||
'x-component-props': {
|
||||
layout: 'one-column',
|
||||
},
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
g5aat11143b: {
|
||||
type: 'void',
|
||||
title: '{{ t("Submit") }}',
|
||||
version: '2.0',
|
||||
'x-action': 'submit',
|
||||
'x-toolbar': 'ActionSchemaToolbar',
|
||||
'x-settings': 'actionSettings:createSubmit',
|
||||
'x-component': 'Action',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-action-settings': {},
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
htmlType: 'submit',
|
||||
},
|
||||
_isJSONSchemaObject: true,
|
||||
'x-use-component-props': 'useCreateActionProps',
|
||||
'x-uid': 'doarbo27x5f',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'n5j6q3dfzst',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': '1k05k8tv3fp',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '4bq4fts48ec',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'tgjtas03gh6',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'raas2rrgtgq',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
gqnjk39afuj: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-app-version': '1.6.21',
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
fvn883pa5lt: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-app-version': '1.6.21',
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
'0wlnuaz623s': {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-toolbar': 'BlockSchemaToolbar',
|
||||
'x-settings': 'blockSettings:table',
|
||||
'x-component': 'CardItem',
|
||||
'x-decorator': 'TableBlockProvider',
|
||||
'x-acl-action': 'users:list',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-filter-targets': [],
|
||||
'x-decorator-props': {
|
||||
action: 'list',
|
||||
params: {
|
||||
pageSize: 20,
|
||||
},
|
||||
rowKey: 'id',
|
||||
dragSort: false,
|
||||
showIndex: true,
|
||||
collection: 'users',
|
||||
dataSource: 'main',
|
||||
},
|
||||
_isJSONSchemaObject: true,
|
||||
'x-use-decorator-props': 'useTableBlockDecoratorProps',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'ActionBar',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-initializer': 'table:configureActions',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
marginBottom: 'var(--nb-spacing)',
|
||||
},
|
||||
},
|
||||
_isJSONSchemaObject: true,
|
||||
'x-uid': 'q7y8z49vkvs',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
e7dmvrnhhhw: {
|
||||
type: 'array',
|
||||
version: '2.0',
|
||||
'x-component': 'TableV2',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-initializer': 'table:configureColumns',
|
||||
'x-component-props': {
|
||||
rowKey: 'id',
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
},
|
||||
_isJSONSchemaObject: true,
|
||||
'x-use-component-props': 'useTableBlockProps',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
title: '{{ t("Actions") }}',
|
||||
version: '2.0',
|
||||
'x-toolbar': 'TableColumnSchemaToolbar',
|
||||
'x-settings': 'fieldSettings:TableColumn',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-decorator': 'TableV2.Column.ActionBar',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-initializer': 'table:configureItemActions',
|
||||
'x-action-column': 'actions',
|
||||
'x-toolbar-props': {
|
||||
initializer: 'table:configureItemActions',
|
||||
},
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
wwt3qrk0ro0: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-component': 'Space',
|
||||
'x-decorator': 'DndContext',
|
||||
'x-app-version': '1.6.21',
|
||||
'x-component-props': {
|
||||
split: '|',
|
||||
},
|
||||
_isJSONSchemaObject: true,
|
||||
'x-uid': '58ymkonaijm',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '46zml84998i',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
d1qruxltt1t: {
|
||||
type: 'void',
|
||||
version: '2.0',
|
||||
'x-toolbar': 'TableColumnSchemaToolbar',
|
||||
'x-settings': 'fieldSettings:TableColumn',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-app-version': '1.6.21',
|
||||
_isJSONSchemaObject: true,
|
||||
properties: {
|
||||
nickname: {
|
||||
version: '2.0',
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': null,
|
||||
'x-app-version': '1.6.21',
|
||||
'x-read-pretty': true,
|
||||
'x-component-props': {
|
||||
ellipsis: true,
|
||||
},
|
||||
'x-decorator-props': {
|
||||
labelStyle: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
'x-collection-field': 'users.nickname',
|
||||
_isJSONSchemaObject: true,
|
||||
'x-uid': 'ut8m8l3qhzn',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'c2592dzb9s1',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': 'nntvcjy39cg',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': 'o5gizqch1wr',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '8awil00a9wo',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'i1dnlv9n2k0',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
name: 'ri6dthungp3',
|
||||
'x-uid': 'b2u59skxpsy',
|
||||
'x-async': true,
|
||||
'x-index': 1,
|
||||
},
|
||||
};
|
||||
|
@ -31,6 +31,21 @@ function getPageMenuSchema({ pageSchemaUid, tabSchemaUid, tabSchemaName }) {
|
||||
};
|
||||
}
|
||||
|
||||
function getPageMenuSchemaWithTabSchema({ tabSchema }) {
|
||||
if (!tabSchema) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'void',
|
||||
'x-component': 'Page',
|
||||
properties: {
|
||||
[tabSchema.name]: tabSchema,
|
||||
},
|
||||
'x-uid': uid(),
|
||||
};
|
||||
}
|
||||
|
||||
export * from '@playwright/test';
|
||||
|
||||
export { defineConfig };
|
||||
@ -193,10 +208,17 @@ export interface PageConfig {
|
||||
*/
|
||||
collections?: CollectionSetting[];
|
||||
/**
|
||||
* @deprecate 在菜单被重构之后,没有办法直接复制完整的页面 Schema 了。所以这个选项不推荐使用了。
|
||||
* 推荐使用 tabSchema,复制一个页面 tab 的 Schema 传给 tabSchema。
|
||||
*
|
||||
* 页面整体的 Schema
|
||||
* @default undefined
|
||||
*/
|
||||
pageSchema?: any;
|
||||
/**
|
||||
* 页面 Tab 的 Schema。当 pageSchema 和 tabSchema 都存在时,最终显示的会是 tabSchema 的内容
|
||||
*/
|
||||
tabSchema?: any;
|
||||
/** 如果为 true 则表示不会更改 PageSchema 的 uid */
|
||||
keepUid?: boolean;
|
||||
/** 在 URL 中的 uid,例如:/admin/0ig6xhe03u2 */
|
||||
@ -217,6 +239,7 @@ interface CreatePageOptions {
|
||||
url?: PageConfig['url'];
|
||||
name?: string;
|
||||
pageSchema?: any;
|
||||
tabSchema?: any;
|
||||
/** 如果为 true 则表示不会更改 PageSchema 的 uid */
|
||||
keepUid?: boolean;
|
||||
/** 在 URL 中的 uid,例如:/admin/0ig6xhe03u2 */
|
||||
@ -367,6 +390,7 @@ export class NocoPage {
|
||||
type: this.options?.type,
|
||||
name: this.options?.name,
|
||||
pageSchema: this.options?.pageSchema,
|
||||
tabSchema: this.options?.tabSchema,
|
||||
url: this.options?.url,
|
||||
keepUid: this.options?.keepUid,
|
||||
pageUid: this.options?.pageUid,
|
||||
@ -737,13 +761,16 @@ const updateUidOfPageSchema = (uiSchema: any) => {
|
||||
* 在 NocoBase 中创建一个页面
|
||||
*/
|
||||
const createPage = async (options?: CreatePageOptions) => {
|
||||
const { type = 'page', url, name, pageSchema, keepUid, pageUid: pageUidFromOptions } = options || {};
|
||||
const { type = 'page', url, name, pageSchema, tabSchema, keepUid, pageUid: pageUidFromOptions } = options || {};
|
||||
const api = await request.newContext({
|
||||
storageState: process.env.PLAYWRIGHT_AUTH_FILE,
|
||||
});
|
||||
|
||||
const schema = getPageMenuSchemaWithTabSchema({ tabSchema }) || pageSchema;
|
||||
|
||||
const state = await api.storageState();
|
||||
const headers = getHeaders(state);
|
||||
const newPageSchema = keepUid ? pageSchema : updateUidOfPageSchema(pageSchema);
|
||||
const newPageSchema = keepUid ? schema : updateUidOfPageSchema(schema);
|
||||
const pageSchemaUid = newPageSchema?.['x-uid'] || uid();
|
||||
const newTabSchemaUid = uid();
|
||||
const newTabSchemaName = uid();
|
||||
|
Loading…
x
Reference in New Issue
Block a user