mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-07 17:44:35 +08:00
feat: input support semantic dom (#53958)
* update * update * feat: input support completely semantic dom * add test * fix test * update * chore: add jest types to tsconfig * update snapshot * update * update * update * update * update * update * update * update * update * update --------- Co-authored-by: thinkasany <480968828@qq.com>
This commit is contained in:
parent
6b2e46fe98
commit
bff829f763
@ -513,6 +513,7 @@ describe('Button', () => {
|
||||
fireEvent.click(getByRole('link'));
|
||||
expect(handleClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should support classnames and styles', () => {
|
||||
const cusomStyles = {
|
||||
root: { color: 'red' },
|
||||
@ -524,20 +525,27 @@ describe('Button', () => {
|
||||
icon: 'custom-icon',
|
||||
content: 'custom-content',
|
||||
};
|
||||
const { container } = render(
|
||||
const { container, rerender, getByText } = render(
|
||||
<Button classNames={customClassNames} styles={cusomStyles} icon={<SearchOutlined />}>
|
||||
antd
|
||||
</Button>,
|
||||
);
|
||||
const root = container.querySelector('.ant-btn') as HTMLElement;
|
||||
const icon = container.querySelector('.ant-btn-icon') as HTMLElement;
|
||||
const root = container.querySelector('.ant-btn');
|
||||
const icon = container.querySelector('.ant-btn-icon');
|
||||
const content = getByText('antd');
|
||||
expect(root).toHaveClass(customClassNames.root);
|
||||
expect(icon).toHaveClass(customClassNames.icon);
|
||||
expect(root).toHaveStyle(cusomStyles.root);
|
||||
expect(icon).toHaveStyle(cusomStyles.icon);
|
||||
expect(container.querySelector(`.${customClassNames.content}`)).toHaveStyle(
|
||||
cusomStyles.content,
|
||||
expect(content).toHaveStyle(cusomStyles.content);
|
||||
rerender(
|
||||
<Button classNames={customClassNames} styles={cusomStyles} loading>
|
||||
antd
|
||||
</Button>,
|
||||
);
|
||||
const loadingIcon = container.querySelector('.ant-btn-icon');
|
||||
expect(loadingIcon).toHaveClass(customClassNames.icon);
|
||||
expect(loadingIcon).toHaveStyle(cusomStyles.icon);
|
||||
});
|
||||
|
||||
it('should support customizing the background color of default type button in disabled state', () => {
|
||||
|
@ -27,7 +27,7 @@ import Compact from './style/compact';
|
||||
|
||||
export type LegacyButtonType = ButtonType | 'danger';
|
||||
|
||||
type SemanticName = 'root' | 'icon' | 'content';
|
||||
export type ButtonSemanticName = 'root' | 'icon' | 'content';
|
||||
export interface BaseButtonProps {
|
||||
type?: ButtonType;
|
||||
color?: ButtonColorType;
|
||||
@ -46,8 +46,8 @@ export interface BaseButtonProps {
|
||||
block?: boolean;
|
||||
children?: React.ReactNode;
|
||||
[key: `data-${string}`]: string;
|
||||
classNames?: Partial<Record<SemanticName, string>>;
|
||||
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
|
||||
classNames?: Partial<Record<ButtonSemanticName, string>>;
|
||||
styles?: Partial<Record<ButtonSemanticName, React.CSSProperties>>;
|
||||
}
|
||||
|
||||
type MergedHTMLAttributes = Omit<
|
||||
@ -351,6 +351,8 @@ const InternalCompoundedButton = React.forwardRef<
|
||||
</IconWrapper>
|
||||
) : (
|
||||
<DefaultLoadingIcon
|
||||
className={iconClasses}
|
||||
style={iconStyle}
|
||||
existIcon={!!icon}
|
||||
prefixCls={prefixCls}
|
||||
loading={innerLoading}
|
||||
|
@ -18,6 +18,7 @@ import type { CollapseProps } from '../collapse';
|
||||
import type { ColorPickerProps } from '../color-picker';
|
||||
import type { DatePickerProps, RangePickerProps } from '../date-picker';
|
||||
import type { DescriptionsProps } from '../descriptions';
|
||||
import type { DividerProps } from '../divider';
|
||||
import type { DrawerProps } from '../drawer';
|
||||
import type { DropdownProps } from '../dropdown';
|
||||
import type { EmptyProps } from '../empty';
|
||||
@ -25,8 +26,9 @@ import type { FlexProps } from '../flex/interface';
|
||||
import type { FloatButtonGroupProps } from '../float-button/interface';
|
||||
import type { FormProps } from '../form/Form';
|
||||
import type { ImageProps } from '../image';
|
||||
import type { InputProps, TextAreaProps } from '../input';
|
||||
import type { InputProps, SearchProps, TextAreaProps } from '../input';
|
||||
import type { InputNumberProps } from '../input-number';
|
||||
import type { OTPProps } from '../input/OTP';
|
||||
import type { ListItemProps } from '../list';
|
||||
import type { Locale } from '../locale';
|
||||
import type { MasonryProps } from '../masonry';
|
||||
@ -65,7 +67,6 @@ import type { TreeProps } from '../tree';
|
||||
import type { TreeSelectProps } from '../tree-select';
|
||||
import type { UploadProps } from '../upload';
|
||||
import type { RenderEmptyHandler } from './defaultRenderEmpty';
|
||||
import type { DividerProps } from '../divider';
|
||||
|
||||
export const defaultPrefixCls = 'ant';
|
||||
export const defaultIconPrefixCls = 'anticon';
|
||||
@ -216,9 +217,13 @@ export type BreadcrumbConfig = ComponentStyleConfig &
|
||||
export type InputConfig = ComponentStyleConfig &
|
||||
Pick<InputProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear' | 'variant'>;
|
||||
|
||||
export type InputSearchConfig = ComponentStyleConfig & Pick<SearchProps, 'classNames' | 'styles'>;
|
||||
|
||||
export type TextAreaConfig = ComponentStyleConfig &
|
||||
Pick<TextAreaProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear' | 'variant'>;
|
||||
|
||||
export type OTPConfig = ComponentStyleConfig & Pick<OTPProps, 'classNames' | 'styles'>;
|
||||
|
||||
export type ButtonConfig = ComponentStyleConfig &
|
||||
Pick<ButtonProps, 'classNames' | 'styles' | 'autoInsertSpace' | 'variant' | 'color'>;
|
||||
|
||||
@ -367,7 +372,9 @@ export interface WaveConfig {
|
||||
|
||||
export interface ConfigComponentProps {
|
||||
input?: InputConfig;
|
||||
inputSearch?: InputSearchConfig;
|
||||
textArea?: TextAreaConfig;
|
||||
otp?: OTPConfig;
|
||||
inputNumber?: InputNumberConfig;
|
||||
pagination?: PaginationConfig;
|
||||
space?: SpaceConfig;
|
||||
|
@ -136,7 +136,9 @@ const {
|
||||
| image | Set Image common props | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode, classNames?:[ImageConfig\["classNames"\]](/components/image#semantic-dom), styles?: [ImageConfig\["styles"\]](/components/image#semantic-dom) } } | - | 5.7.0, `closeIcon`: 5.14.0, `classNames` and `styles`: 6.0.0 |
|
||||
| input | Set Input common props | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 4.2.0, `allowClear`: 5.15.0 |
|
||||
| inputNumber | Set InputNumber common props | { className?: string, style?: React.CSSProperties, classNames?: [InputNumberConfig\["classNames"\]](/components/input-number#semantic-dom), styles?: [InputNumberConfig\["styles"\]](/components/input-number#semantic-dom) } | - | |
|
||||
| textArea | Set TextArea common props | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
|
||||
| otp | Set OTP common props | { className?: string, style?: React.CSSProperties, classNames?: [OTPConfig\["classNames"\]](/components/input#semantic-otp), styles?: [OTPConfig\["styles"\]](/components/input#semantic-otp) } | - | |
|
||||
| inputSearch | Set Search common props | { className?: string, style?: React.CSSProperties, classNames?: [InputSearchConfig\["classNames"\]](/components/input#semantic-search), styles?: [InputSearchConfig\["styles"\]](/components/input#semantic-search) } | - | |
|
||||
| textArea | Set TextArea common props | { autoComplete?: string, className?: string, style?: React.CSSProperties,classNames?:[TextAreaConfig\["classNames"\]](/components/input#semantic-textarea), styles?: [TextAreaConfig\["styles"\]](/components/input#semantic-textarea), 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, item?:{ classNames: [ListItemProps\["classNames"\]](/components/list#listitem), styles: [ListItemProps\["styles"\]](/components/list#listitem) } } | - | 5.7.0 |
|
||||
| masonry | Set Masonry common props | { className?: string, style?: React.CSSProperties, classNames?: [MasonryProps\["classNames"\]](/components/masonry#semantic-dom), styles?: [MasonryProps\["styles"\]](/components/masonry#semantic-dom) } | - | |
|
||||
|
@ -35,7 +35,9 @@ import type {
|
||||
FormConfig,
|
||||
ImageConfig,
|
||||
InputConfig,
|
||||
InputSearchConfig,
|
||||
InputNumberConfig,
|
||||
OTPConfig,
|
||||
ListConfig,
|
||||
MasonryConfig,
|
||||
MentionsConfig,
|
||||
@ -158,6 +160,8 @@ export interface ConfigProviderProps {
|
||||
variant?: Variant;
|
||||
form?: FormConfig;
|
||||
input?: InputConfig;
|
||||
inputSearch?: InputSearchConfig;
|
||||
otp?: OTPConfig;
|
||||
inputNumber?: InputNumberConfig;
|
||||
textArea?: TextAreaConfig;
|
||||
select?: SelectConfig;
|
||||
@ -372,6 +376,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
|
||||
pagination,
|
||||
input,
|
||||
textArea,
|
||||
otp,
|
||||
empty,
|
||||
badge,
|
||||
radio,
|
||||
@ -470,6 +475,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
|
||||
image,
|
||||
input,
|
||||
textArea,
|
||||
otp,
|
||||
layout,
|
||||
list,
|
||||
mentions,
|
||||
|
@ -136,9 +136,11 @@ const {
|
||||
| floatButtonGroup | 设置 FloatButton.Group 组件的通用属性 | { closeIcon?: React.ReactNode } | - | 5.16.0 |
|
||||
| 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), classNames?:[FormConfig\["classNames"\]](/components/form-cn#semantic-dom), styles?: [FormConfig\["styles"\]](/components/form-cn#semantic-dom) } | - | `requiredMark`: 4.8.0; `colon`: 4.18.0; `scrollToFirstError`: 5.2.0; `className` 和 `style`: 5.7.0 |
|
||||
| image | 设置 Image 组件的通用属性 | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode, classNames?:[ImageConfig\["classNames"\]](/components/image-cn#semantic-dom), styles?: [ImageConfig\["styles"\]](/components/image-cn#semantic-dom) } } | - | 5.7.0, `closeIcon`: 5.14.0, `classNames` 和 `styles`: 6.0.0 |
|
||||
| input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.7.0, `allowClear`: 5.15.0 |
|
||||
| input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties,classNames?:[InputConfig\["classNames"\]](/components/input-cn#semantic-input), styles?: [InputConfig\["styles"\]](/components/input-cn#semantic-input), allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.7.0, `allowClear`: 5.15.0 |
|
||||
| inputNumber | 设置 Input 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [InputNumberConfig\["classNames"\]](/components/input-number-cn#semantic-dom), styles?: [InputNumberConfig\["styles"\]](/components/input-number-cn#semantic-dom) } | - | |
|
||||
| textArea | 设置 TextArea 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
|
||||
| otp | 设置 OTP 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [OTPConfig\["classNames"\]](/components/input-cn#semantic-otp), styles?: [OTPConfig\["styles"\]](/components/input-cn#semantic-otp) } | - | |
|
||||
| inputSearch | 设置 Search 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [InputSearchConfig\["classNames"\]](/components/input-cn#semantic-search), styles?: [InputSearchConfig\["styles"\]](/components/input-cn#semantic-search) } | - | |
|
||||
| textArea | 设置 TextArea 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties,classNames?:[TextAreaConfig\["classNames"\]](/components/input-cn#semantic-textarea), styles?: [TextAreaConfig\["styles"\]](/components/input-cn#semantic-textarea), allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
|
||||
| layout | 设置 Layout 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| list | 设置 List 组件的通用属性 | { className?: string, style?: React.CSSProperties, item?:{ classNames: [ListItemProps\["classNames"\]](/components/list-cn#listitem), styles: [ListItemProps\["styles"\]](/components/list-cn#listitem) } } | - | 5.7.0 |
|
||||
| masonry | 设置 Masonry 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [MasonryProps\["classNames"\]](/components/masonry#semantic-dom), styles?: [MasonryProps\["styles"\]](/components/masonry#semantic-dom) } | - | |
|
||||
|
@ -3,7 +3,7 @@ import type { InputRef, InputProps as RcInputProps } from '@rc-component/input';
|
||||
import RcInput from '@rc-component/input';
|
||||
import { InputFocusOptions, triggerFocus } from '@rc-component/input/lib/utils/commonUtils';
|
||||
import { composeRef } from '@rc-component/util/lib/ref';
|
||||
import classNames from 'classnames';
|
||||
import cls from 'classnames';
|
||||
|
||||
import ContextIsolator from '../_util/ContextIsolator';
|
||||
import getAllowClear from '../_util/getAllowClear';
|
||||
@ -22,11 +22,14 @@ import { useCompactItemContext } from '../space/Compact';
|
||||
import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
|
||||
import useStyle, { useSharedStyle } from './style';
|
||||
import { hasPrefixSuffix } from './utils';
|
||||
import useMergeSemantic from '../_util/hooks/useMergeSemantic';
|
||||
|
||||
export type { InputFocusOptions };
|
||||
export type { InputRef };
|
||||
export { triggerFocus };
|
||||
|
||||
type SemanticName = 'root' | 'prefix' | 'suffix' | 'input' | 'count';
|
||||
|
||||
export interface InputProps
|
||||
extends Omit<
|
||||
RcInputProps,
|
||||
@ -43,6 +46,8 @@ export interface InputProps
|
||||
* @default "outlined"
|
||||
*/
|
||||
variant?: Variant;
|
||||
classNames?: Partial<Record<SemanticName, string>>;
|
||||
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
|
||||
[key: `data-${string}`]: string | undefined;
|
||||
}
|
||||
|
||||
@ -64,7 +69,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
|
||||
styles,
|
||||
rootClassName,
|
||||
onChange,
|
||||
classNames: classes,
|
||||
classNames,
|
||||
variant: customVariant,
|
||||
...rest
|
||||
} = props;
|
||||
@ -103,6 +108,11 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
|
||||
const disabled = React.useContext(DisabledContext);
|
||||
const mergedDisabled = customDisabled ?? disabled;
|
||||
|
||||
const [mergedClassNames, mergedStyles] = useMergeSemantic(
|
||||
[contextClassNames, classNames],
|
||||
[contextStyles, styles],
|
||||
);
|
||||
|
||||
// ===================== Status =====================
|
||||
const { status: contextStatus, hasFeedback, feedbackIcon } = useContext(FormItemInputContext);
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
@ -166,17 +176,18 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
|
||||
disabled={mergedDisabled}
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
style={{ ...contextStyle, ...style }}
|
||||
styles={{ ...contextStyles, ...styles }}
|
||||
style={{ ...mergedStyles.root, ...contextStyle, ...style }}
|
||||
styles={mergedStyles}
|
||||
suffix={suffixNode}
|
||||
allowClear={mergedAllowClear}
|
||||
className={classNames(
|
||||
className={cls(
|
||||
className,
|
||||
rootClassName,
|
||||
cssVarCls,
|
||||
rootCls,
|
||||
compactItemClassnames,
|
||||
contextClassName,
|
||||
mergedClassNames.root,
|
||||
)}
|
||||
onChange={handleChange}
|
||||
addonBefore={
|
||||
@ -194,25 +205,23 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
|
||||
)
|
||||
}
|
||||
classNames={{
|
||||
...classes,
|
||||
...contextClassNames,
|
||||
input: classNames(
|
||||
...mergedClassNames,
|
||||
input: cls(
|
||||
{
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
classes?.input,
|
||||
contextClassNames.input,
|
||||
mergedClassNames.input,
|
||||
hashId,
|
||||
),
|
||||
variant: classNames(
|
||||
variant: cls(
|
||||
{
|
||||
[`${prefixCls}-${variant}`]: enableVariantCls,
|
||||
},
|
||||
getStatusClassNames(prefixCls, mergedStatus),
|
||||
),
|
||||
affixWrapper: classNames(
|
||||
affixWrapper: cls(
|
||||
{
|
||||
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
|
||||
@ -220,13 +229,13 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
|
||||
},
|
||||
hashId,
|
||||
),
|
||||
wrapper: classNames(
|
||||
wrapper: cls(
|
||||
{
|
||||
[`${prefixCls}-group-rtl`]: direction === 'rtl',
|
||||
},
|
||||
hashId,
|
||||
),
|
||||
groupWrapper: classNames(
|
||||
groupWrapper: cls(
|
||||
{
|
||||
[`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large',
|
||||
|
@ -1,13 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import useEvent from '@rc-component/util/lib/hooks/useEvent';
|
||||
import pickAttrs from '@rc-component/util/lib/pickAttrs';
|
||||
import classNames from 'classnames';
|
||||
import cls from 'classnames';
|
||||
|
||||
import useMergeSemantic from '../../_util/hooks/useMergeSemantic';
|
||||
import { getMergedStatus } from '../../_util/statusUtils';
|
||||
import type { InputStatus } from '../../_util/statusUtils';
|
||||
import { devUseWarning } from '../../_util/warning';
|
||||
import { ConfigContext } from '../../config-provider';
|
||||
import type { Variant } from '../../config-provider';
|
||||
import { useComponentConfig } from '../../config-provider/context';
|
||||
import useSize from '../../config-provider/hooks/useSize';
|
||||
import type { SizeType } from '../../config-provider/SizeContext';
|
||||
import { FormItemInputContext } from '../../form/context';
|
||||
@ -17,6 +18,8 @@ import useStyle from '../style/otp';
|
||||
import OTPInput from './OTPInput';
|
||||
import type { OTPInputProps } from './OTPInput';
|
||||
|
||||
type SemanticName = 'root' | 'input' | 'separator';
|
||||
|
||||
export interface OTPRef {
|
||||
focus: VoidFunction;
|
||||
blur: VoidFunction;
|
||||
@ -51,6 +54,9 @@ export interface OTPProps
|
||||
type?: React.HTMLInputTypeAttribute;
|
||||
|
||||
onInput?: (value: string[]) => void;
|
||||
|
||||
classNames?: Partial<Record<SemanticName, string>>;
|
||||
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
|
||||
}
|
||||
|
||||
function strToArr(str: string) {
|
||||
@ -61,15 +67,21 @@ interface SeparatorProps {
|
||||
index: number;
|
||||
prefixCls: string;
|
||||
separator: OTPProps['separator'];
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const Separator: React.FC<Readonly<SeparatorProps>> = (props) => {
|
||||
const { index, prefixCls, separator } = props;
|
||||
const { index, prefixCls, separator, className: semanticClassName, style: semanticStyle } = props;
|
||||
const separatorNode = typeof separator === 'function' ? separator(index) : separator;
|
||||
if (!separatorNode) {
|
||||
return null;
|
||||
}
|
||||
return <span className={`${prefixCls}-separator`}>{separatorNode}</span>;
|
||||
return (
|
||||
<span className={cls(`${prefixCls}-separator`, semanticClassName)} style={semanticStyle}>
|
||||
{separatorNode}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
|
||||
@ -90,6 +102,10 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
|
||||
type,
|
||||
onInput,
|
||||
inputMode,
|
||||
classNames,
|
||||
styles,
|
||||
className,
|
||||
style,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
@ -102,9 +118,21 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
|
||||
);
|
||||
}
|
||||
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const {
|
||||
classNames: contextClassNames,
|
||||
styles: contextStyles,
|
||||
getPrefixCls,
|
||||
direction,
|
||||
style: contextStyle,
|
||||
className: contextClassName,
|
||||
} = useComponentConfig('otp');
|
||||
const prefixCls = getPrefixCls('otp', customizePrefixCls);
|
||||
|
||||
const [mergedClassNames, mergedStyles] = useMergeSemantic(
|
||||
[contextClassNames, classNames],
|
||||
[contextStyles, styles],
|
||||
);
|
||||
|
||||
const domAttrs = pickAttrs(restProps, {
|
||||
aria: true,
|
||||
data: true,
|
||||
@ -248,7 +276,8 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
|
||||
<div
|
||||
{...domAttrs}
|
||||
ref={containerRef}
|
||||
className={classNames(
|
||||
className={cls(
|
||||
className,
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
@ -257,7 +286,10 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
|
||||
},
|
||||
cssVarCls,
|
||||
hashId,
|
||||
contextClassName,
|
||||
mergedClassNames.root,
|
||||
)}
|
||||
style={{ ...mergedStyles.root, ...contextStyle, ...style }}
|
||||
role="group"
|
||||
>
|
||||
<FormItemInputContext.Provider value={proxyFormContext}>
|
||||
@ -273,7 +305,8 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
|
||||
index={index}
|
||||
size={mergedSize}
|
||||
htmlSize={1}
|
||||
className={`${prefixCls}-input`}
|
||||
className={cls(mergedClassNames.input, `${prefixCls}-input`)}
|
||||
style={mergedStyles.input}
|
||||
onChange={onInputChange}
|
||||
value={singleValue}
|
||||
onActiveChange={onInputActiveChange}
|
||||
@ -281,7 +314,13 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
|
||||
{...inputSharedProps}
|
||||
/>
|
||||
{index < length - 1 && (
|
||||
<Separator separator={separator} index={index} prefixCls={prefixCls} />
|
||||
<Separator
|
||||
separator={separator}
|
||||
index={index}
|
||||
prefixCls={prefixCls}
|
||||
className={cls(mergedClassNames.separator)}
|
||||
style={mergedStyles.separator}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
|
@ -1,16 +1,20 @@
|
||||
import * as React from 'react';
|
||||
import SearchOutlined from '@ant-design/icons/SearchOutlined';
|
||||
import { composeRef } from '@rc-component/util/lib/ref';
|
||||
import classNames from 'classnames';
|
||||
import cls from 'classnames';
|
||||
|
||||
import useMergeSemantic from '../_util/hooks/useMergeSemantic';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import Button from '../button';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { ButtonSemanticName } from '../button/button';
|
||||
import { useComponentConfig } from '../config-provider/context';
|
||||
import useSize from '../config-provider/hooks/useSize';
|
||||
import { useCompactItemContext } from '../space/Compact';
|
||||
import type { InputProps, InputRef } from './Input';
|
||||
import Input from './Input';
|
||||
|
||||
type SemanticName = 'root' | 'input' | 'prefix' | 'suffix' | 'count';
|
||||
|
||||
export interface SearchProps extends InputProps {
|
||||
inputPrefixCls?: string;
|
||||
onSearch?: (
|
||||
@ -26,6 +30,12 @@ export interface SearchProps extends InputProps {
|
||||
enterButton?: React.ReactNode;
|
||||
loading?: boolean;
|
||||
onPressEnter?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||
classNames?: Partial<Record<SemanticName, string>> & {
|
||||
button?: Partial<Record<ButtonSemanticName, string>>;
|
||||
};
|
||||
styles?: Partial<Record<SemanticName, React.CSSProperties>> & {
|
||||
button?: Partial<Record<ButtonSemanticName, React.CSSProperties>>;
|
||||
};
|
||||
}
|
||||
|
||||
const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
||||
@ -45,10 +55,27 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
||||
onCompositionEnd,
|
||||
variant,
|
||||
onPressEnter: customOnPressEnter,
|
||||
classNames,
|
||||
styles,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const {
|
||||
direction,
|
||||
getPrefixCls,
|
||||
classNames: contextClassNames,
|
||||
styles: contextStyles,
|
||||
} = useComponentConfig('inputSearch');
|
||||
|
||||
const [mergedClassNames, mergedStyles] = useMergeSemantic(
|
||||
[contextClassNames, classNames],
|
||||
[contextStyles, styles],
|
||||
{
|
||||
button: {
|
||||
_default: 'root',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const composedRef = React.useRef<boolean>(false);
|
||||
|
||||
@ -92,7 +119,7 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
||||
};
|
||||
|
||||
const searchIcon = typeof enterButton === 'boolean' ? <SearchOutlined /> : null;
|
||||
const btnClassName = `${prefixCls}-button`;
|
||||
const btnClassName = cls(`${prefixCls}-button`, mergedClassNames.button?.root);
|
||||
|
||||
let button: React.ReactNode;
|
||||
const enterButtonAsElement = (enterButton || {}) as React.ReactElement;
|
||||
@ -120,6 +147,8 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
||||
} else {
|
||||
button = (
|
||||
<Button
|
||||
classNames={mergedClassNames.button}
|
||||
styles={mergedStyles.button}
|
||||
className={btnClassName}
|
||||
color={enterButton ? 'primary' : 'default'}
|
||||
size={size}
|
||||
@ -151,7 +180,7 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
||||
];
|
||||
}
|
||||
|
||||
const cls = classNames(
|
||||
const mergedClassName = cls(
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
@ -159,6 +188,7 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
||||
[`${prefixCls}-with-button`]: !!enterButton,
|
||||
},
|
||||
className,
|
||||
mergedClassNames.root,
|
||||
);
|
||||
|
||||
const handleOnCompositionStart: React.CompositionEventHandler<HTMLInputElement> = (e) => {
|
||||
@ -173,7 +203,9 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
||||
|
||||
const inputProps: InputProps = {
|
||||
...restProps,
|
||||
className: cls,
|
||||
className: mergedClassName,
|
||||
classNames: mergedClassNames,
|
||||
styles: mergedStyles,
|
||||
prefixCls: inputPrefixCls,
|
||||
type: 'search',
|
||||
size,
|
||||
|
@ -5,7 +5,7 @@ import type {
|
||||
TextAreaRef as RcTextAreaRef,
|
||||
} from '@rc-component/textarea';
|
||||
import RcTextArea from '@rc-component/textarea';
|
||||
import classNames from 'classnames';
|
||||
import cls from 'classnames';
|
||||
|
||||
import getAllowClear from '../_util/getAllowClear';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
@ -24,6 +24,9 @@ import type { InputFocusOptions } from './Input';
|
||||
import { triggerFocus } from './Input';
|
||||
import { useSharedStyle } from './style';
|
||||
import useStyle from './style/textarea';
|
||||
import useMergeSemantic from '../_util/hooks/useMergeSemantic';
|
||||
|
||||
type SemanticName = 'root' | 'textarea' | 'count';
|
||||
|
||||
export interface TextAreaProps extends Omit<RcTextAreaProps, 'suffix'> {
|
||||
/** @deprecated Use `variant` instead */
|
||||
@ -36,6 +39,8 @@ export interface TextAreaProps extends Omit<RcTextAreaProps, 'suffix'> {
|
||||
* @default "outlined"
|
||||
*/
|
||||
variant?: Variant;
|
||||
classNames?: Partial<Record<SemanticName, string>>;
|
||||
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
|
||||
}
|
||||
|
||||
export interface TextAreaRef {
|
||||
@ -52,7 +57,7 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
|
||||
disabled: customDisabled,
|
||||
status: customStatus,
|
||||
allowClear,
|
||||
classNames: classes,
|
||||
classNames,
|
||||
rootClassName,
|
||||
className,
|
||||
style,
|
||||
@ -92,6 +97,11 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
|
||||
} = React.useContext(FormItemInputContext);
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
|
||||
const [mergedClassNames, mergedStyles] = useMergeSemantic(
|
||||
[contextClassNames, classNames],
|
||||
[contextStyles, styles],
|
||||
);
|
||||
|
||||
// ===================== Ref ======================
|
||||
const innerRef = React.useRef<RcTextAreaRef>(null);
|
||||
|
||||
@ -157,40 +167,39 @@ const TextArea = forwardRef<TextAreaRef, TextAreaProps>((props, ref) => {
|
||||
<RcTextArea
|
||||
autoComplete={contextAutoComplete}
|
||||
{...rest}
|
||||
style={{ ...contextStyle, ...style }}
|
||||
styles={{ ...contextStyles, ...styles }}
|
||||
style={{ ...mergedStyles.root, ...contextStyle, ...style }}
|
||||
styles={mergedStyles}
|
||||
disabled={mergedDisabled}
|
||||
allowClear={mergedAllowClear}
|
||||
className={classNames(
|
||||
className={cls(
|
||||
cssVarCls,
|
||||
rootCls,
|
||||
className,
|
||||
rootClassName,
|
||||
compactItemClassnames,
|
||||
contextClassName,
|
||||
mergedClassNames.root,
|
||||
// Only for wrapper
|
||||
resizeDirty && `${prefixCls}-textarea-affix-wrapper-resize-dirty`,
|
||||
)}
|
||||
classNames={{
|
||||
...classes,
|
||||
...contextClassNames,
|
||||
textarea: classNames(
|
||||
...mergedClassNames,
|
||||
textarea: cls(
|
||||
{
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
},
|
||||
hashId,
|
||||
classes?.textarea,
|
||||
contextClassNames.textarea,
|
||||
mergedClassNames.textarea,
|
||||
isMouseDown && `${prefixCls}-mouse-active`,
|
||||
),
|
||||
variant: classNames(
|
||||
variant: cls(
|
||||
{
|
||||
[`${prefixCls}-${variant}`]: enableVariantCls,
|
||||
},
|
||||
getStatusClassNames(prefixCls, mergedStatus),
|
||||
),
|
||||
affixWrapper: classNames(
|
||||
affixWrapper: cls(
|
||||
`${prefixCls}-textarea-affix-wrapper`,
|
||||
{
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
|
@ -1,5 +1,107 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Input allowClear semantic dom snapshot 1`] = `
|
||||
<div>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-outlined custom-class css-var-root ant-input-css-var"
|
||||
style="background-color: red;"
|
||||
>
|
||||
<span
|
||||
class="ant-input-prefix custom-prefix"
|
||||
style="color: blue;"
|
||||
>
|
||||
prefix
|
||||
</span>
|
||||
<input
|
||||
class="ant-input custom-input"
|
||||
style="color: red;"
|
||||
type="text"
|
||||
value="123"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-suffix custom-suffix"
|
||||
style="color: yellow;"
|
||||
>
|
||||
<span
|
||||
class="ant-input-show-count-suffix ant-input-show-count-has-suffix custom-count"
|
||||
style="color: green;"
|
||||
>
|
||||
3
|
||||
</span>
|
||||
suffix
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-group-wrapper ant-input-group-wrapper-outlined custom-class css-var-root ant-input-css-var"
|
||||
style="background-color: red;"
|
||||
>
|
||||
<span
|
||||
class="ant-input-wrapper ant-input-group"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-outlined"
|
||||
>
|
||||
<span
|
||||
class="ant-input-prefix custom-prefix"
|
||||
style="color: blue;"
|
||||
>
|
||||
prefix
|
||||
</span>
|
||||
<input
|
||||
class="ant-input custom-input"
|
||||
style="color: red;"
|
||||
type="text"
|
||||
value="123"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-suffix custom-suffix"
|
||||
style="color: yellow;"
|
||||
>
|
||||
<span
|
||||
class="ant-input-show-count-suffix ant-input-show-count-has-suffix custom-count"
|
||||
style="color: green;"
|
||||
>
|
||||
3
|
||||
</span>
|
||||
suffix
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-group-addon"
|
||||
>
|
||||
addon
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
class="ant-input custom-input ant-input-outlined custom-class css-var-root ant-input-css-var"
|
||||
style="color: red; background-color: red;"
|
||||
type="text"
|
||||
value="123"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-group-wrapper ant-input-group-wrapper-outlined custom-class css-var-root ant-input-css-var"
|
||||
style="background-color: red;"
|
||||
>
|
||||
<span
|
||||
class="ant-input-wrapper ant-input-group"
|
||||
>
|
||||
<input
|
||||
class="ant-input custom-input ant-input-outlined"
|
||||
style="color: red;"
|
||||
type="text"
|
||||
value="123"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-group-addon"
|
||||
>
|
||||
addon
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Input allowClear should change type when click 1`] = `
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-outlined css-var-root ant-input-css-var"
|
||||
@ -336,108 +438,6 @@ exports[`Input allowClear should not show icon if value is undefined, null or em
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`Input allowClear should support classNames and styles 1`] = `
|
||||
<div>
|
||||
<span
|
||||
class="rc-input-affix-wrapper rc-input-outlined custom-class css-var-root rc-input-css-var"
|
||||
style="background-color: red;"
|
||||
>
|
||||
<span
|
||||
class="rc-input-prefix custom-prefix"
|
||||
style="color: blue;"
|
||||
>
|
||||
prefix
|
||||
</span>
|
||||
<input
|
||||
class="rc-input custom-input"
|
||||
style="color: red;"
|
||||
type="text"
|
||||
value="123"
|
||||
/>
|
||||
<span
|
||||
class="rc-input-suffix custom-suffix"
|
||||
style="color: yellow;"
|
||||
>
|
||||
<span
|
||||
class="rc-input-show-count-suffix rc-input-show-count-has-suffix custom-count"
|
||||
style="color: green;"
|
||||
>
|
||||
3
|
||||
</span>
|
||||
suffix
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="rc-input-group-wrapper rc-input-group-wrapper-outlined custom-class css-var-root rc-input-css-var"
|
||||
style="background-color: red;"
|
||||
>
|
||||
<span
|
||||
class="rc-input-wrapper rc-input-group"
|
||||
>
|
||||
<span
|
||||
class="rc-input-affix-wrapper rc-input-outlined"
|
||||
>
|
||||
<span
|
||||
class="rc-input-prefix custom-prefix"
|
||||
style="color: blue;"
|
||||
>
|
||||
prefix
|
||||
</span>
|
||||
<input
|
||||
class="rc-input custom-input"
|
||||
style="color: red;"
|
||||
type="text"
|
||||
value="123"
|
||||
/>
|
||||
<span
|
||||
class="rc-input-suffix custom-suffix"
|
||||
style="color: yellow;"
|
||||
>
|
||||
<span
|
||||
class="rc-input-show-count-suffix rc-input-show-count-has-suffix custom-count"
|
||||
style="color: green;"
|
||||
>
|
||||
3
|
||||
</span>
|
||||
suffix
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="rc-input-group-addon"
|
||||
>
|
||||
addon
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
class="rc-input custom-input rc-input-outlined custom-class css-var-root rc-input-css-var"
|
||||
style="color: red; background-color: red;"
|
||||
type="text"
|
||||
value="123"
|
||||
/>
|
||||
<span
|
||||
class="rc-input-group-wrapper rc-input-group-wrapper-outlined custom-class css-var-root rc-input-css-var"
|
||||
style="background-color: red;"
|
||||
>
|
||||
<span
|
||||
class="rc-input-wrapper rc-input-group"
|
||||
>
|
||||
<input
|
||||
class="rc-input custom-input rc-input-outlined"
|
||||
style="color: red;"
|
||||
type="text"
|
||||
value="123"
|
||||
/>
|
||||
<span
|
||||
class="rc-input-group-addon"
|
||||
>
|
||||
addon
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Input rtl render component should be rendered correctly in RTL direction 1`] = `
|
||||
<input
|
||||
class="ant-input ant-input-rtl ant-input-outlined css-var-root ant-input-css-var"
|
||||
|
@ -3,13 +3,13 @@
|
||||
exports[`TextArea allowClear classNames and styles should work 1`] = `
|
||||
<div>
|
||||
<textarea
|
||||
class="ant-input custom-textarea ant-input-outlined css-var-root ant-input-css-var custom-class"
|
||||
class="ant-input custom-textarea ant-input-outlined css-var-root ant-input-css-var custom-class custom-root"
|
||||
style="color: red; background: red;"
|
||||
/>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper ant-input-textarea-show-count ant-input-show-count ant-input-outlined css-var-root ant-input-css-var custom-class"
|
||||
class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper ant-input-textarea-show-count ant-input-show-count ant-input-outlined css-var-root ant-input-css-var custom-class custom-root"
|
||||
data-count="0"
|
||||
style="background: red;"
|
||||
style="color: red; background: red;"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input custom-textarea"
|
||||
|
@ -439,13 +439,12 @@ describe('Input allowClear', () => {
|
||||
expect(container.querySelector('.ant-input-clear-icon')?.textContent).toBe('clear');
|
||||
});
|
||||
|
||||
it('should support classNames and styles', () => {
|
||||
it('semantic dom snapshot', () => {
|
||||
const { container } = render(
|
||||
<>
|
||||
<Input
|
||||
value="123"
|
||||
showCount
|
||||
prefixCls="rc-input"
|
||||
prefix="prefix"
|
||||
suffix="suffix"
|
||||
className="custom-class"
|
||||
@ -467,7 +466,6 @@ describe('Input allowClear', () => {
|
||||
value="123"
|
||||
addonAfter="addon"
|
||||
showCount
|
||||
prefixCls="rc-input"
|
||||
prefix="prefix"
|
||||
suffix="suffix"
|
||||
className="custom-class"
|
||||
@ -487,7 +485,6 @@ describe('Input allowClear', () => {
|
||||
/>
|
||||
<Input
|
||||
value="123"
|
||||
prefixCls="rc-input"
|
||||
className="custom-class"
|
||||
style={{ backgroundColor: 'red' }}
|
||||
classNames={{
|
||||
@ -499,7 +496,6 @@ describe('Input allowClear', () => {
|
||||
/>
|
||||
<Input
|
||||
value="123"
|
||||
prefixCls="rc-input"
|
||||
className="custom-class"
|
||||
addonAfter="addon"
|
||||
style={{ backgroundColor: 'red' }}
|
||||
|
166
components/input/__tests__/semantic.test.tsx
Normal file
166
components/input/__tests__/semantic.test.tsx
Normal file
@ -0,0 +1,166 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Input from '..';
|
||||
|
||||
const testClassNames = {
|
||||
root: 'custom-root',
|
||||
prefix: 'custom-prefix',
|
||||
input: 'custom-input',
|
||||
textarea: 'custom-textarea',
|
||||
suffix: 'custom-suffix',
|
||||
count: 'custom-count',
|
||||
separator: 'custom-separator',
|
||||
button: {
|
||||
root: 'custom-button-root',
|
||||
icon: 'custom-button-icon',
|
||||
content: 'custom-button-content',
|
||||
},
|
||||
};
|
||||
|
||||
const testStyles = {
|
||||
root: { color: 'red' },
|
||||
input: { color: 'blue' },
|
||||
textarea: { color: 'green' },
|
||||
prefix: { color: 'yellow' },
|
||||
suffix: { color: 'purple' },
|
||||
count: { color: 'orange' },
|
||||
separator: { color: 'pink' },
|
||||
button: {
|
||||
root: { color: 'cyan' },
|
||||
icon: { color: 'magenta' },
|
||||
content: { color: 'lime' },
|
||||
},
|
||||
};
|
||||
|
||||
describe('semantic dom', () => {
|
||||
it('input should support classNames and styles', () => {
|
||||
const { container } = render(
|
||||
<Input
|
||||
value="123"
|
||||
showCount
|
||||
prefix="prefix"
|
||||
suffix="suffix"
|
||||
classNames={testClassNames}
|
||||
styles={testStyles}
|
||||
/>,
|
||||
);
|
||||
|
||||
const root = container.querySelector('.ant-input-affix-wrapper');
|
||||
const input = container.querySelector('.ant-input');
|
||||
const prefix = container.querySelector('.ant-input-prefix');
|
||||
const suffix = container.querySelector('.ant-input-suffix');
|
||||
const count = container.querySelector('.ant-input-show-count-suffix');
|
||||
|
||||
expect(root).toHaveClass(testClassNames.root);
|
||||
expect(root).toHaveStyle(testStyles.root);
|
||||
expect(input).toHaveClass(testClassNames.input);
|
||||
expect(input).toHaveStyle(testStyles.input);
|
||||
expect(prefix).toHaveClass(testClassNames.prefix);
|
||||
expect(prefix).toHaveStyle(testStyles.prefix);
|
||||
expect(suffix).toHaveClass(testClassNames.suffix);
|
||||
expect(suffix).toHaveStyle(testStyles.suffix);
|
||||
expect(count).toHaveClass(testClassNames.count);
|
||||
expect(count).toHaveStyle(testStyles.count);
|
||||
});
|
||||
|
||||
it('textarea should support classNames and styles', () => {
|
||||
const { container } = render(
|
||||
<Input.TextArea classNames={testClassNames} styles={testStyles} showCount />,
|
||||
);
|
||||
|
||||
const root = container.querySelector('.ant-input-textarea-affix-wrapper');
|
||||
const textarea = container.querySelector('textarea');
|
||||
const count = container.querySelector('.ant-input-data-count');
|
||||
|
||||
expect(root).toHaveClass(testClassNames.root);
|
||||
expect(root).toHaveStyle(testStyles.root);
|
||||
expect(textarea).toHaveClass(testClassNames.textarea);
|
||||
expect(textarea).toHaveStyle(testStyles.textarea);
|
||||
expect(count).toHaveClass(testClassNames.count);
|
||||
expect(count).toHaveStyle(testStyles.count);
|
||||
});
|
||||
|
||||
it('search should support classNames and styles', () => {
|
||||
const { container, getByText } = render(
|
||||
<Input.Search
|
||||
loading
|
||||
enterButton="button text"
|
||||
classNames={testClassNames}
|
||||
styles={testStyles}
|
||||
showCount
|
||||
prefix="prefix"
|
||||
suffix="suffix"
|
||||
/>,
|
||||
);
|
||||
|
||||
const root = container.querySelector('.ant-input-search');
|
||||
const input = container.querySelector('.ant-input');
|
||||
const prefix = container.querySelector('.ant-input-prefix');
|
||||
const suffix = container.querySelector('.ant-input-suffix');
|
||||
const button = container.querySelector('.ant-btn');
|
||||
const buttonIcon = container.querySelector('.ant-btn-icon');
|
||||
const buttonContent = getByText('button text');
|
||||
const count = container.querySelector('.ant-input-show-count-suffix');
|
||||
|
||||
expect(root).toHaveClass(testClassNames.root);
|
||||
expect(root).toHaveStyle(testStyles.root);
|
||||
expect(input).toHaveClass(testClassNames.input);
|
||||
expect(input).toHaveStyle(testStyles.input);
|
||||
expect(prefix).toHaveClass(testClassNames.prefix);
|
||||
expect(prefix).toHaveStyle(testStyles.prefix);
|
||||
expect(suffix).toHaveClass(testClassNames.suffix);
|
||||
expect(suffix).toHaveStyle(testStyles.suffix);
|
||||
expect(button).toHaveClass(testClassNames.button.root);
|
||||
expect(button).toHaveStyle(testStyles.button.root);
|
||||
expect(buttonIcon).toHaveClass(testClassNames.button.icon);
|
||||
expect(buttonIcon).toHaveStyle(testStyles.button.icon);
|
||||
expect(buttonContent).toHaveClass(testClassNames.button.content);
|
||||
expect(buttonContent).toHaveStyle(testStyles.button.content);
|
||||
expect(count).toHaveClass(testClassNames.count);
|
||||
expect(count).toHaveStyle(testStyles.count);
|
||||
});
|
||||
|
||||
it('password should support classNames and styles', () => {
|
||||
const { container } = render(
|
||||
<Input.Password
|
||||
classNames={testClassNames}
|
||||
styles={testStyles}
|
||||
showCount
|
||||
prefix="prefix"
|
||||
suffix="suffix"
|
||||
/>,
|
||||
);
|
||||
const root = container.querySelector('.ant-input-affix-wrapper');
|
||||
const input = container.querySelector('.ant-input');
|
||||
const prefix = container.querySelector('.ant-input-prefix');
|
||||
const suffix = container.querySelector('.ant-input-suffix');
|
||||
const count = container.querySelector('.ant-input-show-count-suffix');
|
||||
|
||||
expect(root).toHaveClass(testClassNames.root);
|
||||
expect(root).toHaveStyle(testStyles.root);
|
||||
expect(input).toHaveClass(testClassNames.input);
|
||||
expect(input).toHaveStyle(testStyles.input);
|
||||
expect(prefix).toHaveClass(testClassNames.prefix);
|
||||
expect(prefix).toHaveStyle(testStyles.prefix);
|
||||
expect(suffix).toHaveClass(testClassNames.suffix);
|
||||
expect(suffix).toHaveStyle(testStyles.suffix);
|
||||
expect(count).toHaveClass(testClassNames.count);
|
||||
expect(count).toHaveStyle(testStyles.count);
|
||||
});
|
||||
|
||||
it('otp should support classNames and styles', () => {
|
||||
const { container } = render(
|
||||
<Input.OTP separator="-" classNames={testClassNames} styles={testStyles} />,
|
||||
);
|
||||
const root = container.querySelector('.ant-otp');
|
||||
const input = container.querySelector('.ant-input');
|
||||
const separator = container.querySelector('.ant-otp-separator');
|
||||
expect(root).toHaveClass(testClassNames.root);
|
||||
expect(root).toHaveStyle(testStyles.root);
|
||||
expect(input).toHaveClass(testClassNames.input);
|
||||
expect(input).toHaveStyle(testStyles.input);
|
||||
expect(separator).toHaveClass(testClassNames.separator);
|
||||
expect(separator).toHaveStyle(testStyles.separator);
|
||||
});
|
||||
});
|
@ -489,10 +489,14 @@ describe('TextArea allowClear', () => {
|
||||
className="custom-class"
|
||||
style={{ background: 'red' }}
|
||||
classNames={{
|
||||
root: 'custom-root',
|
||||
textarea: 'custom-textarea',
|
||||
count: 'custom-count',
|
||||
}}
|
||||
styles={{
|
||||
root: {
|
||||
color: 'red',
|
||||
},
|
||||
textarea: {
|
||||
color: 'red',
|
||||
},
|
||||
@ -506,10 +510,14 @@ describe('TextArea allowClear', () => {
|
||||
className="custom-class"
|
||||
style={{ background: 'red' }}
|
||||
classNames={{
|
||||
root: 'custom-root',
|
||||
textarea: 'custom-textarea',
|
||||
count: 'custom-count',
|
||||
}}
|
||||
styles={{
|
||||
root: {
|
||||
color: 'red',
|
||||
},
|
||||
textarea: {
|
||||
color: 'red',
|
||||
},
|
||||
|
@ -7,12 +7,14 @@ import useLocale from '../../../.dumi/hooks/useLocale';
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
root: '根元素',
|
||||
input: '输入框元素',
|
||||
prefix: '前缀的包裹元素',
|
||||
suffix: '后缀的包裹元素',
|
||||
count: '文字计数元素',
|
||||
},
|
||||
en: {
|
||||
root: 'root element',
|
||||
input: 'input element',
|
||||
prefix: 'prefix element',
|
||||
suffix: 'suffix element',
|
||||
@ -26,10 +28,11 @@ const App: React.FC = () => {
|
||||
<SemanticPreview
|
||||
componentName="Input"
|
||||
semantics={[
|
||||
{ name: 'input', desc: locale.input, version: '5.4.0' },
|
||||
{ name: 'prefix', desc: locale.prefix, version: '5.4.0' },
|
||||
{ name: 'suffix', desc: locale.suffix, version: '5.4.0' },
|
||||
{ name: 'count', desc: locale.count, version: '5.4.0' },
|
||||
{ name: 'root', desc: locale.root },
|
||||
{ name: 'prefix', desc: locale.prefix },
|
||||
{ name: 'input', desc: locale.input },
|
||||
{ name: 'suffix', desc: locale.suffix },
|
||||
{ name: 'count', desc: locale.count },
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
|
36
components/input/demo/_semantic_otp.tsx
Normal file
36
components/input/demo/_semantic_otp.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { Input } from 'antd';
|
||||
|
||||
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
|
||||
import useLocale from '../../../.dumi/hooks/useLocale';
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
root: '根元素',
|
||||
input: '输入框元素',
|
||||
separator: '分隔符',
|
||||
},
|
||||
en: {
|
||||
root: 'root element',
|
||||
input: 'input element',
|
||||
separator: 'separator element',
|
||||
},
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [locale] = useLocale(locales);
|
||||
return (
|
||||
<SemanticPreview
|
||||
componentName="Input.OTP"
|
||||
semantics={[
|
||||
{ name: 'root', desc: locale.root },
|
||||
{ name: 'input', desc: locale.input },
|
||||
{ name: 'separator', desc: locale.separator },
|
||||
]}
|
||||
>
|
||||
<Input.OTP separator="-" />
|
||||
</SemanticPreview>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
48
components/input/demo/_semantic_password.tsx
Normal file
48
components/input/demo/_semantic_password.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { EditOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import { Input } from 'antd';
|
||||
|
||||
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
|
||||
import useLocale from '../../../.dumi/hooks/useLocale';
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
root: '根元素',
|
||||
input: '输入框元素',
|
||||
prefix: '前缀的包裹元素',
|
||||
suffix: '后缀的包裹元素',
|
||||
count: '文字计数元素',
|
||||
},
|
||||
en: {
|
||||
root: 'root element',
|
||||
input: 'input element',
|
||||
prefix: 'prefix element',
|
||||
suffix: 'suffix element',
|
||||
count: 'count element',
|
||||
},
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [locale] = useLocale(locales);
|
||||
return (
|
||||
<SemanticPreview
|
||||
componentName="Input.Password"
|
||||
semantics={[
|
||||
{ name: 'root', desc: locale.root },
|
||||
{ name: 'prefix', desc: locale.prefix },
|
||||
{ name: 'input', desc: locale.input },
|
||||
{ name: 'suffix', desc: locale.suffix },
|
||||
{ name: 'count', desc: locale.count },
|
||||
]}
|
||||
>
|
||||
<Input.Password
|
||||
showCount
|
||||
prefix={<UserOutlined />}
|
||||
suffix={<EditOutlined />}
|
||||
defaultValue="Hello, Ant Design"
|
||||
/>
|
||||
</SemanticPreview>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
59
components/input/demo/_semantic_search.tsx
Normal file
59
components/input/demo/_semantic_search.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import { EditOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import { Input } from 'antd';
|
||||
|
||||
import useLocale from '../../../.dumi/hooks/useLocale';
|
||||
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
root: '根元素',
|
||||
input: '输入框元素',
|
||||
prefix: '前缀的包裹元素',
|
||||
suffix: '后缀的包裹元素',
|
||||
count: '文字计数元素',
|
||||
'button.root': '按钮根元素',
|
||||
'button.icon': '按钮图标元素',
|
||||
'button.content': '按钮内容元素',
|
||||
},
|
||||
en: {
|
||||
root: 'root element',
|
||||
input: 'input element',
|
||||
prefix: 'prefix element',
|
||||
suffix: 'suffix element',
|
||||
count: 'count element',
|
||||
'button.root': 'button root element',
|
||||
'button.icon': 'button icon element',
|
||||
'button.content': 'button content element',
|
||||
},
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [locale] = useLocale(locales);
|
||||
return (
|
||||
<SemanticPreview
|
||||
componentName="Input.Search"
|
||||
semantics={[
|
||||
{ name: 'root', desc: locale.root },
|
||||
{ name: 'prefix', desc: locale.prefix },
|
||||
{ name: 'input', desc: locale.input },
|
||||
{ name: 'suffix', desc: locale.suffix },
|
||||
{ name: 'count', desc: locale.count },
|
||||
{ name: 'button.root', desc: locale['button.root'] },
|
||||
{ name: 'button.icon', desc: locale['button.icon'] },
|
||||
{ name: 'button.content', desc: locale['button.content'] },
|
||||
]}
|
||||
>
|
||||
<Input.Search
|
||||
loading
|
||||
enterButton="Searching..."
|
||||
showCount
|
||||
prefix={<UserOutlined />}
|
||||
suffix={<EditOutlined />}
|
||||
defaultValue="Hello, Ant Design"
|
||||
/>
|
||||
</SemanticPreview>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
@ -6,10 +6,12 @@ import useLocale from '../../../.dumi/hooks/useLocale';
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
textarea: '输入框元素',
|
||||
root: '根元素',
|
||||
textarea: '文本域元素',
|
||||
count: '文字计数元素',
|
||||
},
|
||||
en: {
|
||||
root: 'root element',
|
||||
textarea: 'textarea element',
|
||||
count: 'count element',
|
||||
},
|
||||
@ -19,10 +21,11 @@ const App: React.FC = () => {
|
||||
const [locale] = useLocale(locales);
|
||||
return (
|
||||
<SemanticPreview
|
||||
componentName="TextArea"
|
||||
componentName="Input.TextArea"
|
||||
semantics={[
|
||||
{ name: 'textarea', desc: locale.textarea, version: '5.4.0' },
|
||||
{ name: 'count', desc: locale.count, version: '5.4.0' },
|
||||
{ name: 'root', desc: locale.root },
|
||||
{ name: 'textarea', desc: locale.textarea },
|
||||
{ name: 'count', desc: locale.count },
|
||||
]}
|
||||
>
|
||||
<Input.TextArea defaultValue="Hello, Ant Design" rows={3} count={{ max: 100, show: true }} />
|
||||
|
@ -55,7 +55,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| addonBefore | The label text displayed before (on the left side of) the input field | ReactNode | - | |
|
||||
| allowClear | If allow to remove input content with clear icon | boolean \| { clearIcon: ReactNode } | false | |
|
||||
| ~~bordered~~ | Whether has border style, please use `variant` instead | boolean | true | 4.5.0 |
|
||||
| classNames | Semantic DOM class | Record<[SemanticDOM](#input-1), string> | - | 5.4.0 |
|
||||
| classNames | Semantic DOM class | Record<[SemanticDOM](#semantic-input), string> | - | 5.4.0 |
|
||||
| count | Character count config | [CountConfig](#countconfig) | - | 5.10.0 |
|
||||
| defaultValue | The initial input content | string | - | |
|
||||
| disabled | Whether the input is disabled | boolean | false | |
|
||||
@ -64,7 +64,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| prefix | The prefix icon for the Input | ReactNode | - | |
|
||||
| showCount | Whether to show character count | boolean \| { formatter: (info: { value: string, count: number, maxLength?: number }) => ReactNode } | false | 4.18.0 info.value: 4.23.0 |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| styles | Semantic DOM style | Record<[SemanticDOM](#input-1), CSSProperties> | - | 5.4.0 |
|
||||
| styles | Semantic DOM style | Record<[SemanticDOM](#semantic-input), CSSProperties> | - | 5.4.0 |
|
||||
| size | The size of the input box. Note: in the context of a form, the `middle` size is used | `large` \| `middle` \| `small` | - | |
|
||||
| suffix | The suffix icon for the Input | ReactNode | - | |
|
||||
| type | The type of input, see: [MDN](https://developer.mozilla.org/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types)( use `Input.TextArea` instead of `type="textarea"`) | string | `text` | |
|
||||
@ -100,18 +100,20 @@ Same as Input, and more:
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| autoSize | Height auto size feature, can be set to true \| false or an object { minRows: 2, maxRows: 6 } | boolean \| object | false | |
|
||||
| classNames | Semantic DOM class | Record<[SemanticDOM](#inputtextarea-1), string> | - | 5.4.0 |
|
||||
| styles | Semantic DOM style | Record<[SemanticDOM](#inputtextarea-1), CSSProperties> | - | 5.4.0 |
|
||||
| classNames | Semantic DOM class | Record<[SemanticDOM](#semantic-textarea), string> | - | 5.4.0 |
|
||||
| styles | Semantic DOM style | Record<[SemanticDOM](#semantic-textarea), CSSProperties> | - | 5.4.0 |
|
||||
|
||||
The rest of the props of `Input.TextArea` are the same as the original [textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea).
|
||||
|
||||
### Input.Search
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| classNames | Semantic DOM class | Record<[SemanticDOM](#semantic-search), string> | - | |
|
||||
| enterButton | false displays the default button color, true uses the primary color, or you can provide a custom button. Conflicts with addonAfter. | ReactNode | false |
|
||||
| loading | Search box with loading | boolean | false |
|
||||
| onSearch | The callback function triggered when you click on the search-icon, the clear-icon or press the Enter key | function(value, event, { source: "input" \| "clear" }) | - |
|
||||
| styles | Semantic DOM style | Record<[SemanticDOM](#semantic-search), CSSProperties> | - | |
|
||||
|
||||
Supports all props of `Input`.
|
||||
|
||||
@ -119,7 +121,9 @@ Supports all props of `Input`.
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| classNames | Semantic DOM class | Record<[SemanticDOM](#semantic-password), string> | - | |
|
||||
| iconRender | Custom toggle button | (visible) => ReactNode | (visible) => (visible ? <EyeOutlined /> : <EyeInvisibleOutlined />) | 4.3.0 |
|
||||
| styles | Semantic DOM style | Record<[SemanticDOM](#semantic-password), CSSProperties> | - | |
|
||||
| visibilityToggle | Whether show toggle button or control password visible | boolean \| [VisibilityToggle](#visibilitytoggle) | true | |
|
||||
|
||||
### Input.OTP
|
||||
@ -132,10 +136,12 @@ Added in `5.16.0`.
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| classNames | Semantic DOM class | Record<[SemanticDOM](#semantic-otp), string> | - | |
|
||||
| defaultValue | Default value | string | - | |
|
||||
| disabled | Whether the input is disabled | boolean | false | |
|
||||
| formatter | Format display, blank fields will be filled with ` ` | (value: string) => string | - | |
|
||||
| separator | render the separator after the input box of the specified index | ReactNode \|((i: number) => ReactNode) | - | 5.24.0 |
|
||||
| styles | Semantic DOM style | Record<[SemanticDOM](#semantic-otp), CSSProperties> | - | |
|
||||
| mask | Custom display, the original value will not be modified | boolean \| string | `false` | `5.17.0` |
|
||||
| length | The number of input elements | number | 6 | |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | |
|
||||
@ -161,14 +167,26 @@ Added in `5.16.0`.
|
||||
|
||||
## Semantic DOM
|
||||
|
||||
### Input
|
||||
### Input {#semantic-input}
|
||||
|
||||
<code src="./demo/_semantic_input.tsx" simplify="true"></code>
|
||||
|
||||
### Input.TextArea
|
||||
### Input.TextArea {#semantic-textarea}
|
||||
|
||||
<code src="./demo/_semantic_textarea.tsx" simplify="true"></code>
|
||||
|
||||
### Input.Search {#semantic-search}
|
||||
|
||||
<code src="./demo/_semantic_search.tsx" simplify="true"></code>
|
||||
|
||||
### Input.Password {#semantic-password}
|
||||
|
||||
<code src="./demo/_semantic_password.tsx" simplify="true"></code>
|
||||
|
||||
### Input.OTP {#semantic-otp}
|
||||
|
||||
<code src="./demo/_semantic_otp.tsx" simplify="true"></code>
|
||||
|
||||
## Design Token
|
||||
|
||||
<ComponentTokenTable component="Input"></ComponentTokenTable>
|
||||
|
@ -56,7 +56,7 @@ demo:
|
||||
| addonBefore | 带标签的 input,设置前置标签 | ReactNode | - | |
|
||||
| allowClear | 可以点击清除图标删除内容 | boolean \| { clearIcon: ReactNode } | - | |
|
||||
| ~~bordered~~ | 是否有边框, 请使用 `variant` 替换 | boolean | true | 4.5.0 |
|
||||
| classNames | 语义化结构 class | Record<[SemanticDOM](#input-1), string> | - | 5.4.0 |
|
||||
| classNames | 语义化结构 class | Record<[SemanticDOM](#semantic-input), string> | - | 5.4.0 |
|
||||
| count | 字符计数配置 | [CountConfig](#countconfig) | - | 5.10.0 |
|
||||
| defaultValue | 输入框默认内容 | string | - | |
|
||||
| disabled | 是否禁用状态,默认为 false | boolean | false | |
|
||||
@ -65,7 +65,7 @@ demo:
|
||||
| prefix | 带有前缀图标的 input | ReactNode | - | |
|
||||
| showCount | 是否展示字数 | boolean \| { formatter: (info: { value: string, count: number, maxLength?: number }) => ReactNode } | false | 4.18.0 info.value: 4.23.0 |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| styles | 语义化结构 style | Record<[SemanticDOM](#input-1), CSSProperties> | - | 5.4.0 |
|
||||
| styles | 语义化结构 style | Record<[SemanticDOM](#semantic-input), CSSProperties> | - | 5.4.0 |
|
||||
| size | 控件大小。注:标准表单内的输入框大小限制为 `middle` | `large` \| `middle` \| `small` | - | |
|
||||
| suffix | 带有后缀图标的 input | ReactNode | - | |
|
||||
| type | 声明 input 类型,同原生 input 标签的 type 属性,见:[MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input#属性)(请直接使用 `Input.TextArea` 代替 `type="textarea"`) | string | `text` | |
|
||||
@ -101,18 +101,20 @@ interface CountConfig {
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| autoSize | 自适应内容高度,可设置为 true \| false 或对象:{ minRows: 2, maxRows: 6 } | boolean \| object | false | |
|
||||
| classNames | 语义化结构 class | Record<[SemanticDOM](#inputtextarea-1), string> | - | 5.4.0 |
|
||||
| styles | 语义化结构 style | Record<[SemanticDOM](#inputtextarea-1), CSSProperties> | - | 5.4.0 |
|
||||
| classNames | 语义化结构 class | Record<[SemanticDOM](#semantic-textarea), string> | - | 5.4.0 |
|
||||
| styles | 语义化结构 style | Record<[SemanticDOM](#semantic-textarea), CSSProperties> | - | 5.4.0 |
|
||||
|
||||
`Input.TextArea` 的其他属性和浏览器自带的 [textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) 一致。
|
||||
|
||||
### Input.Search
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| classNames | 语义化结构 class | Record<[SemanticDOM](#semantic-search), string> | - | |
|
||||
| enterButton | 是否有确认按钮,可设为按钮文字。该属性会与 `addonAfter` 冲突。 | ReactNode | false |
|
||||
| loading | 搜索 loading | boolean | false |
|
||||
| onSearch | 点击搜索图标、清除图标,或按下回车键时的回调 | function(value, event, { source: "input" \| "clear" }) | - |
|
||||
| styles | 语义化结构 style | Record<[SemanticDOM](#semantic-search), CSSProperties> | - | |
|
||||
|
||||
其余属性和 Input 一致。
|
||||
|
||||
@ -120,7 +122,9 @@ interface CountConfig {
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| classNames | 语义化结构 class | Record<[SemanticDOM](#semantic-password), string> | - | |
|
||||
| iconRender | 自定义切换按钮 | (visible) => ReactNode | (visible) => (visible ? <EyeOutlined /> : <EyeInvisibleOutlined />) | 4.3.0 |
|
||||
| styles | 语义化结构 style | Record<[SemanticDOM](#semantic-password), CSSProperties> | - | |
|
||||
| visibilityToggle | 是否显示切换按钮或者控制密码显隐 | boolean \| [VisibilityToggle](#visibilitytoggle) | true | |
|
||||
|
||||
### Input.OTP
|
||||
@ -133,6 +137,7 @@ interface CountConfig {
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| classNames | 语义化结构 class | Record<[SemanticDOM](#semantic-otp), string> | - | |
|
||||
| defaultValue | 默认值 | string | - | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| formatter | 格式化展示,留空字段会被 ` ` 填充 | (value: string) => string | - | |
|
||||
@ -140,6 +145,7 @@ interface CountConfig {
|
||||
| mask | 自定义展示,和 `formatter` 的区别是不会修改原始值 | boolean \| string | `false` | `5.17.0` |
|
||||
| length | 输入元素数量 | number | 6 | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | |
|
||||
| styles | 语义化结构 style | Record<[SemanticDOM](#semantic-otp), CSSProperties> | - | |
|
||||
| size | 输入框大小 | `small` \| `middle` \| `large` | `middle` | |
|
||||
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` \| `underlined` | `outlined` | `underlined`: 5.24.0 |
|
||||
| value | 输入框内容 | string | - | |
|
||||
@ -162,14 +168,26 @@ interface CountConfig {
|
||||
|
||||
## Semantic DOM
|
||||
|
||||
### Input
|
||||
### Input {#semantic-input}
|
||||
|
||||
<code src="./demo/_semantic_input.tsx" simplify="true"></code>
|
||||
|
||||
### Input.TextArea
|
||||
### Input.TextArea {#semantic-textarea}
|
||||
|
||||
<code src="./demo/_semantic_textarea.tsx" simplify="true"></code>
|
||||
|
||||
### Input.Search {#semantic-search}
|
||||
|
||||
<code src="./demo/_semantic_search.tsx" simplify="true"></code>
|
||||
|
||||
### Input.Password {#semantic-password}
|
||||
|
||||
<code src="./demo/_semantic_password.tsx" simplify="true"></code>
|
||||
|
||||
### Input.OTP {#semantic-otp}
|
||||
|
||||
<code src="./demo/_semantic_otp.tsx" simplify="true"></code>
|
||||
|
||||
## 主题变量(Design Token)
|
||||
|
||||
<ComponentTokenTable component="Input"></ComponentTokenTable>
|
||||
|
@ -116,9 +116,9 @@ exports[`site test Component components/image en Page 1`] = `4`;
|
||||
|
||||
exports[`site test Component components/image zh Page 1`] = `4`;
|
||||
|
||||
exports[`site test Component components/input en Page 1`] = `7`;
|
||||
exports[`site test Component components/input en Page 1`] = `6`;
|
||||
|
||||
exports[`site test Component components/input zh Page 1`] = `7`;
|
||||
exports[`site test Component components/input zh Page 1`] = `6`;
|
||||
|
||||
exports[`site test Component components/input-number en Page 1`] = `2`;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user