fix: form item support comment (#41771)

* fix: form item support comment

* chore: clean up

* fix: lint
This commit is contained in:
二货爱吃白萝卜 2023-04-12 16:15:04 +08:00 committed by GitHub
parent 0991bf3fe9
commit e2606d477d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 19 deletions

View File

@ -6,18 +6,19 @@ import type { Meta, NamePath } from 'rc-field-form/lib/interface';
import useState from 'rc-util/lib/hooks/useState';
import { supportRef } from 'rc-util/lib/ref';
import * as React from 'react';
import useFormItemStatus from '../hooks/useFormItemStatus';
import { ConfigContext } from '../../config-provider';
import { cloneElement, isValidElement } from '../../_util/reactNode';
import warning from '../../_util/warning';
import { FormContext, NoStyleItemContext } from '../context';
import { ConfigContext } from '../../config-provider';
import type { FormItemInputProps } from '../FormItemInput';
import type { FormItemLabelProps, LabelTooltipType } from '../FormItemLabel';
import { FormContext, NoStyleItemContext } from '../context';
import useFormItemStatus from '../hooks/useFormItemStatus';
import useFrameState from '../hooks/useFrameState';
import useItemRef from '../hooks/useItemRef';
import { getFieldId, toArray } from '../util';
import ItemHolder from './ItemHolder';
import useChildren from '../hooks/useChildren';
import useStyle from '../style';
const NAME_SPLIT = '__SPLIT__';
@ -110,7 +111,10 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
} = props;
const { getPrefixCls } = React.useContext(ConfigContext);
const { name: formName } = React.useContext(FormContext);
const isRenderProps = typeof children === 'function';
const mergedChildren = useChildren(children);
const isRenderProps = typeof mergedChildren === 'function';
const notifyParentMetaChange = React.useContext(NoStyleItemContext);
const { validateTrigger: contextValidateTrigger } = React.useContext(FieldContext);
@ -232,7 +236,7 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
}
if (!hasName && !isRenderProps && !dependencies) {
return wrapSSR(renderLayout(children) as JSX.Element);
return wrapSSR(renderLayout(mergedChildren) as JSX.Element);
}
let variables: Record<string, string> = {};
@ -287,13 +291,13 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
'Form.Item',
"`shouldUpdate` and `dependencies` shouldn't be used together. See https://u.ant.design/form-deps.",
);
if (Array.isArray(children) && hasName) {
if (Array.isArray(mergedChildren) && hasName) {
warning(
false,
'Form.Item',
'A `Form.Item` with a `name` prop must have a single child element. For information on how to render more complex form items, see https://u.ant.design/complex-form-item.',
);
childNode = children;
childNode = mergedChildren;
} else if (isRenderProps && (!(shouldUpdate || dependencies) || hasName)) {
warning(
!!(shouldUpdate || dependencies),
@ -311,14 +315,14 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
'Form.Item',
'Must set `name` or use a render function when `dependencies` is set.',
);
} else if (isValidElement(children)) {
} else if (isValidElement(mergedChildren)) {
warning(
children.props.defaultValue === undefined,
mergedChildren.props.defaultValue === undefined,
'Form.Item',
'`defaultValue` will not work on controlled Field. You should use `initialValues` of Form instead.',
);
const childProps = { ...children.props, ...mergedControl };
const childProps = { ...mergedChildren.props, ...mergedControl };
if (!childProps.id) {
childProps.id = fieldId;
}
@ -342,8 +346,8 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
childProps['aria-required'] = 'true';
}
if (supportRef(children)) {
childProps.ref = getItemRef(mergedName, children);
if (supportRef(mergedChildren)) {
childProps.ref = getItemRef(mergedName, mergedChildren);
}
// We should keep user origin event handler
@ -355,7 +359,7 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
triggers.forEach((eventName) => {
childProps[eventName] = (...args: any[]) => {
mergedControl[eventName]?.(...args);
children.props[eventName]?.(...args);
mergedChildren.props[eventName]?.(...args);
};
});
@ -369,21 +373,21 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
childNode = (
<MemoInput
value={mergedControl[props.valuePropName || 'value']}
update={children}
update={mergedChildren}
childProps={watchingChildProps}
>
{cloneElement(children, childProps)}
{cloneElement(mergedChildren, childProps)}
</MemoInput>
);
} else if (isRenderProps && (shouldUpdate || dependencies) && !hasName) {
childNode = children(context);
childNode = mergedChildren(context);
} else {
warning(
!mergedName.length,
'Form.Item',
'`name` is only used for validate React element. If you are using Form.Item as layout display, please remove `name` instead.',
);
childNode = children as React.ReactNode;
childNode = mergedChildren as React.ReactNode;
}
return renderLayout(childNode, fieldId, isRequired);

View File

@ -8,6 +8,7 @@ import Form from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, pureRender, render, screen, waitFakeTimer } from '../../../tests/utils';
import { resetWarned } from '../../_util/warning';
import Button from '../../button';
import Cascader from '../../cascader';
import Checkbox from '../../checkbox';
@ -1459,7 +1460,7 @@ describe('Form', () => {
render(
<Modal open>
<Form>
<Form.Item help='This is a help message'>
<Form.Item help="This is a help message">
<Input />
</Form.Item>
</Form>
@ -1547,7 +1548,7 @@ describe('Form', () => {
const [form] = Form.useForm();
return (
<Form form={form} name='test-form'>
<Form form={form} name="test-form">
<Form.Item name="error" rules={[{ required: true, message: 'This is a error message.' }]}>
<ErrorItem />
</Form.Item>
@ -1859,4 +1860,22 @@ describe('Form', () => {
expect(container.querySelectorAll('.ant-form-item-required')).toHaveLength(2);
expect(container.querySelectorAll('.ant-form-item-required-mark-optional')).toHaveLength(2);
});
it('children support comment', () => {
resetWarned();
const { container } = render(
<Form initialValues={{ name: 'bamboo', age: '14' }}>
<Form.Item name="name">
{/* Comment here */}
<Input />
</Form.Item>
<Form.Item name="age">{[null, <Input key="input" />]}</Form.Item>
</Form>,
);
expect(container.querySelectorAll('input')[0].value).toEqual('bamboo');
expect(container.querySelectorAll('input')[1].value).toEqual('14');
expect(errorSpy).not.toHaveBeenCalled();
});
});

View File

@ -0,0 +1,13 @@
import toArray from 'rc-util/lib/Children/toArray';
import type { FormItemProps } from '../FormItem';
export default function useChildren(
children?: FormItemProps['children'],
): FormItemProps['children'] {
if (typeof children === 'function') {
return children;
}
const childList = toArray(children);
return childList.length <= 1 ? childList[0] : childList;
}