Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: CP support Input.TextArea allowClear、autoComplete、className、classNames、style、styles #47589

Merged
merged 11 commits into from
Feb 28, 2024
2 changes: 1 addition & 1 deletion components/_util/getAllowClear.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const getAllowClear = (allowClear: AllowClear): AllowClear => {
clearIcon: <CloseCircleFilled />,
};
}

return mergedAllowClear;
};

Expand Down
56 changes: 56 additions & 0 deletions components/config-provider/__tests__/style.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ describe('ConfigProvider support style and className props', () => {
allowClear: {
clearIcon: <span className="cp-test-icon">cp-test-icon</span>,
},
autoComplete: 'test-cp-autocomplete',
}}
>
<Input
Expand All @@ -522,6 +523,61 @@ describe('ConfigProvider support style and className props', () => {
expect(inputElement).toHaveClass('cp-classNames-input');
expect(inputElement).toHaveStyle({ color: 'blue' });
expect(inputElement?.getAttribute('autocomplete')).toBe('test-autocomplete');
expect(inputElement?.getAttribute('autocomplete')).not.toBe('test-cp-autocomplete');
expect(
container?.querySelector<HTMLSpanElement>('.ant-input-affix-wrapper .cp-test-icon'),
).toBeTruthy();
});

it('Should Input.TextArea autoComplete & className & style & classNames & styles & allowClear works', () => {
const { container } = render(
<ConfigProvider
textArea={{
className: 'cp-textArea',
style: { backgroundColor: 'yellow' },
classNames: {
textarea: 'cp-classNames-textArea',
count: 'cp-classNames-count',
},
styles: {
textarea: {
color: 'blue',
},
count: {
color: 'red',
},
},
allowClear: {
clearIcon: <span className="cp-test-icon">cp-test-icon</span>,
},
autoComplete: 'test-cp-autocomplete',
}}
>
<Input.TextArea
autoComplete="test-autocomplete"
li-jia-nan marked this conversation as resolved.
Show resolved Hide resolved
placeholder="Basic usage"
value="test"
prefix="¥"
count={{ show: true }}
/>
</ConfigProvider>,
);
const wrapperElement = container.querySelector<HTMLSpanElement>('.ant-input-affix-wrapper');
expect(wrapperElement).toHaveClass('cp-textArea');
expect(wrapperElement).toHaveStyle({ backgroundColor: 'yellow' });

const inputElement = container.querySelector<HTMLTextAreaElement>('.ant-input');
expect(inputElement).toHaveClass('cp-classNames-textArea');
expect(inputElement).toHaveStyle({ color: 'blue' });
expect(inputElement?.getAttribute('autocomplete')).toBe('test-autocomplete');
expect(inputElement?.getAttribute('autocomplete')).not.toBe('test-cp-autocomplete');

const countElement = container.querySelector<HTMLSpanElement>(
'.ant-input-affix-wrapper .ant-input-data-count',
);
expect(countElement).toHaveClass('cp-classNames-count');
expect(countElement).toHaveStyle({ color: 'red' });

expect(
container?.querySelector<HTMLSpanElement>('.ant-input-affix-wrapper .cp-test-icon'),
).toBeTruthy();
Expand Down
6 changes: 5 additions & 1 deletion components/config-provider/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { CollapseProps } from '../collapse';
import type { DrawerProps } from '../drawer';
import type { FlexProps } from '../flex/interface';
import type { FormProps } from '../form/Form';
import type { InputProps } from '../input';
import type { InputProps, TextAreaProps } from '../input';
import type { Locale } from '../locale';
import type { MenuProps } from '../menu';
import type { ModalProps } from '../modal';
Expand Down Expand Up @@ -103,6 +103,9 @@ export type BadgeConfig = ComponentStyleConfig & Pick<BadgeProps, 'classNames' |
export type InputConfig = ComponentStyleConfig &
Pick<InputProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear'>;

export type TextAreaConfig = ComponentStyleConfig &
Pick<TextAreaProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear'>;

export type ButtonConfig = ComponentStyleConfig & Pick<ButtonProps, 'classNames' | 'styles'>;

export type NotificationConfig = ComponentStyleConfig & Pick<ArgsProps, 'closeIcon'>;
Expand Down Expand Up @@ -138,6 +141,7 @@ export interface ConfigConsumerProps {
csp?: CSPConfig;
autoInsertSpaceInButton?: boolean;
input?: InputConfig;
textArea?: TextAreaConfig;
pagination?: ComponentStyleConfig & Pick<PaginationProps, 'showSizeChanger'>;
locale?: Locale;
direction?: DirectionType;
Expand Down
1 change: 1 addition & 0 deletions components/config-provider/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const {
| form | Set Form common props | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional`, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options) } | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0; className: 5.7.0; style: 5.7.0 |
| image | Set Image common props | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode } } | - | 5.7.0, closeIcon: 5.14.0 |
| input | Set Input common props | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 4.2.0, allowClear: 5.15.0 |
| textArea | Set TextArea common props | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
| layout | Set Layout common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| list | Set List common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| menu | Set Menu common props | { className?: string, style?: React.CSSProperties, expandIcon?: ReactNode \| props => ReactNode } | - | 5.7.0, expandIcon: 5.15.0 |
Expand Down
4 changes: 4 additions & 0 deletions components/config-provider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import type {
TableConfig,
TabsConfig,
TagConfig,
TextAreaConfig,
Theme,
ThemeConfig,
TourConfig,
Expand Down Expand Up @@ -124,6 +125,7 @@ export interface ConfigProviderProps {
form?: ComponentStyleConfig &
Pick<FormProps, 'requiredMark' | 'colon' | 'scrollToFirstError' | 'validateMessages'>;
input?: InputConfig;
textArea?: TextAreaConfig;
select?: ComponentStyleConfig & Pick<SelectProps, 'showSearch'>;
pagination?: ComponentStyleConfig & Pick<PaginationProps, 'showSizeChanger'>;
locale?: Locale;
Expand Down Expand Up @@ -321,6 +323,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
menu,
pagination,
input,
textArea,
empty,
badge,
radio,
Expand Down Expand Up @@ -405,6 +408,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
steps,
image,
input,
textArea,
layout,
list,
mentions,
Expand Down
1 change: 1 addition & 0 deletions components/config-provider/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ const {
| form | 设置 Form 组件的通用属性 | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)} | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0; className: 5.7.0; style: 5.7.0 |
| image | 设置 Image 组件的通用属性 | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode } } | - | 5.7.0, closeIcon: 5.14.0 |
| input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.7.0, allowClear: 5.15.0 |
| textArea | 设置 TextArea 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
| layout | 设置 Layout 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| list | 设置 List 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| menu | 设置 Menu 组件的通用属性 | { className?: string, style?: React.CSSProperties, expandIcon?: ReactNode \| props => ReactNode } | - | 5.7.0, expandIcon: 5.15.0 |
Expand Down
32 changes: 16 additions & 16 deletions components/input/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import * as React from 'react';
import { forwardRef } from 'react';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import classNames from 'classnames';
import type { BaseInputProps } from 'rc-input/lib/interface';
import type { TextAreaRef as RcTextAreaRef } from 'rc-textarea';
import RcTextArea from 'rc-textarea';
import type { TextAreaProps as RcTextAreaProps } from 'rc-textarea/lib/interface';

import getAllowClear from '../_util/getAllowClear';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import type { Variant } from '../form/hooks/useVariants';
import useVariant from '../form/hooks/useVariants';
import type { InputFocusOptions } from './Input';
import { triggerFocus } from './Input';
import useStyle from './style';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import type { Variant } from '../form/hooks/useVariants';
import useVariant from '../form/hooks/useVariants';
import { devUseWarning } from '../_util/warning';

export interface TextAreaProps extends Omit<RcTextAreaProps, 'suffix'> {
/** @deprecated Use `variant` instead */
Expand Down Expand Up @@ -52,6 +51,8 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
classNames: classes,
rootClassName,
className,
style,
styles,
variant: customVariant,
...rest
} = props;
Expand All @@ -61,7 +62,7 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
deprecated(!('bordered' in props), 'bordered', 'variant');
}

const { getPrefixCls, direction } = React.useContext(ConfigContext);
const { getPrefixCls, direction, textArea } = React.useContext(ConfigContext);

// ===================== Size =====================
const mergedSize = useSize(customizeSize);
Expand Down Expand Up @@ -91,35 +92,34 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {

const prefixCls = getPrefixCls('input', customizePrefixCls);

// Allow clear
let mergedAllowClear: BaseInputProps['allowClear'];
if (typeof allowClear === 'object' && allowClear?.clearIcon) {
mergedAllowClear = allowClear;
} else if (allowClear) {
mergedAllowClear = { clearIcon: <CloseCircleFilled /> };
}

// ===================== Style =====================
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);

const [variant, enableVariantCls] = useVariant(customVariant, bordered);

const mergedAllowClear = getAllowClear(allowClear ?? textArea?.allowClear);

return wrapCSSVar(
<RcTextArea
autoComplete={textArea?.autoComplete}
{...rest}
style={{ ...textArea?.style, ...style }}
styles={{ ...textArea?.styles, ...styles }}
disabled={mergedDisabled}
allowClear={mergedAllowClear}
className={classNames(cssVarCls, rootCls, className, rootClassName)}
className={classNames(cssVarCls, rootCls, className, rootClassName, textArea?.className)}
classNames={{
...classes,
...textArea?.classNames,
textarea: classNames(
{
[`${prefixCls}-sm`]: mergedSize === 'small',
[`${prefixCls}-lg`]: mergedSize === 'large',
},
hashId,
classes?.textarea,
textArea?.classNames?.textarea,
),
variant: classNames(
{
Expand Down