mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
feat: CP support Input.TextArea allowClear、autoComplete、className、classNames、style、styles (#47589)
* feat: CP support Input allowClear * feat: CP support Input allowClear * fix: fix * fix: fix * revert * revert * Update Input.tsx Signed-off-by: lijianan <574980606@qq.com> * Update getAllowClear.tsx Signed-off-by: lijianan <574980606@qq.com> * Update getAllowClear.tsx Signed-off-by: lijianan <574980606@qq.com> * test: add test case --------- Signed-off-by: lijianan <574980606@qq.com>
This commit is contained in:
parent
6c0d3e46c4
commit
271daafa39
@ -13,7 +13,7 @@ const getAllowClear = (allowClear: AllowClear): AllowClear => {
|
||||
clearIcon: <CloseCircleFilled />,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return mergedAllowClear;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
@ -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"
|
||||
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();
|
||||
|
@ -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';
|
||||
@ -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'>;
|
||||
@ -138,6 +141,7 @@ export interface ConfigConsumerProps {
|
||||
csp?: CSPConfig;
|
||||
autoInsertSpaceInButton?: boolean;
|
||||
input?: InputConfig;
|
||||
textArea?: TextAreaConfig;
|
||||
pagination?: ComponentStyleConfig & Pick<PaginationProps, 'showSizeChanger'>;
|
||||
locale?: Locale;
|
||||
direction?: DirectionType;
|
||||
|
@ -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 |
|
||||
|
@ -39,6 +39,7 @@ import type {
|
||||
TableConfig,
|
||||
TabsConfig,
|
||||
TagConfig,
|
||||
TextAreaConfig,
|
||||
Theme,
|
||||
ThemeConfig,
|
||||
TourConfig,
|
||||
@ -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;
|
||||
@ -321,6 +323,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
|
||||
menu,
|
||||
pagination,
|
||||
input,
|
||||
textArea,
|
||||
empty,
|
||||
badge,
|
||||
radio,
|
||||
@ -405,6 +408,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
|
||||
steps,
|
||||
image,
|
||||
input,
|
||||
textArea,
|
||||
layout,
|
||||
list,
|
||||
mentions,
|
||||
|
@ -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 |
|
||||
|
@ -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 */
|
||||
@ -52,6 +51,8 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
|
||||
classNames: classes,
|
||||
rootClassName,
|
||||
className,
|
||||
style,
|
||||
styles,
|
||||
variant: customVariant,
|
||||
...rest
|
||||
} = props;
|
||||
@ -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);
|
||||
@ -91,28 +92,26 @@ 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',
|
||||
@ -120,6 +119,7 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
|
||||
},
|
||||
hashId,
|
||||
classes?.textarea,
|
||||
textArea?.classNames?.textarea,
|
||||
),
|
||||
variant: classNames(
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user