fix: ConfigProvider form validateMessages nesting error (#43480)

* test: add unit case

* fix:  `ConfigProvider` form validateMessages nesting error

nesting error: https://github.com/ant-design/ant-design/issues/43210
merge error: 5ce9818401/components/config-provider/__tests__/form.test.tsx (L100-L110)

* test: update case
This commit is contained in:
Wuxh 2023-07-12 11:26:43 +08:00 committed by GitHub
parent f57c688db5
commit e59016b3bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 108 additions and 10 deletions

View File

@ -1,10 +1,13 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import type { ValidateMessages } from 'rc-field-form/es/interface';
import ConfigProvider from '..'; import ConfigProvider from '..';
import { render } from '../../../tests/utils'; import { render, waitFakeTimer, fireEvent } from '../../../tests/utils';
import type { FormInstance } from '../../form'; import type { FormInstance } from '../../form';
import Form from '../../form'; import Form from '../../form';
import Button from '../../button';
import Input from '../../input'; import Input from '../../input';
import InputNumber from '../../input-number';
import zhCN from '../../locale/zh_CN'; import zhCN from '../../locale/zh_CN';
describe('ConfigProvider.Form', () => { describe('ConfigProvider.Form', () => {
@ -82,6 +85,97 @@ describe('ConfigProvider.Form', () => {
expect(explains[0]).toHaveTextContent('必须'); expect(explains[0]).toHaveTextContent('必须');
expect(explains[explains.length - 1]).toHaveTextContent('年龄必须等于17'); expect(explains[explains.length - 1]).toHaveTextContent('年龄必须等于17');
}); });
// copied: https://github.com/ant-design/ant-design/blob/5ce9818401f976fcb665eff2a48e5f05d17acf39/components/config-provider/__tests__/form.test.tsx#L99-L150
it('nested description should use the default value of this warehouse first', async () => {
const validateMessages: ValidateMessages = {
number: {
// eslint-disable-next-line no-template-curly-in-string
max: '${label} 最大值为 ${max}',
/**
* Intentionally not filling `range` to test default message
* default: https://github.com/ant-design/ant-design/blob/12596a06f2ff88d8a27e72f6f9bac7c63a0b2ece/components/locale/en_US.ts#L123
*/
// range:
},
};
const formRef = React.createRef<FormInstance>();
const { container } = render(
<ConfigProvider form={{ validateMessages }}>
<Form ref={formRef} initialValues={{ age: 1, rate: 6 }}>
<Form.Item name="rate" rules={[{ type: 'number', max: 5 }]}>
<InputNumber />
</Form.Item>
<Form.Item name="age" rules={[{ type: 'number', max: 99, min: 18 }]}>
<InputNumber />
</Form.Item>
</Form>
</ConfigProvider>,
);
await act(async () => {
try {
await formRef.current?.validateFields();
} catch (e) {
// Do nothing
}
});
await act(async () => {
jest.runAllTimers();
await Promise.resolve();
});
act(() => {
jest.runAllTimers();
});
expect(container.querySelectorAll('.ant-form-item-explain')).toHaveLength(2);
expect(container.querySelectorAll('.ant-form-item-explain')[0]).toHaveTextContent(
'rate 最大值为 5',
);
expect(container.querySelectorAll('.ant-form-item-explain')[1]).toHaveTextContent(
'age must be between 18-99',
);
});
// https://github.com/ant-design/ant-design/issues/43210
it('should merge parent ConfigProvider validateMessages', async () => {
const MyForm = () => (
<Form>
<Form.Item name="name" label="Name" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form>
);
const { container, getAllByRole, getAllByText } = render(
<ConfigProvider>
<MyForm />
<ConfigProvider form={{ validateMessages: { required: 'Required' } }}>
<MyForm />
<ConfigProvider>
<MyForm />
</ConfigProvider>
</ConfigProvider>
</ConfigProvider>,
);
const submitButtons = getAllByRole('button');
expect(submitButtons).toHaveLength(3);
submitButtons.forEach((b) => fireEvent.click(b));
await waitFakeTimer();
expect(container.querySelectorAll('.ant-form-item-explain-error')).toHaveLength(3);
expect(getAllByText('Please enter Name')).toHaveLength(1);
expect(getAllByText('Required')).toHaveLength(2);
});
}); });
describe('form requiredMark', () => { describe('form requiredMark', () => {

View File

@ -1,4 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import type { ValidateMessages } from 'rc-field-form/lib/interface';
import type { RequiredMark } from '../form/Form'; import type { RequiredMark } from '../form/Form';
import type { Locale } from '../locale-provider'; import type { Locale } from '../locale-provider';
import type { RenderEmptyHandler } from './defaultRenderEmpty'; import type { RenderEmptyHandler } from './defaultRenderEmpty';
@ -45,6 +46,7 @@ export interface ConfigConsumerProps {
virtual?: boolean; virtual?: boolean;
dropdownMatchSelectWidth?: boolean; dropdownMatchSelectWidth?: boolean;
form?: { form?: {
validateMessages?: ValidateMessages;
requiredMark?: RequiredMark; requiredMark?: RequiredMark;
colon?: boolean; colon?: boolean;
}; };

View File

@ -2,6 +2,7 @@ import IconContext from '@ant-design/icons/lib/components/Context';
import type { ValidateMessages } from 'rc-field-form/lib/interface'; import type { ValidateMessages } from 'rc-field-form/lib/interface';
import useMemo from 'rc-util/lib/hooks/useMemo'; import useMemo from 'rc-util/lib/hooks/useMemo';
import * as React from 'react'; import * as React from 'react';
import { merge } from 'rc-util/lib/utils/set';
import type { RequiredMark } from '../form/Form'; import type { RequiredMark } from '../form/Form';
import ValidateMessagesContext from '../form/validateMessagesContext'; import ValidateMessagesContext from '../form/validateMessagesContext';
import type { Locale } from '../locale-provider'; import type { Locale } from '../locale-provider';
@ -224,16 +225,17 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = props => {
); );
let childNode = children; let childNode = children;
// Additional Form provider
let validateMessages: ValidateMessages = {};
if (locale) { const validateMessages = React.useMemo(
validateMessages = () =>
locale.Form?.defaultValidateMessages || defaultLocale.Form?.defaultValidateMessages || {}; merge(
} defaultLocale.Form?.defaultValidateMessages || {},
if (form && form.validateMessages) { memoedConfig.locale?.Form?.defaultValidateMessages || {},
validateMessages = { ...validateMessages, ...form.validateMessages }; memoedConfig.form?.validateMessages || {},
} form?.validateMessages || {},
),
[memoedConfig, form?.validateMessages],
);
if (Object.keys(validateMessages).length > 0) { if (Object.keys(validateMessages).length > 0) {
childNode = ( childNode = (