chore: auto merge branchs (#34284)

chore: merge master into next
This commit is contained in:
github-actions[bot] 2022-03-03 09:52:51 +00:00 committed by GitHub
commit 6b08487afc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
212 changed files with 23944 additions and 5088 deletions

View File

@ -1,5 +1,6 @@
import { CSSMotionProps, MotionEventHandler, MotionEndEventHandler } from 'rc-motion';
import { MotionEvent } from 'rc-motion/lib/interface';
import { tuple } from './type';
// ================== Collapse Motion ==================
const getCollapsedHeight: MotionEventHandler = () => ({ height: 0, opacity: 0 });
@ -25,11 +26,21 @@ const collapseMotion: CSSMotionProps = {
motionDeadline: 500,
};
const SelectPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight');
export type SelectCommonPlacement = typeof SelectPlacements[number];
const getTransitionDirection = (placement: SelectCommonPlacement | undefined) => {
if (placement !== undefined && (placement === 'topLeft' || placement === 'topRight')) {
return `slide-down`;
}
return `slide-up`;
};
const getTransitionName = (rootPrefixCls: string, motion: string, transitionName?: string) => {
if (transitionName !== undefined) {
return transitionName;
}
return `${rootPrefixCls}-${motion}`;
};
export { getTransitionName };
export { getTransitionName, getTransitionDirection };
export default collapseMotion;

View File

@ -42,6 +42,7 @@ export default function getPlacements(config: PlacementsConfig) {
horizontalArrowShift = 16,
verticalArrowShift = 8,
autoAdjustOverflow,
arrowPointAtCenter,
} = config;
const placementMap: BuildInPlacements = {
left: {
@ -94,7 +95,7 @@ export default function getPlacements(config: PlacementsConfig) {
},
};
Object.keys(placementMap).forEach(key => {
placementMap[key] = config.arrowPointAtCenter
placementMap[key] = arrowPointAtCenter
? {
...placementMap[key],
overflow: getOverflowOptions(autoAdjustOverflow),

View File

@ -0,0 +1,44 @@
import React from 'react';
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import classNames from 'classnames';
import { ValidateStatus } from '../form/FormItem';
import { tuple } from './type';
const InputStatuses = tuple('warning', 'error', '');
export type InputStatus = typeof InputStatuses[number];
const iconMap = {
success: CheckCircleFilled,
warning: ExclamationCircleFilled,
error: CloseCircleFilled,
validating: LoadingOutlined,
};
export const getFeedbackIcon = (prefixCls: string, status?: ValidateStatus) => {
const IconNode = status && iconMap[status];
return IconNode ? (
<span className={`${prefixCls}-feedback-icon`}>
<IconNode />
</span>
) : null;
};
export function getStatusClassNames(
prefixCls: string,
status?: ValidateStatus,
hasFeedback?: boolean,
) {
return classNames({
[`${prefixCls}-status-success`]: status === 'success',
[`${prefixCls}-status-warning`]: status === 'warning',
[`${prefixCls}-status-error`]: status === 'error',
[`${prefixCls}-status-validating`]: status === 'validating',
[`${prefixCls}-has-feedback`]: hasFeedback,
});
}
export const getMergedStatus = (contextStatus?: ValidateStatus, customStatus?: InputStatus) =>
customStatus || contextStatus;

View File

@ -17,7 +17,7 @@ class AffixMounter extends React.Component<{
}> {
private container: HTMLDivElement;
public affix: Affix;
public affix: React.Component<AffixProps, AffixState>;
componentDidMount() {
this.container.addEventListener = jest
@ -58,7 +58,7 @@ describe('Affix Render', () => {
const domMock = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect');
let affixMounterWrapper: ReactWrapper<unknown, unknown, AffixMounter>;
let affixWrapper: ReactWrapper<AffixProps, AffixState, Affix>;
let affixWrapper: ReactWrapper<AffixProps, AffixState, React.Component<AffixProps, AffixState>>;
const classRect: Record<string, DOMRect> = {
container: {
@ -157,9 +157,9 @@ describe('Affix Render', () => {
const getTarget = () => container;
affixWrapper = mount(<Affix target={getTarget}>{null}</Affix>);
affixWrapper.setProps({ target: () => null });
expect(affixWrapper.instance().state.status).toBe(0);
expect(affixWrapper.instance().state.affixStyle).toBe(undefined);
expect(affixWrapper.instance().state.placeholderStyle).toBe(undefined);
expect(affixWrapper.find('Affix').last().state().status).toBe(0);
expect(affixWrapper.find('Affix').last().state().affixStyle).toBe(undefined);
expect(affixWrapper.find('Affix').last().state().placeholderStyle).toBe(undefined);
});
it('instance change', async () => {

View File

@ -126,7 +126,7 @@ class Affix extends React.Component<AffixProps, AffixState> {
getOffsetTop = () => {
const { offsetBottom, offsetTop } = this.props;
return (offsetBottom === undefined && offsetTop === undefined) ? 0 : offsetTop;
return offsetBottom === undefined && offsetTop === undefined ? 0 : offsetTop;
};
getOffsetBottom = () => this.props.offsetBottom;
@ -286,4 +286,10 @@ class Affix extends React.Component<AffixProps, AffixState> {
}
}
export default Affix;
const AffixFC = React.forwardRef<Affix, AffixProps>((props, ref) => <Affix {...props} ref={ref} />);
if (process.env.NODE_ENV !== 'production') {
AffixFC.displayName = 'Affix';
}
export default AffixFC;

View File

@ -1,5 +1,4 @@
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import Affix from '.';
export type BindElement = HTMLElement | Window | null | undefined;
@ -41,7 +40,7 @@ const TRIGGER_EVENTS = [
interface ObserverEntity {
target: HTMLElement | Window;
affixList: Affix[];
affixList: any[];
eventHandlers: { [eventName: string]: any };
}
@ -52,7 +51,7 @@ export function getObserverEntities() {
return observerEntities;
}
export function addObserveTarget(target: HTMLElement | Window | null, affix: Affix): void {
export function addObserveTarget<T>(target: HTMLElement | Window | null, affix: T): void {
if (!target) return;
let entity: ObserverEntity | undefined = observerEntities.find(item => item.target === target);
@ -78,7 +77,7 @@ export function addObserveTarget(target: HTMLElement | Window | null, affix: Aff
}
}
export function removeObserveTarget(affix: Affix): void {
export function removeObserveTarget<T>(affix: T): void {
const observerEntity = observerEntities.find(oriObserverEntity => {
const hasAffix = oriObserverEntity.affixList.some(item => item === affix);
if (hasAffix) {

View File

@ -107,7 +107,7 @@ exports[`renders ./components/auto-complete/demo/certain-category.md extend cont
class="ant-select-selection-search"
>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-select-selection-search-input"
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-select-selection-search-input ant-input-group-wrapper-lg"
>
<span
class="ant-input-wrapper ant-input-group"
@ -1876,6 +1876,107 @@ exports[`renders ./components/auto-complete/demo/options.md extend context corre
</div>
`;
exports[`renders ./components/auto-complete/demo/status.md extend context correctly 1`] = `
<div
class="ant-space ant-space-vertical"
style="width:100%"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-select ant-select-status-error ant-select-auto-complete ant-select-single ant-select-show-search"
style="width:200px"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-activedescendant="undefined_list_0"
aria-autocomplete="list"
aria-controls="undefined_list"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
role="combobox"
type="search"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
/>
</div>
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
>
<div>
<div
class="ant-select-item-empty"
id="undefined_list"
role="listbox"
/>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-select ant-select-status-warning ant-select-auto-complete ant-select-single ant-select-show-search"
style="width:200px"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-activedescendant="undefined_list_0"
aria-autocomplete="list"
aria-controls="undefined_list"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
role="combobox"
type="search"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
/>
</div>
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
>
<div>
<div
class="ant-select-item-empty"
id="undefined_list"
role="listbox"
/>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/auto-complete/demo/uncertain-category.md extend context correctly 1`] = `
<div
class="ant-select ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
@ -1888,7 +1989,7 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md extend co
class="ant-select-selection-search"
>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-input-search-with-button ant-select-selection-search-input"
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-input-search-with-button ant-select-selection-search-input ant-input-group-wrapper-lg"
>
<span
class="ant-input-wrapper ant-input-group"

View File

@ -79,7 +79,7 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
class="ant-select-selection-search"
>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-select-selection-search-input"
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-select-selection-search-input ant-input-group-wrapper-lg"
>
<span
class="ant-input-wrapper ant-input-group"
@ -1033,6 +1033,79 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
</div>
`;
exports[`renders ./components/auto-complete/demo/status.md correctly 1`] = `
<div
class="ant-space ant-space-vertical"
style="width:100%"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-select ant-select-status-error ant-select-auto-complete ant-select-single ant-select-show-search"
style="width:200px"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-activedescendant="undefined_list_0"
aria-autocomplete="list"
aria-controls="undefined_list"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
role="combobox"
type="search"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
/>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-select ant-select-status-warning ant-select-auto-complete ant-select-single ant-select-show-search"
style="width:200px"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-activedescendant="undefined_list_0"
aria-autocomplete="list"
aria-controls="undefined_list"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
role="combobox"
type="search"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
/>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly 1`] = `
<div
class="ant-select ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
@ -1045,7 +1118,7 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly
class="ant-select-selection-search"
>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-input-search-with-button ant-select-selection-search-input"
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-input-search-with-button ant-select-selection-search-input ant-input-group-wrapper-lg"
>
<span
class="ant-input-wrapper ant-input-group"

View File

@ -0,0 +1,42 @@
---
order: 19
version: 4.19.0
title:
zh-CN: 自定义状态
en-US: Status
---
## zh-CN
使用 `status` 为 AutoComplete 添加状态,可选 `error` 或者 `warning`
## en-US
Add status to AutoComplete with `status`, which could be `error` or `warning`.
```tsx
import React, { useState } from 'react';
import { AutoComplete, Space } from 'antd';
const mockVal = (str: string, repeat: number = 1) => ({
value: str.repeat(repeat),
});
const ValidateInputs: React.FC = () => {
const [options, setOptions] = useState<{ value: string }[]>([]);
const onSearch = (searchText: string) => {
setOptions(
!searchText ? [] : [mockVal(searchText), mockVal(searchText, 2), mockVal(searchText, 3)],
);
};
return (
<Space direction="vertical" style={{ width: '100%' }}>
<AutoComplete options={options} onSearch={onSearch} status="error" style={{ width: 200 }} />
<AutoComplete options={options} onSearch={onSearch} status="warning" style={{ width: 200 }} />
</Space>
);
};
ReactDOM.render(<ValidateInputs />, mountNode);
```

View File

@ -38,6 +38,7 @@ The differences with Select are:
| open | Controlled open state of dropdown | boolean | - | |
| options | Select options. Will get better perf than jsx definition | { label, value }\[] | - | |
| placeholder | The placeholder of input | string | - | |
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| value | Selected option | string | - | |
| onBlur | Called when leaving the component | function() | - | |
| onChange | Called when select an option or input value change, or value of input is changed | function(value) | - | |
@ -48,10 +49,10 @@ The differences with Select are:
## Methods
| Name | Description | Version |
| --- | --- | --- |
| blur() | Remove focus | |
| focus() | Get focus | |
| Name | Description | Version |
| ------- | ------------ | ------- |
| blur() | Remove focus | |
| focus() | Get focus | |
## FAQ

View File

@ -20,6 +20,7 @@ import Select, {
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import devWarning from '../_util/devWarning';
import { isValidElement } from '../_util/reactNode';
import { InputStatus } from '../_util/statusUtils';
const { Option } = Select;
@ -37,6 +38,7 @@ export interface AutoCompleteProps<
'inputIcon' | 'loading' | 'mode' | 'optionLabelProp' | 'labelInValue'
> {
dataSource?: DataSourceItemType[];
status?: InputStatus;
}
function isSelectOptionOrSelectOptGroup(child: any): Boolean {

View File

@ -40,6 +40,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/qtJm4yt45/AutoComplete.svg
| open | 是否展开下拉菜单 | boolean | - | |
| options | 数据化配置选项内容,相比 jsx 定义会获得更好的渲染性能 | { label, value }\[] | - | |
| placeholder | 输入框提示 | string | - | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| value | 指定当前选中的条目 | string | - | |
| onBlur | 失去焦点时的回调 | function() | - | |
| onChange | 选中 option或 input 的 value 变化时,调用此函数 | function(value) | - | |
@ -50,10 +51,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/qtJm4yt45/AutoComplete.svg
## 方法
| 名称 | 描述 | 版本 |
| --- | --- | --- |
| blur() | 移除焦点 | |
| focus() | 获取焦点 | |
| 名称 | 描述 | 版本 |
| ------- | -------- | ---- |
| blur() | 移除焦点 | |
| focus() | 获取焦点 | |
## FAQ

View File

@ -30,7 +30,7 @@ const BreadcrumbItem: BreadcrumbItemInterface = ({
const renderBreadcrumbNode = (breadcrumbItem: React.ReactNode) => {
if (overlay) {
return (
<DropDown overlay={overlay} placement="bottomCenter" {...dropdownProps}>
<DropDown overlay={overlay} placement="bottom" {...dropdownProps}>
<span className={`${prefixCls}-overlay-link`}>
{breadcrumbItem}
<DownOutlined />
@ -62,9 +62,7 @@ const BreadcrumbItem: BreadcrumbItemInterface = ({
return (
<span>
{link}
{separator && (
<span className={`${prefixCls}-separator`}>{separator}</span>
)}
{separator && <span className={`${prefixCls}-separator`}>{separator}</span>}
</span>
);
}

View File

@ -1674,6 +1674,241 @@ exports[`renders ./components/cascader/demo/multiple.md extend context correctly
</div>
`;
exports[`renders ./components/cascader/demo/placement.md extend context correctly 1`] = `
Array [
<div
class="ant-radio-group ant-radio-group-outline"
>
<label
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"
>
<span
class="ant-radio-button ant-radio-button-checked"
>
<input
checked=""
class="ant-radio-button-input"
type="radio"
value="topLeft"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
topLeft
</span>
</label>
<label
class="ant-radio-button-wrapper"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input"
type="radio"
value="topRight"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
topRight
</span>
</label>
<label
class="ant-radio-button-wrapper"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input"
type="radio"
value="bottomLeft"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
bottomLeft
</span>
</label>
<label
class="ant-radio-button-wrapper"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input"
type="radio"
value="bottomRight"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
bottomRight
</span>
</label>
</div>,
<br />,
<br />,
<div
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="undefined_list"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
>
Please select
</span>
</div>
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
>
<div>
<div
class="ant-cascader-menus"
>
<ul
class="ant-cascader-menu"
role="menu"
>
<li
aria-checked="false"
class="ant-cascader-menu-item ant-cascader-menu-item-expand"
data-path-key="zhejiang"
role="menuitemcheckbox"
title="Zhejiang"
>
<div
class="ant-cascader-menu-item-content"
>
Zhejiang
</div>
<div
class="ant-cascader-menu-item-expand-icon"
>
<span
aria-label="right"
class="anticon anticon-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
/>
</svg>
</span>
</div>
</li>
<li
aria-checked="false"
class="ant-cascader-menu-item ant-cascader-menu-item-expand"
data-path-key="jiangsu"
role="menuitemcheckbox"
title="Jiangsu"
>
<div
class="ant-cascader-menu-item-content"
>
Jiangsu
</div>
<div
class="ant-cascader-menu-item-expand-icon"
>
<span
aria-label="right"
class="anticon anticon-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
/>
</svg>
</span>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>,
]
`;
exports[`renders ./components/cascader/demo/search.md extend context correctly 1`] = `
<div
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search"
@ -2267,6 +2502,276 @@ Array [
]
`;
exports[`renders ./components/cascader/demo/status.md extend context correctly 1`] = `
<div
class="ant-space ant-space-vertical"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-select ant-cascader ant-select-status-error ant-select-single ant-select-allow-clear ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="undefined_list"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
>
Error
</span>
</div>
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
>
<div>
<div
class="ant-cascader-menus ant-cascader-menu-empty"
>
<ul
class="ant-cascader-menu"
role="menu"
>
<li
aria-checked="false"
class="ant-cascader-menu-item ant-cascader-menu-item-disabled"
data-path-key="__EMPTY__"
role="menuitemcheckbox"
>
<div
class="ant-cascader-menu-item-content"
>
<div
class="ant-empty ant-empty-normal ant-empty-small"
>
<div
class="ant-empty-image"
>
<svg
class="ant-empty-img-simple"
height="41"
viewBox="0 0 64 41"
width="64"
xmlns="http://www.w3.org/2000/svg"
>
<g
fill="none"
fill-rule="evenodd"
transform="translate(0 1)"
>
<ellipse
class="ant-empty-img-simple-ellipse"
cx="32"
cy="33"
rx="32"
ry="7"
/>
<g
class="ant-empty-img-simple-g"
fill-rule="nonzero"
>
<path
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
/>
<path
class="ant-empty-img-simple-path"
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
/>
</g>
</g>
</svg>
</div>
<div
class="ant-empty-description"
>
No Data
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-select ant-cascader ant-select-status-warning ant-select-multiple ant-select-allow-clear"
>
<div
class="ant-select-selector"
>
<div
class="ant-select-selection-overflow"
>
<div
class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix"
style="opacity:1"
>
<div
class="ant-select-selection-search"
style="width:0"
>
<input
aria-autocomplete="list"
aria-controls="undefined_list"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
<span
aria-hidden="true"
class="ant-select-selection-search-mirror"
>
 
</span>
</div>
</div>
</div>
<span
class="ant-select-selection-placeholder"
>
Warning multiple
</span>
</div>
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
>
<div>
<div
class="ant-cascader-menus ant-cascader-menu-empty"
>
<ul
class="ant-cascader-menu"
role="menu"
>
<li
aria-checked="false"
class="ant-cascader-menu-item ant-cascader-menu-item-disabled"
data-path-key="__EMPTY__"
role="menuitemcheckbox"
>
<div
class="ant-cascader-menu-item-content"
>
<div
class="ant-empty ant-empty-normal ant-empty-small"
>
<div
class="ant-empty-image"
>
<svg
class="ant-empty-img-simple"
height="41"
viewBox="0 0 64 41"
width="64"
xmlns="http://www.w3.org/2000/svg"
>
<g
fill="none"
fill-rule="evenodd"
transform="translate(0 1)"
>
<ellipse
class="ant-empty-img-simple-ellipse"
cx="32"
cy="33"
rx="32"
ry="7"
/>
<g
class="ant-empty-img-simple-g"
fill-rule="nonzero"
>
<path
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
/>
<path
class="ant-empty-img-simple-path"
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
/>
</g>
</g>
</svg>
</div>
<div
class="ant-empty-description"
>
No Data
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/cascader/demo/suffix.md extend context correctly 1`] = `
Array [
<div

View File

@ -658,6 +658,151 @@ exports[`renders ./components/cascader/demo/multiple.md correctly 1`] = `
</div>
`;
exports[`renders ./components/cascader/demo/placement.md correctly 1`] = `
Array [
<div
class="ant-radio-group ant-radio-group-outline"
>
<label
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"
>
<span
class="ant-radio-button ant-radio-button-checked"
>
<input
checked=""
class="ant-radio-button-input"
type="radio"
value="topLeft"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
topLeft
</span>
</label>
<label
class="ant-radio-button-wrapper"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input"
type="radio"
value="topRight"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
topRight
</span>
</label>
<label
class="ant-radio-button-wrapper"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input"
type="radio"
value="bottomLeft"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
bottomLeft
</span>
</label>
<label
class="ant-radio-button-wrapper"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input"
type="radio"
value="bottomRight"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
bottomRight
</span>
</label>
</div>,
<br />,
<br />,
<div
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="undefined_list"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
>
Please select
</span>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>,
]
`;
exports[`renders ./components/cascader/demo/search.md correctly 1`] = `
<div
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search"
@ -891,6 +1036,126 @@ Array [
]
`;
exports[`renders ./components/cascader/demo/status.md correctly 1`] = `
<div
class="ant-space ant-space-vertical"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-select ant-cascader ant-select-status-error ant-select-single ant-select-allow-clear ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="undefined_list"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
>
Error
</span>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-select ant-cascader ant-select-status-warning ant-select-multiple ant-select-allow-clear"
>
<div
class="ant-select-selector"
>
<div
class="ant-select-selection-overflow"
>
<div
class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix"
style="opacity:1"
>
<div
class="ant-select-selection-search"
style="width:0"
>
<input
aria-autocomplete="list"
aria-controls="undefined_list"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
<span
aria-hidden="true"
class="ant-select-selection-search-mirror"
>
 
</span>
</div>
</div>
</div>
<span
class="ant-select-selection-placeholder"
>
Warning multiple
</span>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/cascader/demo/suffix.md correctly 1`] = `
Array [
<div

View File

@ -368,6 +368,23 @@ describe('Cascader', () => {
expect(wrapper.find('.ant-select-selection-placeholder').text()).toEqual(customPlaceholder);
});
it('placement work correctly', () => {
const customerOptions = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
},
],
},
];
const wrapper = mount(<Cascader options={customerOptions} placement="topRight" />);
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('topRight');
});
it('popup correctly with defaultValue RTL', () => {
const wrapper = mount(
<ConfigProvider direction="rtl">
@ -484,10 +501,12 @@ describe('Cascader', () => {
describe('legacy props', () => {
it('popupClassName', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const wrapper = mount(<Cascader open popupPlacement="topRight" popupClassName="mock-cls" />);
const wrapper = mount(
<Cascader open popupPlacement="bottomLeft" popupClassName="mock-cls" />,
);
expect(wrapper.exists('.mock-cls')).toBeTruthy();
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('topRight');
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('bottomLeft');
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Cascader] `popupClassName` is deprecated. Please use `dropdownClassName` instead.',

View File

@ -0,0 +1,77 @@
---
order: 13
title:
zh-CN: 弹出位置
en-US: Placement
---
## zh-CN
可以通过 `placement` 手动指定弹出的位置。
## en-US
You can manually specify the position of the popup via `placement`.
```jsx
import { Cascader, Radio } from 'antd';
const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
];
const SetPlacementDemo = () => {
const [placement, SetPlacement] = React.useState('topLeft');
const placementChange = e => {
SetPlacement(e.target.value);
};
return (
<>
<Radio.Group value={placement} onChange={placementChange}>
<Radio.Button value="topLeft">topLeft</Radio.Button>
<Radio.Button value="topRight">topRight</Radio.Button>
<Radio.Button value="bottomLeft">bottomLeft</Radio.Button>
<Radio.Button value="bottomRight">bottomRight</Radio.Button>
</Radio.Group>
<br />
<br />
<Cascader options={options} placeholder="Please select" placement={placement} />
</>
);
};
ReactDOM.render(<SetPlacementDemo />, mountNode);
```

View File

@ -0,0 +1,28 @@
---
order: 16
version: 4.19.0
title:
zh-CN: 自定义状态
en-US: Status
---
## zh-CN
使用 `status` 为 Cascader 添加状态,可选 `error` 或者 `warning`
## en-US
Add status to Cascader with `status`, which could be `error` or `warning`.
```tsx
import { Cascader, Space } from 'antd';
const Validation: React.FC = () => (
<Space direction="vertical">
<Cascader status="error" placeholder="Error" />
<Cascader status="warning" multiple placeholder="Warning multiple" />
</Space>
);
ReactDOM.render(<Validation />, mountNode);
```

View File

@ -42,9 +42,10 @@ Cascade selection box.
| open | Set visible of cascader popup | boolean | - | 4.17.0 |
| options | The data options of cascade | [Option](#Option)\[] | - | |
| placeholder | The input placeholder | string | `Please select` | |
| placement | Use preset popup align config from builtinPlacements`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` | 4.17.0 |
| placement | Use preset popup align config from builtinPlacements | `bottomLeft` `bottomRight` `topLeft` `topRight` | `bottomLeft` | 4.17.0 |
| showSearch | Whether show search input in single mode | boolean \| [Object](#showSearch) | false | |
| size | The input size | `large` \| `middle` \| `small` | - | |
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| style | The additional style | CSSProperties | - | |
| suffixIcon | The custom suffix icon | ReactNode | - | |
| tagRender | Customize tag render when `multiple` | (props) => ReactNode | - | 4.17.0 |

View File

@ -13,12 +13,15 @@ import omit from 'rc-util/lib/omit';
import RightOutlined from '@ant-design/icons/RightOutlined';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import LeftOutlined from '@ant-design/icons/LeftOutlined';
import { useContext } from 'react';
import devWarning from '../_util/devWarning';
import { ConfigContext } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext';
import getIcons from '../select/utils/iconUtil';
import { getTransitionName } from '../_util/motion';
import { getTransitionName, getTransitionDirection, SelectCommonPlacement } from '../_util/motion';
import { FormItemStatusContext } from '../form/context';
import { getMergedStatus, getStatusClassNames, InputStatus } from '../_util/statusUtils';
// Align the design since we use `rc-select` in root. This help:
// - List search content will show all content
@ -94,10 +97,11 @@ export type CascaderProps<DataNodeType> = UnionCascaderProps & {
multiple?: boolean;
size?: SizeType;
bordered?: boolean;
placement?: SelectCommonPlacement;
suffixIcon?: React.ReactNode;
options?: DataNodeType[];
}
status?: InputStatus;
};
export interface CascaderRef {
focus: () => void;
@ -116,11 +120,14 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
popupClassName,
dropdownClassName,
expandIcon,
placement,
showSearch,
allowClear = true,
notFoundContent,
direction,
getPopupContainer,
status: customStatus,
showArrow,
...rest
} = props;
@ -133,11 +140,15 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
direction: rootDirection,
// virtual,
// dropdownMatchSelectWidth,
} = React.useContext(ConfigContext);
} = useContext(ConfigContext);
const mergedDirection = direction || rootDirection;
const isRtl = mergedDirection === 'rtl';
// =================== Status =====================
const { status: contextStatus, hasFeedback } = useContext(FormItemStatusContext);
const mergedStatus = getMergedStatus(contextStatus, customStatus);
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
devWarning(
@ -213,12 +224,26 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
);
// ===================== Icons =====================
const mergedShowArrow = showArrow !== undefined ? showArrow : props.loading || !multiple;
const { suffixIcon, removeIcon, clearIcon } = getIcons({
...props,
status: mergedStatus,
hasFeedback,
showArrow: mergedShowArrow,
multiple,
prefixCls,
});
// ===================== Placement =====================
const getPlacement = () => {
if (placement !== undefined) {
return placement;
}
return direction === 'rtl'
? ('bottomRight' as SelectCommonPlacement)
: ('bottomLeft' as SelectCommonPlacement);
};
// ==================== Render =====================
return (
<RcCascader
@ -231,10 +256,12 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
[`${prefixCls}-rtl`]: isRtl,
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(prefixCls, mergedStatus, hasFeedback),
className,
)}
{...(restProps as any)}
direction={mergedDirection}
placement={getPlacement()}
notFoundContent={mergedNotFoundContent}
allowClear={allowClear}
showSearch={mergedShowSearch}
@ -247,9 +274,14 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
dropdownClassName={mergedDropdownClassName}
dropdownPrefixCls={customizePrefixCls || cascaderPrefixCls}
choiceTransitionName={getTransitionName(rootPrefixCls, '', choiceTransitionName)}
transitionName={getTransitionName(rootPrefixCls, 'slide-up', transitionName)}
transitionName={getTransitionName(
rootPrefixCls,
getTransitionDirection(placement),
transitionName,
)}
getPopupContainer={getPopupContainer || getContextPopupContainer}
ref={ref}
showArrow={hasFeedback || showArrow}
/>
);
}) as (<OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType>(

View File

@ -43,9 +43,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
| open | 控制浮层显隐 | boolean | - | 4.17.0 |
| options | 可选项数据源 | [Option](#Option)\[] | - | |
| placeholder | 输入框占位文本 | string | `请选择` | |
| placement | 浮层预设位置`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` | 4.17.0 |
| placement | 浮层预设位置 | `bottomLeft` `bottomRight` `topLeft` `topRight` | `bottomLeft` | 4.17.0 |
| showSearch | 在选择框中显示搜索框 | boolean \| [Object](#showSearch) | false | |
| size | 输入框大小 | `large` \| `middle` \| `small` | - | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| style | 自定义样式 | CSSProperties | - | |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| tagRender | 自定义 tag 内容,多选时生效 | (props) => ReactNode | - | 4.17.0 |

View File

@ -4,3 +4,5 @@ import './index.less';
// style dependencies
import '../../empty/style';
import '../../select/style';
// deps-lint-skip: form

View File

@ -13367,7 +13367,7 @@ exports[`ConfigProvider components Form configProvider 1`] = `
class="config-form-item-control-input-content"
>
<input
class="config-input"
class="config-input config-input-status-error"
type="text"
value=""
/>
@ -13405,7 +13405,7 @@ exports[`ConfigProvider components Form configProvider componentSize large 1`] =
class="config-form-item-control-input-content"
>
<input
class="config-input config-input-lg"
class="config-input config-input-lg config-input-status-error"
type="text"
value=""
/>
@ -13443,7 +13443,7 @@ exports[`ConfigProvider components Form configProvider componentSize middle 1`]
class="config-form-item-control-input-content"
>
<input
class="config-input"
class="config-input config-input-status-error"
type="text"
value=""
/>
@ -13481,7 +13481,7 @@ exports[`ConfigProvider components Form configProvider virtual and dropdownMatch
class="ant-form-item-control-input-content"
>
<input
class="ant-input"
class="ant-input ant-input-status-error"
type="text"
value=""
/>
@ -13519,7 +13519,7 @@ exports[`ConfigProvider components Form normal 1`] = `
class="ant-form-item-control-input-content"
>
<input
class="ant-input"
class="ant-input ant-input-status-error"
type="text"
value=""
/>
@ -13557,7 +13557,7 @@ exports[`ConfigProvider components Form prefixCls 1`] = `
class="prefix-Form-item-control-input-content"
>
<input
class="prefix-Form"
class="prefix-Form prefix-Form-status-error"
type="text"
value=""
/>
@ -13744,7 +13744,7 @@ exports[`ConfigProvider components Input configProvider componentSize large 1`]
value=""
/>
<span
class="config-input-group-wrapper config-input-group-wrapper-lg config-input-search config-input-search-large"
class="config-input-group-wrapper config-input-search config-input-search-large config-input-group-wrapper-lg"
>
<span
class="config-input-wrapper config-input-group"
@ -13786,11 +13786,11 @@ exports[`ConfigProvider components Input configProvider componentSize large 1`]
</span>
</span>
<span
class="config-input-affix-wrapper config-input-affix-wrapper-lg config-input-password"
class="config-input-affix-wrapper config-input-password config-input-affix-wrapper-lg"
>
<input
action="click"
class="config-input config-input-lg"
class="config-input"
type="password"
value=""
/>
@ -21904,14 +21904,14 @@ exports[`ConfigProvider components Skeleton prefixCls 1`] = `
exports[`ConfigProvider components Slider configProvider 1`] = `
<div
class="config-slider"
class="config-slider config-slider-horizontal"
>
<div
class="config-slider-rail"
/>
<div
class="config-slider-track"
style="left:0%;right:auto;width:0%"
style="left:0%;width:0%"
/>
<div
class="config-slider-step"
@ -21923,7 +21923,7 @@ exports[`ConfigProvider components Slider configProvider 1`] = `
aria-valuenow="0"
class="config-slider-handle config-tooltip-open"
role="slider"
style="left:0%;right:auto;transform:translateX(-50%)"
style="left:0%;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -21950,22 +21950,19 @@ exports[`ConfigProvider components Slider configProvider 1`] = `
</div>
</div>
</div>
<div
class="config-slider-mark"
/>
</div>
`;
exports[`ConfigProvider components Slider configProvider componentSize large 1`] = `
<div
class="config-slider"
class="config-slider config-slider-horizontal"
>
<div
class="config-slider-rail"
/>
<div
class="config-slider-track"
style="left:0%;right:auto;width:0%"
style="left:0%;width:0%"
/>
<div
class="config-slider-step"
@ -21977,7 +21974,7 @@ exports[`ConfigProvider components Slider configProvider componentSize large 1`]
aria-valuenow="0"
class="config-slider-handle config-tooltip-open"
role="slider"
style="left:0%;right:auto;transform:translateX(-50%)"
style="left:0%;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -22004,22 +22001,19 @@ exports[`ConfigProvider components Slider configProvider componentSize large 1`]
</div>
</div>
</div>
<div
class="config-slider-mark"
/>
</div>
`;
exports[`ConfigProvider components Slider configProvider componentSize middle 1`] = `
<div
class="config-slider"
class="config-slider config-slider-horizontal"
>
<div
class="config-slider-rail"
/>
<div
class="config-slider-track"
style="left:0%;right:auto;width:0%"
style="left:0%;width:0%"
/>
<div
class="config-slider-step"
@ -22031,7 +22025,7 @@ exports[`ConfigProvider components Slider configProvider componentSize middle 1`
aria-valuenow="0"
class="config-slider-handle config-tooltip-open"
role="slider"
style="left:0%;right:auto;transform:translateX(-50%)"
style="left:0%;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -22058,22 +22052,19 @@ exports[`ConfigProvider components Slider configProvider componentSize middle 1`
</div>
</div>
</div>
<div
class="config-slider-mark"
/>
</div>
`;
exports[`ConfigProvider components Slider configProvider virtual and dropdownMatchSelectWidth 1`] = `
<div
class="ant-slider"
class="ant-slider ant-slider-horizontal"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left:0%;right:auto;width:0%"
style="left:0%;width:0%"
/>
<div
class="ant-slider-step"
@ -22085,7 +22076,7 @@ exports[`ConfigProvider components Slider configProvider virtual and dropdownMat
aria-valuenow="0"
class="ant-slider-handle ant-tooltip-open"
role="slider"
style="left:0%;right:auto;transform:translateX(-50%)"
style="left:0%;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -22112,22 +22103,19 @@ exports[`ConfigProvider components Slider configProvider virtual and dropdownMat
</div>
</div>
</div>
<div
class="ant-slider-mark"
/>
</div>
`;
exports[`ConfigProvider components Slider normal 1`] = `
<div
class="ant-slider"
class="ant-slider ant-slider-horizontal"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left:0%;right:auto;width:0%"
style="left:0%;width:0%"
/>
<div
class="ant-slider-step"
@ -22139,7 +22127,7 @@ exports[`ConfigProvider components Slider normal 1`] = `
aria-valuenow="0"
class="ant-slider-handle ant-tooltip-open"
role="slider"
style="left:0%;right:auto;transform:translateX(-50%)"
style="left:0%;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -22166,22 +22154,19 @@ exports[`ConfigProvider components Slider normal 1`] = `
</div>
</div>
</div>
<div
class="ant-slider-mark"
/>
</div>
`;
exports[`ConfigProvider components Slider prefixCls 1`] = `
<div
class="prefix-Slider"
class="prefix-Slider prefix-Slider-horizontal"
>
<div
class="prefix-Slider-rail"
/>
<div
class="prefix-Slider-track"
style="left:0%;right:auto;width:0%"
style="left:0%;width:0%"
/>
<div
class="prefix-Slider-step"
@ -22193,7 +22178,7 @@ exports[`ConfigProvider components Slider prefixCls 1`] = `
aria-valuenow="0"
class="prefix-Slider-handle prefix-Slider-tooltip-open"
role="slider"
style="left:0%;right:auto;transform:translateX(-50%)"
style="left:0%;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -22220,9 +22205,6 @@ exports[`ConfigProvider components Slider prefixCls 1`] = `
</div>
</div>
</div>
<div
class="prefix-Slider-mark"
/>
</div>
`;

View File

@ -199,4 +199,36 @@ describe('DatePicker', () => {
expect(year).toBe(startDate.format('YYYY'));
expect(wrapper.find('.ant-picker-time-panel').length).toBe(1);
});
it('placement api work correctly ', () => {
const popupAlignDefault = (points = ['tl', 'bl'], offset = [0, 4]) => ({
points,
offset,
overflow: {
adjustX: 1,
adjustY: 1,
},
});
const wrapper = mount(
<DatePicker.RangePicker defaultValue={moment()} placement="bottomLeft" />,
);
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(popupAlignDefault(['tl', 'bl']));
wrapper.setProps({
placement: 'bottomRight',
});
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(popupAlignDefault(['tr', 'br']));
wrapper.setProps({
placement: 'topLeft',
});
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(
popupAlignDefault(['bl', 'tl'], [0, -4]),
);
wrapper.setProps({
placement: 'topRight',
});
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(
popupAlignDefault(['br', 'tr'], [0, -4]),
);
});
});

View File

@ -5,6 +5,7 @@ import DatePicker from '..';
import { setMockDate, resetMockDate } from '../../../tests/utils';
import { openPicker, selectCell, closePicker } from './utils';
import focusTest from '../../../tests/shared/focusTest';
import enUS from '../locale/en_US';
const { RangePicker } = DatePicker;
@ -96,4 +97,10 @@ describe('RangePicker', () => {
expect(wrapper.find('input').first().props().placeholder).toEqual('Start date');
expect(wrapper.find('input').last().props().placeholder).toEqual('End date');
});
it('RangePicker picker quarter placeholder', () => {
const wrapper = mount(<RangePicker picker="quarter" locale={enUS} />);
expect(wrapper.find('input').at(0).props().placeholder).toEqual('Start quarter');
expect(wrapper.find('input').at(1).props().placeholder).toEqual('End quarter');
});
});

View File

@ -2349,6 +2349,216 @@ exports[`renders ./components/date-picker/demo/mode.md correctly 1`] = `
</div>
`;
exports[`renders ./components/date-picker/demo/placement.md correctly 1`] = `
Array [
<div
class="ant-radio-group ant-radio-group-outline"
>
<label
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"
>
<span
class="ant-radio-button ant-radio-button-checked"
>
<input
checked=""
class="ant-radio-button-input"
type="radio"
value="topLeft"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
topLeft
</span>
</label>
<label
class="ant-radio-button-wrapper"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input"
type="radio"
value="topRight"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
topRight
</span>
</label>
<label
class="ant-radio-button-wrapper"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input"
type="radio"
value="bottomLeft"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
bottomLeft
</span>
</label>
<label
class="ant-radio-button-wrapper"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input"
type="radio"
value="bottomRight"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
bottomRight
</span>
</label>
</div>,
<br />,
<br />,
<div
class="ant-picker"
>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
placeholder="Select date"
readonly=""
size="12"
title=""
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>,
<br />,
<br />,
<div
class="ant-picker ant-picker-range"
>
<div
class="ant-picker-input ant-picker-input-active"
>
<input
autocomplete="off"
placeholder="Start date"
readonly=""
size="12"
value=""
/>
</div>
<div
class="ant-picker-range-separator"
>
<span
aria-label="to"
class="ant-picker-separator"
>
<span
aria-label="swap-right"
class="anticon anticon-swap-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="swap-right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M873.1 596.2l-164-208A32 32 0 00684 376h-64.8c-6.7 0-10.4 7.7-6.3 13l144.3 183H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h695.9c26.8 0 41.7-30.8 25.2-51.8z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
placeholder="End date"
readonly=""
size="12"
value=""
/>
</div>
<div
class="ant-picker-active-bar"
style="left:0;width:0;position:absolute"
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>,
]
`;
exports[`renders ./components/date-picker/demo/presetted-ranges.md correctly 1`] = `
<div
class="ant-space ant-space-vertical"
@ -2875,6 +3085,92 @@ exports[`renders ./components/date-picker/demo/range-picker.md correctly 1`] = `
</span>
</div>
</div>
<div
class="ant-space-item"
style="margin-bottom:12px"
>
<div
class="ant-picker ant-picker-range"
>
<div
class="ant-picker-input ant-picker-input-active"
>
<input
autocomplete="off"
placeholder="Start quarter"
readonly=""
size="12"
value=""
/>
</div>
<div
class="ant-picker-range-separator"
>
<span
aria-label="to"
class="ant-picker-separator"
>
<span
aria-label="swap-right"
class="anticon anticon-swap-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="swap-right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M873.1 596.2l-164-208A32 32 0 00684 376h-64.8c-6.7 0-10.4 7.7-6.3 13l144.3 183H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h695.9c26.8 0 41.7-30.8 25.2-51.8z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
placeholder="End quarter"
readonly=""
size="12"
value=""
/>
</div>
<div
class="ant-picker-active-bar"
style="left:0;width:0;position:absolute"
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
<div
class="ant-space-item"
>
@ -3432,6 +3728,103 @@ exports[`renders ./components/date-picker/demo/start-end.md correctly 1`] = `
</div>
`;
exports[`renders ./components/date-picker/demo/status.md correctly 1`] = `
<div
class="ant-space ant-space-vertical"
style="width:100%"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-picker ant-picker-status-error"
style="width:100%"
>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
placeholder="Select date"
readonly=""
size="12"
title=""
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-picker ant-picker-status-warning"
style="width:100%"
>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
placeholder="Select date"
readonly=""
size="12"
title=""
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/date-picker/demo/suffix.md correctly 1`] = `
<div
class="ant-space ant-space-vertical"

View File

@ -0,0 +1,47 @@
---
order: 28
title:
zh-CN: 基本
en-US: Basic
---
## zh-CN
可以通过 `placement` 手动指定弹出的位置。
## en-US
You can manually specify the position of the popup via `placement`.
```jsx
import { DatePicker, Space, Radio } from 'antd';
const { RangePicker } = DatePicker;
const SetPlacementDemo = () => {
const [placement, SetPlacement] = React.useState('topLeft');
const placementChange = e => {
SetPlacement(e.target.value);
};
return (
<>
<Radio.Group value={placement} onChange={placementChange}>
<Radio.Button value="topLeft">topLeft</Radio.Button>
<Radio.Button value="topRight">topRight</Radio.Button>
<Radio.Button value="bottomLeft">bottomLeft</Radio.Button>
<Radio.Button value="bottomRight">bottomRight</Radio.Button>
</Radio.Group>
<br />
<br />
<DatePicker placement={placement} />
<br />
<br />
<RangePicker placement={placement} />
</>
);
};
ReactDOM.render(<SetPlacementDemo />, mountNode);
```

View File

@ -24,6 +24,7 @@ ReactDOM.render(
<RangePicker showTime />
<RangePicker picker="week" />
<RangePicker picker="month" />
<RangePicker picker="quarter" />
<RangePicker picker="year" />
</Space>,
mountNode,

View File

@ -0,0 +1,28 @@
---
order: 19
version: 4.19.0
title:
zh-CN: 自定义状态
en-US: Status
---
## zh-CN
使用 `status` 为 DatePicker 添加状态,可选 `error` 或者 `warning`
## en-US
Add status to DatePicker with `status`, which could be `error` or `warning`.
```tsx
import { DatePicker, Space } from 'antd';
const Status: React.FC = () => (
<Space direction="vertical" style={{ width: '100%' }}>
<DatePicker status="error" style={{ width: '100%' }} />
<DatePicker status="warning" style={{ width: '100%' }} />
</Space>
);
ReactDOM.render(<Status />, mountNode);
```

View File

@ -10,7 +10,7 @@ import enUS from '../locale/en_US';
import { ConfigContext, ConfigConsumerProps } from '../../config-provider';
import SizeContext from '../../config-provider/SizeContext';
import LocaleReceiver from '../../locale-provider/LocaleReceiver';
import { getRangePlaceholder } from '../util';
import { getRangePlaceholder, transPlacement2DropdownAlign } from '../util';
import { RangePickerProps, PickerLocale, getTimeProps, Components } from '.';
import { PickerComponentClass } from './interface';
@ -43,6 +43,7 @@ export default function generateRangePicker<DateType>(
prefixCls: customizePrefixCls,
getPopupContainer: customGetPopupContainer,
className,
placement,
size: customizeSize,
bordered = true,
placeholder,
@ -73,6 +74,7 @@ export default function generateRangePicker<DateType>(
</span>
}
ref={this.pickerRef}
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
placeholder={getRangePlaceholder(picker, locale, placeholder)}
suffixIcon={picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
clearIcon={<CloseCircleFilled />}

View File

@ -7,7 +7,7 @@ import RCPicker from 'rc-picker';
import { PickerMode } from 'rc-picker/lib/interface';
import { GenerateConfig } from 'rc-picker/lib/generate/index';
import enUS from '../locale/en_US';
import { getPlaceholder } from '../util';
import { getPlaceholder, transPlacement2DropdownAlign } from '../util';
import devWarning from '../../_util/devWarning';
import { ConfigContext, ConfigConsumerProps } from '../../config-provider';
import LocaleReceiver from '../../locale-provider/LocaleReceiver';
@ -21,9 +21,18 @@ import {
Components,
} from '.';
import { PickerComponentClass } from './interface';
import { FormItemStatusContext } from '../../form/context';
import {
getFeedbackIcon,
getMergedStatus,
getStatusClassNames,
InputStatus,
} from '../../_util/statusUtils';
export default function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
type DatePickerProps = PickerProps<DateType>;
type DatePickerProps = PickerProps<DateType> & {
status?: InputStatus;
};
function getPicker<InnerPickerProps extends DatePickerProps>(
picker?: PickerMode,
@ -59,6 +68,23 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
}
};
renderFeedback = (prefixCls: string) => (
<FormItemStatusContext.Consumer>
{({ hasFeedback, status: contextStatus }) => {
const { status: customStatus } = this.props;
const status = getMergedStatus(contextStatus, customStatus);
return hasFeedback && getFeedbackIcon(prefixCls, status);
}}
</FormItemStatusContext.Consumer>
);
renderSuffix = (prefixCls: string, mergedPicker?: PickerMode) => (
<>
{mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
{this.renderFeedback(prefixCls)}
</>
);
renderPicker = (contextLocale: PickerLocale) => {
const locale = { ...contextLocale, ...this.props.locale };
const { getPrefixCls, direction, getPopupContainer } = this.context;
@ -68,7 +94,9 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
className,
size: customizeSize,
bordered = true,
placement,
placeholder,
status: customStatus,
...restProps
} = this.props;
const { format, showTime } = this.props as any;
@ -99,36 +127,44 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
const mergedSize = customizeSize || size;
return (
<RCPicker<DateType>
ref={this.pickerRef}
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
suffixIcon={
mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />
}
clearIcon={<CloseCircleFilled />}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
allowClear
transitionName={`${rootPrefixCls}-slide-up`}
{...additionalProps}
{...restProps}
{...additionalOverrideProps}
locale={locale!.lang}
className={classNames(
{
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-borderless`]: !bordered,
},
className,
<FormItemStatusContext.Consumer>
{({ hasFeedback, status: contextStatus }) => (
<RCPicker<DateType>
ref={this.pickerRef}
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
suffixIcon={this.renderSuffix(prefixCls, mergedPicker)}
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
clearIcon={<CloseCircleFilled />}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
allowClear
transitionName={`${rootPrefixCls}-slide-up`}
{...additionalProps}
{...restProps}
{...additionalOverrideProps}
locale={locale!.lang}
className={classNames(
{
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(
prefixCls,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
className,
)}
prefixCls={prefixCls}
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
/>
)}
prefixCls={prefixCls}
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
/>
</FormItemStatusContext.Consumer>
);
}}
</SizeContext.Consumer>

View File

@ -17,6 +17,7 @@ import PickerTag from '../PickerTag';
import { TimePickerLocale } from '../../time-picker';
import generateSinglePicker from './generateSinglePicker';
import generateRangePicker from './generateRangePicker';
import { tuple } from '../../_util/type';
export const Components = { button: PickerButton, rangeItem: PickerTag };
@ -27,13 +28,18 @@ function toArray<T>(list: T | T[]): T[] {
return Array.isArray(list) ? list : [list];
}
export function getTimeProps<DateType>(
props: { format?: string; picker?: PickerMode } & SharedTimeProps<DateType>,
export function getTimeProps<DateType, DisabledTime>(
props: { format?: string; picker?: PickerMode } & Omit<
SharedTimeProps<DateType>,
'disabledTime'
> & {
disabledTime?: DisabledTime;
},
) {
const { format, picker, showHour, showMinute, showSecond, use12Hours } = props;
const firstFormat = toArray(format)[0];
const showTimeObj: SharedTimeProps<DateType> = { ...props };
const showTimeObj = { ...props };
if (firstFormat && typeof firstFormat === 'string') {
if (!firstFormat.includes('s') && showSecond === undefined) {
@ -64,16 +70,16 @@ export function getTimeProps<DateType>(
showTime: showTimeObj,
};
}
const DataPickerPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight');
type DataPickerPlacement = typeof DataPickerPlacements[number];
type InjectDefaultProps<Props> = Omit<
Props,
| 'locale'
| 'generateConfig'
| 'hideHeader'
| 'components'
'locale' | 'generateConfig' | 'hideHeader' | 'components'
> & {
locale?: PickerLocale;
size?: SizeType;
placement?: DataPickerPlacement;
bordered?: boolean;
};
@ -96,6 +102,7 @@ export type AdditionalPickerLocaleLangProps = {
monthPlaceholder?: string;
weekPlaceholder?: string;
rangeYearPlaceholder?: [string, string];
rangeQuarterPlaceholder?: [string, string];
rangeMonthPlaceholder?: [string, string];
rangeWeekPlaceholder?: [string, string];
rangePlaceholder?: [string, string];

View File

@ -69,9 +69,11 @@ The following APIs are shared by DatePicker, RangePicker.
| panelRender | Customize panel render | (panelNode) => ReactNode | - | 4.5.0 |
| picker | Set picker type | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
| placeholder | The placeholder of date input | string \| \[string,string] | - | |
| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
| popupStyle | To customize the style of the popup calendar | CSSProperties | {} | |
| prevIcon | The custom prev icon | ReactNode | - | 4.17.0 |
| size | To determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | `large` \| `middle` \| `small` | - | |
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| style | To customize the style of the input box | CSSProperties | {} | |
| suffixIcon | The custom suffix icon | ReactNode | - | |
| superNextIcon | The custom super next icon | ReactNode | - | 4.17.0 |

View File

@ -70,9 +70,11 @@ import locale from 'antd/lib/locale/zh_CN';
| panelRender | 自定义渲染面板 | (panelNode) => ReactNode | - | 4.5.0 |
| picker | 设置选择器类型 | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
| placeholder | 输入框提示文字 | string \| \[string, string] | - | |
| placement | 选择框弹出的位置 | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
| popupStyle | 额外的弹出日历样式 | CSSProperties | {} | |
| prevIcon | 自定义上一个图标 | ReactNode | - | 4.17.0 |
| size | 输入框大小,`large` 高度为 40px`small` 为 24px默认是 32px | `large` \| `middle` \| `small` | - | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| style | 自定义输入框样式 | CSSProperties | {} | |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| superNextIcon | 自定义 `<<` 切换图标 | ReactNode | - | 4.17.0 |

View File

@ -12,6 +12,7 @@ const locale: PickerLocale = {
weekPlaceholder: 'Select week',
rangePlaceholder: ['Start date', 'End date'],
rangeYearPlaceholder: ['Start year', 'End year'],
rangeQuarterPlaceholder: ['Start quarter', 'End quarter'],
rangeMonthPlaceholder: ['Start month', 'End month'],
rangeWeekPlaceholder: ['Start week', 'End week'],
...CalendarLocale,

View File

@ -12,6 +12,7 @@ const locale: PickerLocale = {
weekPlaceholder: 'Select week',
rangePlaceholder: ['Start date', 'End date'],
rangeYearPlaceholder: ['Start year', 'End year'],
rangeQuarterPlaceholder: ['Start quarter', 'End quarter'],
rangeMonthPlaceholder: ['Start month', 'End month'],
rangeWeekPlaceholder: ['Start week', 'End week'],
...CalendarLocale,

View File

@ -13,6 +13,7 @@ const locale: PickerLocale = {
rangePlaceholder: ['开始日期', '结束日期'],
rangeYearPlaceholder: ['开始年份', '结束年份'],
rangeMonthPlaceholder: ['开始月份', '结束月份'],
rangeQuarterPlaceholder: ['开始季度', '结束季度'],
rangeWeekPlaceholder: ['开始周', '结束周'],
...CalendarLocale,
},

View File

@ -13,6 +13,7 @@ const locale: PickerLocale = {
rangePlaceholder: ['開始日期', '結束日期'],
rangeYearPlaceholder: ['開始年份', '結束年份'],
rangeMonthPlaceholder: ['開始月份', '結束月份'],
rangeQuarterPlaceholder: ['開始季度', '結束季度'],
rangeWeekPlaceholder: ['開始周', '結束周'],
...CalendarLocale,
},

View File

@ -1,6 +1,7 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@import './status';
@picker-prefix-cls: ~'@{ant-prefix}-picker';
@ -13,7 +14,7 @@
}
.@{picker-prefix-cls} {
@arrow-size: 10px;
@arrow-size: @popover-arrow-width;
.reset-component();
.picker-padding(@input-height-base, @font-size-base, @input-padding-horizontal-base);
@ -106,6 +107,8 @@
}
&-suffix {
display: flex;
flex: none;
align-self: center;
margin-left: (@padding-xs / 2);
color: @disabled-color;
@ -114,6 +117,10 @@
> * {
vertical-align: top;
&:not(:last-child) {
margin-right: 8px;
}
}
}
@ -221,17 +228,17 @@
&-placement-bottomLeft {
.@{picker-prefix-cls}-range-arrow {
top: (@arrow-size / 2) - (@arrow-size / 3);
top: (@arrow-size / 2) - (@arrow-size / 3) + 0.7px;
display: block;
transform: rotate(-45deg);
transform: rotate(-135deg) translateY(1px);
}
}
&-placement-topLeft {
.@{picker-prefix-cls}-range-arrow {
bottom: (@arrow-size / 2) - (@arrow-size / 3);
bottom: (@arrow-size / 2) - (@arrow-size / 3) + 0.7px;
display: block;
transform: rotate(135deg);
transform: rotate(45deg);
}
}
@ -311,19 +318,14 @@
width: @arrow-size;
height: @arrow-size;
margin-left: @input-padding-horizontal-base * 1.5;
box-shadow: 2px -2px 6px fade(@black, 6%);
background: linear-gradient(
135deg,
transparent 40%,
@calendar-bg 40%
); // Use linear-gradient to prevent arrow from covering text
box-shadow: 2px 2px 6px -2px fade(@black, 10%); // use spread radius to hide shadow over popover
transition: left @animation-duration-slow ease-out;
&::after {
position: absolute;
top: @border-width-base;
right: @border-width-base;
width: @arrow-size;
height: @arrow-size;
border: (@arrow-size / 2) solid @border-color-split;
border-color: @calendar-bg @calendar-bg transparent transparent;
content: '';
}
.roundedArrow(@arrow-size, 5px, @calendar-bg);
}
&-panel-container {

View File

@ -3,3 +3,5 @@ import './index.less';
// style dependencies
import '../../tag/style';
import '../../button/style';
// deps-lint-skip: form

View File

@ -0,0 +1,52 @@
@import '../../input/style/mixin';
@picker-prefix-cls: ~'@{ant-prefix}-picker';
.picker-status-color(
@text-color: @input-color;
@border-color: @input-border-color;
@background-color: @input-bg;
@hoverBorderColor: @primary-color-hover;
@outlineColor: @primary-color-outline;
) {
&.@{picker-prefix-cls} {
&,
&:not([disabled]):hover {
background-color: @background-color;
border-color: @border-color;
}
&-focused,
&:focus {
.active(@text-color, @hoverBorderColor, @outlineColor);
}
}
.@{picker-prefix-cls}-feedback-icon {
color: @text-color;
}
}
.@{picker-prefix-cls} {
&-status-error {
.picker-status-color(@error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
}
&-status-warning {
.picker-status-color(@warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
}
&-status-validating {
.@{picker-prefix-cls}-feedback-icon {
display: inline-block;
color: @primary-color;
}
}
&-status-success {
.@{picker-prefix-cls}-feedback-icon {
color: @success-color;
animation-name: diffZoomIn1 !important;
}
}
}

View File

@ -1,4 +1,6 @@
import { PickerMode } from 'rc-picker/lib/interface';
import { DirectionType } from '../config-provider';
import { SelectCommonPlacement } from '../_util/motion';
import { PickerLocale } from './generatePicker';
export function getPlaceholder(
@ -40,6 +42,9 @@ export function getRangePlaceholder(
if (picker === 'year' && locale.lang.yearPlaceholder) {
return locale.lang.rangeYearPlaceholder;
}
if (picker === 'quarter' && locale.lang.quarterPlaceholder) {
return locale.lang.rangeQuarterPlaceholder;
}
if (picker === 'month' && locale.lang.monthPlaceholder) {
return locale.lang.rangeMonthPlaceholder;
}
@ -51,3 +56,56 @@ export function getRangePlaceholder(
}
return locale.lang.rangePlaceholder;
}
export function transPlacement2DropdownAlign(
direction: DirectionType,
placement?: SelectCommonPlacement,
) {
const overflow = {
adjustX: 1,
adjustY: 1,
};
switch (placement) {
case 'bottomLeft': {
return {
points: ['tl', 'bl'],
offset: [0, 4],
overflow,
};
}
case 'bottomRight': {
return {
points: ['tr', 'br'],
offset: [0, 4],
overflow,
};
}
case 'topLeft': {
return {
points: ['bl', 'tl'],
offset: [0, -4],
overflow,
};
}
case 'topRight': {
return {
points: ['br', 'tr'],
offset: [0, -4],
overflow,
};
}
default: {
return direction === 'rtl'
? {
points: ['tr', 'br'],
offset: [0, 4],
overflow,
}
: {
points: ['tl', 'bl'],
offset: [0, 4],
overflow,
};
}
}
}

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
export interface DividerProps {
prefixCls?: string;
@ -14,56 +14,54 @@ export interface DividerProps {
plain?: boolean;
}
const Divider: React.FC<DividerProps> = props => (
<ConfigConsumer>
{({ getPrefixCls, direction }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,
type = 'horizontal',
orientation = 'center',
orientationMargin,
className,
children,
dashed,
plain,
...restProps
} = props;
const prefixCls = getPrefixCls('divider', customizePrefixCls);
const orientationPrefix = orientation.length > 0 ? `-${orientation}` : orientation;
const hasChildren = !!children;
const hasCustomMarginLeft = orientation === 'left' && orientationMargin != null;
const hasCustomMarginRight = orientation === 'right' && orientationMargin != null;
const classString = classNames(
prefixCls,
`${prefixCls}-${type}`,
{
[`${prefixCls}-with-text`]: hasChildren,
[`${prefixCls}-with-text${orientationPrefix}`]: hasChildren,
[`${prefixCls}-dashed`]: !!dashed,
[`${prefixCls}-plain`]: !!plain,
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-no-default-orientation-margin-left`]: hasCustomMarginLeft,
[`${prefixCls}-no-default-orientation-margin-right`]: hasCustomMarginRight,
},
className,
);
const Divider: React.FC<DividerProps> = props => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const innerStyle = {
...(hasCustomMarginLeft && { marginLeft: orientationMargin }),
...(hasCustomMarginRight && { marginRight: orientationMargin }),
};
const {
prefixCls: customizePrefixCls,
type = 'horizontal',
orientation = 'center',
orientationMargin,
className,
children,
dashed,
plain,
...restProps
} = props;
const prefixCls = getPrefixCls('divider', customizePrefixCls);
const orientationPrefix = orientation.length > 0 ? `-${orientation}` : orientation;
const hasChildren = !!children;
const hasCustomMarginLeft = orientation === 'left' && orientationMargin != null;
const hasCustomMarginRight = orientation === 'right' && orientationMargin != null;
const classString = classNames(
prefixCls,
`${prefixCls}-${type}`,
{
[`${prefixCls}-with-text`]: hasChildren,
[`${prefixCls}-with-text${orientationPrefix}`]: hasChildren,
[`${prefixCls}-dashed`]: !!dashed,
[`${prefixCls}-plain`]: !!plain,
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-no-default-orientation-margin-left`]: hasCustomMarginLeft,
[`${prefixCls}-no-default-orientation-margin-right`]: hasCustomMarginRight,
},
className,
);
return (
<div className={classString} {...restProps} role="separator">
{children && (
<span className={`${prefixCls}-inner-text`} style={innerStyle}>
{children}
</span>
)}
</div>
);
}}
</ConfigConsumer>
);
const innerStyle = {
...(hasCustomMarginLeft && { marginLeft: orientationMargin }),
...(hasCustomMarginRight && { marginRight: orientationMargin }),
};
return (
<div className={classString} {...restProps} role="separator">
{children && (
<span className={`${prefixCls}-inner-text`} style={innerStyle}>
{children}
</span>
)}
</div>
);
};
export default Divider;

View File

@ -15,7 +15,7 @@ Array [
type="button"
>
<span>
bottomCenter
bottom
</span>
</button>,
<button
@ -40,7 +40,61 @@ Array [
type="button"
>
<span>
topCenter
top
</span>
</button>,
<button
class="ant-btn ant-btn-default ant-dropdown-trigger"
type="button"
>
<span>
topRight
</span>
</button>,
]
`;
exports[`renders ./components/dropdown/demo/arrow-center.md correctly 1`] = `
Array [
<button
class="ant-btn ant-btn-default ant-dropdown-trigger"
type="button"
>
<span>
bottomLeft
</span>
</button>,
<button
class="ant-btn ant-btn-default ant-dropdown-trigger"
type="button"
>
<span>
bottom
</span>
</button>,
<button
class="ant-btn ant-btn-default ant-dropdown-trigger"
type="button"
>
<span>
bottomRight
</span>
</button>,
<br />,
<button
class="ant-btn ant-btn-default ant-dropdown-trigger"
type="button"
>
<span>
topLeft
</span>
</button>,
<button
class="ant-btn ant-btn-default ant-dropdown-trigger"
type="button"
>
<span>
top
</span>
</button>,
<button
@ -658,7 +712,7 @@ exports[`renders ./components/dropdown/demo/placement.md correctly 1`] = `
type="button"
>
<span>
bottomCenter
bottom
</span>
</button>
</div>
@ -706,7 +760,7 @@ exports[`renders ./components/dropdown/demo/placement.md correctly 1`] = `
type="button"
>
<span>
topCenter
top
</span>
</button>
</div>

View File

@ -59,4 +59,24 @@ describe('Dropdown', () => {
await sleep(500);
expect(wrapper.find(Dropdown).find('#customExpandIcon').length).toBe(1);
});
it('should warn if use topCenter or bottomCenter', () => {
const error = jest.spyOn(console, 'error');
mount(
<div>
<Dropdown overlay="123" placement="bottomCenter">
<button type="button">bottomCenter</button>
</Dropdown>
<Dropdown overlay="123" placement="topCenter">
<button type="button">topCenter</button>
</Dropdown>
</div>,
);
expect(error).toHaveBeenCalledWith(
expect.stringContaining("[antd: Dropdown] You are using 'bottomCenter'"),
);
expect(error).toHaveBeenCalledWith(
expect.stringContaining("[antd: Dropdown] You are using 'topCenter'"),
);
});
});

View File

@ -0,0 +1,75 @@
---
order: 3
title:
zh-CN: 箭头指向
en-US: Arrow pointing at the center
---
## zh-CN
设置 `arrow``{ pointAtCenter: true }` 后,箭头将指向目标元素的中心。
## en-US
By specifying `arrow` prop with `{ pointAtCenter: true }`, the arrow will point to the center of the target element.
```jsx
import { Menu, Dropdown, Button } from 'antd';
const menu = (
<Menu>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
3rd menu item
</a>
</Menu.Item>
</Menu>
);
ReactDOM.render(
<>
<Dropdown overlay={menu} placement="bottomLeft" arrow={{ pointAtCenter: true }}>
<Button>bottomLeft</Button>
</Dropdown>
<Dropdown overlay={menu} placement="bottom" arrow={{ pointAtCenter: true }}>
<Button>bottom</Button>
</Dropdown>
<Dropdown overlay={menu} placement="bottomRight" arrow={{ pointAtCenter: true }}>
<Button>bottomRight</Button>
</Dropdown>
<br />
<Dropdown overlay={menu} placement="topLeft" arrow={{ pointAtCenter: true }}>
<Button>topLeft</Button>
</Dropdown>
<Dropdown overlay={menu} placement="top" arrow={{ pointAtCenter: true }}>
<Button>top</Button>
</Dropdown>
<Dropdown overlay={menu} placement="topRight" arrow={{ pointAtCenter: true }}>
<Button>topRight</Button>
</Dropdown>
</>,
mountNode,
);
```
```css
#components-dropdown-demo-arrow-center .ant-btn {
margin-right: 8px;
margin-bottom: 8px;
}
.ant-row-rtl #components-dropdown-demo-arrow-center .ant-btn {
margin-right: 0;
margin-bottom: 8px;
margin-left: 8px;
}
```

View File

@ -41,8 +41,8 @@ ReactDOM.render(
<Dropdown overlay={menu} placement="bottomLeft" arrow>
<Button>bottomLeft</Button>
</Dropdown>
<Dropdown overlay={menu} placement="bottomCenter" arrow>
<Button>bottomCenter</Button>
<Dropdown overlay={menu} placement="bottom" arrow>
<Button>bottom</Button>
</Dropdown>
<Dropdown overlay={menu} placement="bottomRight" arrow>
<Button>bottomRight</Button>
@ -51,8 +51,8 @@ ReactDOM.render(
<Dropdown overlay={menu} placement="topLeft" arrow>
<Button>topLeft</Button>
</Dropdown>
<Dropdown overlay={menu} placement="topCenter" arrow>
<Button>topCenter</Button>
<Dropdown overlay={menu} placement="top" arrow>
<Button>top</Button>
</Dropdown>
<Dropdown overlay={menu} placement="topRight" arrow>
<Button>topRight</Button>

View File

@ -46,7 +46,7 @@ ReactDOM.render(
<Dropdown.Button onClick={handleButtonClick} overlay={menu}>
Dropdown
</Dropdown.Button>
<Dropdown.Button overlay={menu} placement="bottomCenter" icon={<UserOutlined />}>
<Dropdown.Button overlay={menu} placement="bottom" icon={<UserOutlined />}>
Dropdown
</Dropdown.Button>
<Dropdown.Button onClick={handleButtonClick} overlay={menu} disabled>

View File

@ -42,8 +42,8 @@ ReactDOM.render(
<Dropdown overlay={menu} placement="bottomLeft">
<Button>bottomLeft</Button>
</Dropdown>
<Dropdown overlay={menu} placement="bottomCenter">
<Button>bottomCenter</Button>
<Dropdown overlay={menu} placement="bottom">
<Button>bottom</Button>
</Dropdown>
<Dropdown overlay={menu} placement="bottomRight">
<Button>bottomRight</Button>
@ -53,8 +53,8 @@ ReactDOM.render(
<Dropdown overlay={menu} placement="topLeft">
<Button>topLeft</Button>
</Dropdown>
<Dropdown overlay={menu} placement="topCenter">
<Button>topCenter</Button>
<Dropdown overlay={menu} placement="top">
<Button>top</Button>
</Dropdown>
<Dropdown overlay={menu} placement="topRight">
<Button>topRight</Button>

View File

@ -7,6 +7,7 @@ import { ConfigContext } from '../config-provider';
import devWarning from '../_util/devWarning';
import { tuple } from '../_util/type';
import { cloneElement } from '../_util/reactNode';
import getPlacements from '../_util/placements';
const Placements = tuple(
'topLeft',
@ -15,6 +16,8 @@ const Placements = tuple(
'bottomLeft',
'bottomCenter',
'bottomRight',
'top',
'bottom',
);
type Placement = typeof Placements[number];
@ -34,8 +37,12 @@ type Align = {
useCssTransform?: boolean;
};
export type DropdownArrowOptions = {
pointAtCenter?: boolean;
};
export interface DropDownProps {
arrow?: boolean;
arrow?: boolean | DropdownArrowOptions;
trigger?: ('click' | 'hover' | 'contextMenu')[];
overlay: React.ReactElement | OverlayFunc;
onVisibleChange?: (visible: boolean) => void;
@ -129,10 +136,21 @@ const Dropdown: DropdownInterface = props => {
const getPlacement = () => {
const { placement } = props;
if (placement !== undefined) {
return placement;
if (!placement) {
return direction === 'rtl' ? ('bottomRight' as Placement) : ('bottomLeft' as Placement);
}
return direction === 'rtl' ? ('bottomRight' as Placement) : ('bottomLeft' as Placement);
if (placement.includes('Center')) {
const newPlacement = placement.slice(0, placement.indexOf('Center'));
devWarning(
!placement.includes('Center'),
'Dropdown',
`You are using '${placement}' placement in Dropdown, which is deprecated. Try to use '${newPlacement}' instead.`,
);
return newPlacement;
}
return placement;
};
const {
@ -169,11 +187,16 @@ const Dropdown: DropdownInterface = props => {
alignPoint = true;
}
const builtinPlacements = getPlacements({
arrowPointAtCenter: typeof arrow === 'object' && arrow.pointAtCenter,
});
return (
<RcDropdown
arrow={arrow}
alignPoint={alignPoint}
{...props}
builtinPlacements={builtinPlacements}
arrow={!!arrow}
overlayClassName={overlayClassNameCustomized}
prefixCls={prefixCls}
getPopupContainer={getPopupContainer || getContextPopupContainer}

View File

@ -17,14 +17,14 @@ When there are more than a few options to choose from, you can wrap them in a `D
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| arrow | Whether the dropdown arrow should be visible | boolean | false | |
| arrow | Whether the dropdown arrow should be visible | boolean \| { pointAtCenter: boolean } | false | |
| disabled | Whether the dropdown menu is disabled | boolean | - | |
| destroyPopupOnHide | Whether destroy dropdown when hidden | boolean | false | |
| getPopupContainer | To set the container of the dropdown menu. The default is to create a div element in body, but you can reset it to the scrolling area and make a relative reposition. [Example on CodePen](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
| overlay | The dropdown menu | [Menu](/components/menu) \| () => Menu | - | |
| overlayClassName | The class name of the dropdown root element | string | - | |
| overlayStyle | The style of the dropdown root element | CSSProperties | - | |
| placement | Placement of popup menu: `bottomLeft`, `bottomCenter`, `bottomRight`, `topLeft`, `topCenter` or `topRight` | string | `bottomLeft` | |
| placement | Placement of popup menu: `bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
| trigger | The trigger mode which executes the dropdown action. Note that hover can't be used on touchscreens | Array&lt;`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
| visible | Whether the dropdown menu is currently visible | boolean | - | |
| onVisibleChange | Called when the visible state is changed. Not trigger when hidden by click item | (visible: boolean) => void | - | |
@ -44,7 +44,7 @@ You should use [Menu](/components/menu/) as `overlay`. The menu items and divide
| disabled | Whether the dropdown menu is disabled | boolean | - | |
| icon | Icon (appears on the right) | ReactNode | - | |
| overlay | The dropdown menu | [Menu](/components/menu) | - | |
| placement | Placement of popup menu: `bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | string | `bottomLeft` | |
| placement | Placement of popup menu: `bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
| size | Size of the button, the same as [Button](/components/button/#API) | string | `default` | |
| trigger | The trigger mode which executes the dropdown action | Array&lt;`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
| type | Type of the button, the same as [Button](/components/button/#API) | string | `default` | |

View File

@ -21,14 +21,14 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| arrow | 下拉框箭头是否显示 | boolean | false | |
| arrow | 下拉框箭头是否显示 | boolean \| { pointAtCenter: boolean } | false | |
| disabled | 菜单是否禁用 | boolean | - | |
| destroyPopupOnHide | 关闭后是否销毁 Dropdown | boolean | false | |
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
| overlay | 菜单 | [Menu](/components/menu) \| () => Menu | - | |
| overlayClassName | 下拉根元素的类名称 | string | - | |
| overlayStyle | 下拉根元素的样式 | CSSProperties | - | |
| placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | string | `bottomLeft` | |
| placement | 菜单弹出位置:`bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
| trigger | 触发下拉的行为, 移动端不支持 hover | Array&lt;`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
| visible | 菜单是否显示 | boolean | - | |
| onVisibleChange | 菜单显示状态改变时调用,参数为 `visible`。点击菜单按钮导致的消失不会触发 | (visible: boolean) => void | - | |
@ -48,7 +48,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
| disabled | 菜单是否禁用 | boolean | - | |
| icon | 右侧的 icon | ReactNode | - | |
| overlay | 菜单 | [Menu](/components/menu/) | - | |
| placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | string | `bottomLeft` | |
| placement | 菜单弹出位置:`bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
| size | 按钮大小,和 [Button](/components/button/#API) 一致 | string | `default` | |
| trigger | 触发下拉的行为 | Array&lt;`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
| type | 按钮类型,和 [Button](/components/button/#API) 一致 | string | `default` | |

View File

@ -49,14 +49,14 @@
}
// Offset the popover to account for the dropdown arrow
&-show-arrow&-placement-topCenter,
&-show-arrow&-placement-topLeft,
&-show-arrow&-placement-top,
&-show-arrow&-placement-topRight {
padding-bottom: @popover-distance;
}
&-show-arrow&-placement-bottomCenter,
&-show-arrow&-placement-bottomLeft,
&-show-arrow&-placement-bottom,
&-show-arrow&-placement-bottomRight {
padding-top: @popover-distance;
}
@ -68,23 +68,25 @@
position: absolute;
z-index: 1; // lift it up so the menu wouldn't cask shadow on it
display: block;
width: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
height: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
background: transparent;
border-style: solid;
border-width: (sqrt(@popover-arrow-width * @popover-arrow-width * 2) / 2);
width: @popover-arrow-width;
height: @popover-arrow-width;
background: linear-gradient(
135deg,
transparent 40%,
@popover-bg 40%
); // Use linear-gradient to prevent arrow from covering text
.roundedArrow(@popover-arrow-width, 5px, @popover-bg);
}
&-placement-top > &-arrow,
&-placement-topLeft > &-arrow,
&-placement-topRight > &-arrow {
bottom: @popover-arrow-width * sqrt((1 / 2)) + 2px;
box-shadow: 3px 3px 7px -3px fade(@black, 10%);
transform: rotate(45deg);
}
&-placement-topCenter > &-arrow,
&-placement-topLeft > &-arrow,
&-placement-topRight > &-arrow {
bottom: @popover-distance - @popover-arrow-width + 2.2px;
border-color: transparent @popover-bg @popover-bg transparent;
box-shadow: 3px 3px 7px fade(@black, 7%);
}
&-placement-topCenter > &-arrow {
&-placement-top > &-arrow {
left: 50%;
transform: translateX(-50%) rotate(45deg);
}
@ -97,17 +99,17 @@
right: 16px;
}
&-placement-bottomCenter > &-arrow,
&-placement-bottom > &-arrow,
&-placement-bottomLeft > &-arrow,
&-placement-bottomRight > &-arrow {
top: @popover-distance - @popover-arrow-width + 2px;
border-color: @popover-bg transparent transparent @popover-bg;
box-shadow: -2px -2px 5px fade(@black, 6%);
top: (@popover-arrow-width + 2px) * sqrt((1 / 2));
box-shadow: 2px 2px 5px -2px fade(@black, 10%);
transform: rotate(-135deg) translateY(-0.5px);
}
&-placement-bottomCenter > &-arrow {
&-placement-bottom > &-arrow {
left: 50%;
transform: translateX(-50%) rotate(45deg);
transform: translateX(-50%) rotate(-135deg) translateY(-0.5px);
}
&-placement-bottomLeft > &-arrow {
@ -299,8 +301,8 @@
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottomLeft,
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottomLeft,
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottomCenter,
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottomCenter,
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottom,
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottom,
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottomRight,
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottomRight {
animation-name: antSlideUpIn;
@ -308,21 +310,21 @@
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-topLeft,
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-topLeft,
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-topCenter,
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-topCenter,
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-top,
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-top,
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-topRight,
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-topRight {
animation-name: antSlideDownIn;
}
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottomLeft,
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottomCenter,
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottom,
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottomRight {
animation-name: antSlideUpOut;
}
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-topLeft,
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-topCenter,
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-top,
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-topRight {
animation-name: antSlideDownOut;
}

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import { useContext } from 'react';
import { useContext, useMemo } from 'react';
import classNames from 'classnames';
import { Field, FormInstance, FieldContext, ListContext } from 'rc-field-form';
import { FieldProps } from 'rc-field-form/lib/Field';
@ -12,7 +12,12 @@ import { tuple } from '../_util/type';
import devWarning from '../_util/devWarning';
import FormItemLabel, { FormItemLabelProps, LabelTooltipType } from './FormItemLabel';
import FormItemInput, { FormItemInputProps } from './FormItemInput';
import { FormContext, NoStyleItemContext } from './context';
import {
FormContext,
FormItemStatusContext,
NoStyleItemContext,
FormItemStatusContextProps,
} from './context';
import { toArray, getFieldId } from './util';
import { cloneElement, isValidElement } from '../_util/reactNode';
import useFrameState from './hooks/useFrameState';
@ -199,6 +204,28 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
// ===================== Children Ref =====================
const getItemRef = useItemRef();
// ======================== Status ========================
let mergedValidateStatus: ValidateStatus = '';
if (validateStatus !== undefined) {
mergedValidateStatus = validateStatus;
} else if (meta?.validating) {
mergedValidateStatus = 'validating';
} else if (debounceErrors.length) {
mergedValidateStatus = 'error';
} else if (debounceWarnings.length) {
mergedValidateStatus = 'warning';
} else if (meta?.touched) {
mergedValidateStatus = 'success';
}
const formItemStatusContext = useMemo<FormItemStatusContextProps>(
() => ({
status: mergedValidateStatus,
hasFeedback,
}),
[mergedValidateStatus, hasFeedback],
);
// ======================== Render ========================
function renderLayout(
baseChildren: React.ReactNode,
@ -208,19 +235,6 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
if (noStyle && !hidden) {
return baseChildren;
}
// ======================== Status ========================
let mergedValidateStatus: ValidateStatus = '';
if (validateStatus !== undefined) {
mergedValidateStatus = validateStatus;
} else if (meta?.validating) {
mergedValidateStatus = 'validating';
} else if (debounceErrors.length) {
mergedValidateStatus = 'error';
} else if (debounceWarnings.length) {
mergedValidateStatus = 'warning';
} else if (meta?.touched) {
mergedValidateStatus = 'success';
}
const itemClassName = {
[`${prefixCls}-item`]: true,
@ -281,11 +295,12 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
warnings={debounceWarnings}
prefixCls={prefixCls}
status={mergedValidateStatus}
validateStatus={mergedValidateStatus}
help={help}
>
<NoStyleItemContext.Provider value={onSubItemMetaChange}>
{baseChildren}
<FormItemStatusContext.Provider value={formItemStatusContext}>
{baseChildren}
</FormItemStatusContext.Provider>
</NoStyleItemContext.Provider>
</FormItemInput>
</Row>

View File

@ -1,10 +1,5 @@
import * as React from 'react';
import classNames from 'classnames';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import Col, { ColProps } from '../grid/col';
import { ValidateStatus } from './FormItem';
import { FormContext, FormItemPrefixContext } from './context';
@ -15,8 +10,6 @@ interface FormItemInputMiscProps {
children: React.ReactNode;
errors: React.ReactNode[];
warnings: React.ReactNode[];
hasFeedback?: boolean;
validateStatus?: ValidateStatus;
/** @private Internal Usage, do not use in any of your production. */
_internalItemRender?: {
mark: string;
@ -38,13 +31,6 @@ export interface FormItemInputProps {
help?: React.ReactNode;
}
const iconMap: { [key: string]: any } = {
success: CheckCircleFilled,
warning: ExclamationCircleFilled,
error: CloseCircleFilled,
validating: LoadingOutlined,
};
const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = props => {
const {
prefixCls,
@ -53,9 +39,7 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
children,
errors,
warnings,
hasFeedback,
_internalItemRender: formItemRender,
validateStatus,
extra,
help,
} = props;
@ -67,15 +51,6 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
const className = classNames(`${baseClassName}-control`, mergedWrapperCol.className);
// Should provides additional icon if `hasFeedback`
const IconNode = validateStatus && iconMap[validateStatus];
const icon =
hasFeedback && IconNode ? (
<span className={`${baseClassName}-children-icon`}>
<IconNode />
</span>
) : null;
// Pass to sub FormItem should not with col info
const subFormContext = React.useMemo(() => ({ ...formContext }), [formContext]);
delete subFormContext.labelCol;
@ -84,7 +59,6 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
const inputDom = (
<div className={`${baseClassName}-control-input`}>
<div className={`${baseClassName}-control-input-content`}>{children}</div>
{icon}
</div>
);
const formItemContext = React.useMemo(() => ({ prefixCls, status }), [prefixCls, status]);

File diff suppressed because it is too large Load Diff

View File

@ -50,3 +50,10 @@ export interface FormItemPrefixContextProps {
export const FormItemPrefixContext = React.createContext<FormItemPrefixContextProps>({
prefixCls: '',
});
export interface FormItemStatusContextProps {
status?: ValidateStatus;
hasFeedback?: boolean;
}
export const FormItemStatusContext = React.createContext<FormItemStatusContextProps>({});

View File

@ -23,7 +23,17 @@ We provide properties like `validateStatus` `help` `hasFeedback` to customize yo
```tsx
import { SmileOutlined } from '@ant-design/icons';
import { Form, Input, DatePicker, TimePicker, Select, Cascader, InputNumber, Mentions } from 'antd';
import {
Form,
Input,
DatePicker,
TimePicker,
Select,
Cascader,
InputNumber,
Mentions,
TreeSelect,
} from 'antd';
const { Option } = Select;
@ -87,7 +97,7 @@ ReactDOM.render(
</Form.Item>
<Form.Item label="Error" hasFeedback validateStatus="error">
<Select allowClear>
<Select placeholder="I'm Select" allowClear>
<Option value="1">Option 1</Option>
<Option value="2">Option 2</Option>
<Option value="3">Option 3</Option>
@ -97,10 +107,18 @@ ReactDOM.render(
<Form.Item
label="Validating"
hasFeedback
validateStatus="validating"
help="The information is being validated..."
validateStatus="error"
help="Something breaks the rule."
>
<Cascader options={[{ value: 'xx', label: 'xx' }]} allowClear />
<Cascader placeholder="I'm Cascader" options={[{ value: 'xx', label: 'xx' }]} allowClear />
</Form.Item>
<Form.Item label="Warning" hasFeedback validateStatus="warning" help="Need to be checked">
<TreeSelect
placeholder="I'm TreeSelect"
treeData={[{ value: 'xx', label: 'xx' }]}
allowClear
/>
</Form.Item>
<Form.Item label="inline" style={{ marginBottom: 0 }}>
@ -137,9 +155,13 @@ ReactDOM.render(
<Input.Password allowClear placeholder="with input password and allowClear" />
</Form.Item>
<Form.Item label="Fail" validateStatus="error">
<Form.Item label="Fail" validateStatus="error" hasFeedback>
<Mentions />
</Form.Item>
<Form.Item label="Fail" validateStatus="error" hasFeedback help="Should have something">
<Input.TextArea allowClear showCount />
</Form.Item>
</Form>,
mountNode,
);

View File

@ -19,9 +19,10 @@ import { Form, InputNumber } from 'antd';
type ValidateStatus = Parameters<typeof Form.Item>[0]['validateStatus'];
function validatePrimeNumber(
number: number,
): { validateStatus: ValidateStatus; errorMsg: string | null } {
function validatePrimeNumber(number: number): {
validateStatus: ValidateStatus;
errorMsg: string | null;
} {
if (number === 11) {
return {
validateStatus: 'success',

View File

@ -242,7 +242,7 @@ Provide linkage between forms. If a sub form with `name` prop update, it will au
| isFieldsTouched | Check if fields have been operated. Check if all fields is touched when `allTouched` is `true` | (nameList?: [NamePath](#NamePath)\[], allTouched?: boolean) => boolean | |
| isFieldTouched | Check if a field has been operated | (name: [NamePath](#NamePath)) => boolean | |
| isFieldValidating | Check field if is in validating | (name: [NamePath](#NamePath)) => boolean | |
| resetFields | Reset fields to `initialValues` | (fields?: [FieldData](#FieldData)\[]) => void | |
| resetFields | Reset fields to `initialValues` | (fields?: [NamePath](#NamePath)\[]) => void | |
| scrollToField | Scroll to field position | (name: [NamePath](#NamePath), options: \[[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)]) => void | |
| setFields | Set fields status | (fields: [FieldData](#FieldData)\[]) => void | |
| setFieldsValue | Set fields value(Will directly pass to form store. If you do not want to modify passed object, please clone first) | (values) => void | |

View File

@ -241,7 +241,7 @@ Form.List 渲染表单相关操作函数。
| isFieldsTouched | 检查一组字段是否被用户操作过,`allTouched` 为 `true` 时检查是否所有字段都被操作过 | (nameList?: [NamePath](#NamePath)\[], allTouched?: boolean) => boolean | |
| isFieldTouched | 检查对应字段是否被用户操作过 | (name: [NamePath](#NamePath)) => boolean | |
| isFieldValidating | 检查对应字段是否正在校验 | (name: [NamePath](#NamePath)) => boolean | |
| resetFields | 重置一组字段到 `initialValues` | (fields?: [FieldData](#FieldData)\[]) => void | |
| resetFields | 重置一组字段到 `initialValues` | (fields?: [NamePath](#NamePath)\[]) => void | |
| scrollToField | 滚动到对应字段位置 | (name: [NamePath](#NamePath), options: [ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)) => void | |
| setFields | 设置一组字段状态 | (fields: [FieldData](#FieldData)\[]) => void | |
| setFieldsValue | 设置表单的值(该值将直接传入 form store 中。如果你不希望传入对象被修改,请克隆后传入) | (values) => void | |

View File

@ -10,40 +10,10 @@
.@{ant-prefix}-form-item-split {
color: @text-color;
}
// 输入框的不同校验状态
:not(.@{ant-prefix}-input-disabled):not(.@{ant-prefix}-input-borderless).@{ant-prefix}-input,
:not(.@{ant-prefix}-input-affix-wrapper-disabled):not(.@{ant-prefix}-input-affix-wrapper-borderless).@{ant-prefix}-input-affix-wrapper,
:not(.@{ant-prefix}-input-number-affix-wrapper-disabled):not(.@{ant-prefix}-input-number-affix-wrapper-borderless).@{ant-prefix}-input-number-affix-wrapper {
&,
&:hover {
background-color: @background-color;
border-color: @border-color;
}
&:focus,
&-focused {
.active(@border-color, @hoverBorderColor, @outlineColor);
}
}
.@{ant-prefix}-calendar-picker-open .@{ant-prefix}-calendar-picker-input {
.active(@border-color, @hoverBorderColor, @outlineColor);
}
.@{ant-prefix}-input-prefix,
.@{ant-prefix}-input-number-prefix {
color: @text-color;
}
.@{ant-prefix}-input-group-addon,
.@{ant-prefix}-input-number-group-addon {
color: @text-color;
border-color: @border-color;
}
.has-feedback {
color: @text-color;
}
}
// Reset form styles

View File

@ -24,287 +24,19 @@
}
&-has-feedback {
// ========================= Input =========================
.@{ant-prefix}-input {
padding-right: 24px;
}
// https://github.com/ant-design/ant-design/issues/19884
.@{ant-prefix}-input-affix-wrapper {
.@{ant-prefix}-input-suffix {
padding-right: 18px;
}
}
// Fix issue: https://github.com/ant-design/ant-design/issues/7854
.@{ant-prefix}-input-search:not(.@{ant-prefix}-input-search-enter-button) {
.@{ant-prefix}-input-suffix {
right: 28px;
}
}
// ======================== Switch =========================
.@{ant-prefix}-switch {
margin: 2px 0 4px;
}
// ======================== Select =========================
// Fix overlapping between feedback icon and <Select>'s arrow.
// https://github.com/ant-design/ant-design/issues/4431
> .@{ant-prefix}-select .@{ant-prefix}-select-arrow,
> .@{ant-prefix}-select .@{ant-prefix}-select-clear,
:not(.@{ant-prefix}-input-group-addon) > .@{ant-prefix}-select .@{ant-prefix}-select-arrow,
:not(.@{ant-prefix}-input-group-addon) > .@{ant-prefix}-select .@{ant-prefix}-select-clear,
:not(.@{ant-prefix}-input-number-group-addon)
> .@{ant-prefix}-select
.@{ant-prefix}-select-arrow,
:not(.@{ant-prefix}-input-number-group-addon)
> .@{ant-prefix}-select
.@{ant-prefix}-select-clear {
right: 32px;
}
> .@{ant-prefix}-select .@{ant-prefix}-select-selection-selected-value,
:not(.@{ant-prefix}-input-group-addon)
> .@{ant-prefix}-select
.@{ant-prefix}-select-selection-selected-value,
:not(.@{ant-prefix}-input-number-group-addon)
> .@{ant-prefix}-select
.@{ant-prefix}-select-selection-selected-value {
padding-right: 42px;
}
// ======================= Cascader ========================
.@{ant-prefix}-cascader-picker {
&-arrow {
margin-right: 19px;
}
&-clear {
right: 32px;
}
}
// ======================== Picker =========================
// Fix issue: https://github.com/ant-design/ant-design/issues/4783
.@{ant-prefix}-picker {
padding-right: @input-padding-horizontal-base + @font-size-base * 1.3;
&-large {
padding-right: @input-padding-horizontal-lg + @font-size-base * 1.3;
}
&-small {
padding-right: @input-padding-horizontal-sm + @font-size-base * 1.3;
}
}
// ===================== Status Group ======================
&.@{form-item-prefix-cls} {
&-has-success,
&-has-warning,
&-has-error,
&-is-validating {
// ====================== Icon ======================
.@{form-item-prefix-cls}-children-icon {
position: absolute;
top: 50%;
right: 0;
z-index: 1;
width: @input-height-base;
height: 20px;
margin-top: -10px;
font-size: @font-size-base;
line-height: 20px;
text-align: center;
visibility: visible;
animation: zoomIn 0.3s @ease-out-back;
pointer-events: none;
}
}
}
}
// ======================== Success ========================
&-has-success {
&.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon {
color: @success-color;
animation-name: diffZoomIn1 !important;
}
}
// ======================== Warning ========================
&-has-warning {
.form-control-validation(@warning-color; @warning-color; @form-warning-input-bg; @warning-color-hover; @warning-color-outline);
&.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon {
color: @warning-color;
animation-name: diffZoomIn3 !important;
}
// Select
.@{ant-prefix}-select:not(.@{ant-prefix}-select-disabled):not(.@{ant-prefix}-select-customize-input) {
.@{ant-prefix}-select-selector {
background-color: @form-warning-input-bg;
border-color: @warning-color !important;
}
&.@{ant-prefix}-select-open .@{ant-prefix}-select-selector,
&.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector {
.active(@warning-color, @warning-color-hover, @warning-color-outline);
}
}
// InputNumber, TimePicker
.@{ant-prefix}-input-number,
.@{ant-prefix}-picker {
background-color: @form-warning-input-bg;
border-color: @warning-color;
&-focused,
&:focus {
.active(@warning-color, @warning-color-hover, @warning-color-outline);
}
&:not([disabled]):hover {
background-color: @form-warning-input-bg;
border-color: @warning-color;
}
}
.@{ant-prefix}-cascader-picker:focus .@{ant-prefix}-cascader-input {
.active(@warning-color, @warning-color-hover, @warning-color-outline);
}
}
// ========================= Error =========================
&-has-error {
.form-control-validation(@error-color; @error-color; @form-error-input-bg; @error-color-hover; @error-color-outline);
&.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon {
color: @error-color;
animation-name: diffZoomIn2 !important;
}
// Select
.@{ant-prefix}-select:not(.@{ant-prefix}-select-disabled):not(.@{ant-prefix}-select-customize-input) {
.@{ant-prefix}-select-selector {
background-color: @form-error-input-bg;
border-color: @error-color !important;
}
&.@{ant-prefix}-select-open .@{ant-prefix}-select-selector,
&.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector {
.active(@error-color, @error-color-hover, @error-color-outline);
}
}
// fixes https://github.com/ant-design/ant-design/issues/20482
.@{ant-prefix}-input-group-addon,
.@{ant-prefix}-input-number-group-addon {
.@{ant-prefix}-select {
&.@{ant-prefix}-select-single:not(.@{ant-prefix}-select-customize-input)
.@{ant-prefix}-select-selector {
background-color: inherit;
border: 0;
box-shadow: none;
}
}
}
.@{ant-prefix}-select.@{ant-prefix}-select-auto-complete {
.@{ant-prefix}-input:focus {
border-color: @error-color;
}
}
// InputNumber, TimePicker
.@{ant-prefix}-input-number,
.@{ant-prefix}-picker {
background-color: @form-error-input-bg;
border-color: @error-color;
&-focused,
&:focus {
.active(@error-color, @error-color-hover, @error-color-outline);
}
&:not([disabled]):hover {
background-color: @form-error-input-bg;
border-color: @error-color;
}
}
.@{ant-prefix}-mention-wrapper {
.@{ant-prefix}-mention-editor {
&,
&:not([disabled]):hover {
background-color: @form-error-input-bg;
border-color: @error-color;
}
}
&.@{ant-prefix}-mention-active:not([disabled]) .@{ant-prefix}-mention-editor,
.@{ant-prefix}-mention-editor:not([disabled]):focus {
.active(@error-color, @error-color-hover, @error-color-outline);
}
}
// Cascader
.@{ant-prefix}-cascader-picker {
&:hover
.@{ant-prefix}-cascader-picker-label:hover
+ .@{ant-prefix}-cascader-input.@{ant-prefix}-input {
border-color: @error-color;
}
&:focus .@{ant-prefix}-cascader-input {
background-color: @form-error-input-bg;
.active(@error-color, @error-color-hover, @error-color-outline);
}
}
// Transfer
.@{ant-prefix}-transfer {
&-list {
border-color: @error-color;
&-search:not([disabled]) {
border-color: @input-border-color;
&:hover {
.hover();
}
&:focus {
.active();
}
}
}
}
// Radio.Group
.@{ant-prefix}-radio-button-wrapper {
border-color: @error-color !important;
&:not(:first-child) {
&::before {
background-color: @error-color;
}
}
}
// Mentions
.@{ant-prefix}-mentions {
border-color: @error-color !important;
&-focused,
&:focus {
.active(@error-color, @error-color-hover, @error-color-outline);
}
}
}
// ====================== Validating =======================
&-is-validating {
&.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon {
display: inline-block;
color: @primary-color;
}
}
}

View File

@ -838,41 +838,41 @@ Array [
style="width:50%"
>
<div
class="ant-slider ant-slider-with-marks"
class="ant-slider ant-slider-horizontal ant-slider-with-marks"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left:0%;right:auto;width:20%"
style="left:0%;width:20%"
/>
<div
class="ant-slider-step"
>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:0%"
style="left:0%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:20%"
style="left:20%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:40%"
style="left:40%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:60%"
style="left:60%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:80%"
style="left:80%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:100%"
style="left:100%;transform:translateX(-50%)"
/>
</div>
<div
@ -882,7 +882,7 @@ Array [
aria-valuenow="1"
class="ant-slider-handle"
role="slider"
style="left:20%;right:auto;transform:translateX(-50%)"
style="left:20%;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -914,37 +914,37 @@ Array [
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
style="left:0%;transform:translateX(-50%)"
>
8
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:20%"
style="left:20%;transform:translateX(-50%)"
>
16
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:40%"
style="left:40%;transform:translateX(-50%)"
>
24
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:60%"
style="left:60%;transform:translateX(-50%)"
>
32
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:80%"
style="left:80%;transform:translateX(-50%)"
>
40
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%"
style="left:100%;transform:translateX(-50%)"
>
48
</span>
@ -958,41 +958,41 @@ Array [
style="width:50%"
>
<div
class="ant-slider ant-slider-with-marks"
class="ant-slider ant-slider-horizontal ant-slider-with-marks"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left:0%;right:auto;width:20%"
style="left:0%;width:20%"
/>
<div
class="ant-slider-step"
>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:0%"
style="left:0%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:20%"
style="left:20%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:40%"
style="left:40%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:60%"
style="left:60%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:80%"
style="left:80%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:100%"
style="left:100%;transform:translateX(-50%)"
/>
</div>
<div
@ -1002,7 +1002,7 @@ Array [
aria-valuenow="1"
class="ant-slider-handle"
role="slider"
style="left:20%;right:auto;transform:translateX(-50%)"
style="left:20%;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -1034,37 +1034,37 @@ Array [
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
style="left:0%;transform:translateX(-50%)"
>
8
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:20%"
style="left:20%;transform:translateX(-50%)"
>
16
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:40%"
style="left:40%;transform:translateX(-50%)"
>
24
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:60%"
style="left:60%;transform:translateX(-50%)"
>
32
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:80%"
style="left:80%;transform:translateX(-50%)"
>
40
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%"
style="left:100%;transform:translateX(-50%)"
>
48
</span>
@ -1078,41 +1078,41 @@ Array [
style="width:50%;margin-bottom:48px"
>
<div
class="ant-slider ant-slider-with-marks"
class="ant-slider ant-slider-horizontal ant-slider-with-marks"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left:0%;right:auto;width:40%"
style="left:0%;width:40%"
/>
<div
class="ant-slider-step"
>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:0%"
style="left:0%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:20%"
style="left:20%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:40%"
style="left:40%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:60%"
style="left:60%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:80%"
style="left:80%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:100%"
style="left:100%;transform:translateX(-50%)"
/>
</div>
<div
@ -1122,7 +1122,7 @@ Array [
aria-valuenow="2"
class="ant-slider-handle"
role="slider"
style="left:40%;right:auto;transform:translateX(-50%)"
style="left:40%;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -1154,37 +1154,37 @@ Array [
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
style="left:0%;transform:translateX(-50%)"
>
2
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:20%"
style="left:20%;transform:translateX(-50%)"
>
3
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:40%"
style="left:40%;transform:translateX(-50%)"
>
4
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:60%"
style="left:60%;transform:translateX(-50%)"
>
6
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:80%"
style="left:80%;transform:translateX(-50%)"
>
8
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%"
style="left:100%;transform:translateX(-50%)"
>
12
</span>

View File

@ -838,41 +838,41 @@ Array [
style="width:50%"
>
<div
class="ant-slider ant-slider-with-marks"
class="ant-slider ant-slider-horizontal ant-slider-with-marks"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left:0%;right:auto;width:20%"
style="left:0%;width:20%"
/>
<div
class="ant-slider-step"
>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:0%"
style="left:0%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:20%"
style="left:20%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:40%"
style="left:40%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:60%"
style="left:60%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:80%"
style="left:80%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:100%"
style="left:100%;transform:translateX(-50%)"
/>
</div>
<div
@ -882,7 +882,7 @@ Array [
aria-valuenow="1"
class="ant-slider-handle"
role="slider"
style="left:20%;right:auto;transform:translateX(-50%)"
style="left:20%;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -890,37 +890,37 @@ Array [
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
style="left:0%;transform:translateX(-50%)"
>
8
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:20%"
style="left:20%;transform:translateX(-50%)"
>
16
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:40%"
style="left:40%;transform:translateX(-50%)"
>
24
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:60%"
style="left:60%;transform:translateX(-50%)"
>
32
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:80%"
style="left:80%;transform:translateX(-50%)"
>
40
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%"
style="left:100%;transform:translateX(-50%)"
>
48
</span>
@ -934,41 +934,41 @@ Array [
style="width:50%"
>
<div
class="ant-slider ant-slider-with-marks"
class="ant-slider ant-slider-horizontal ant-slider-with-marks"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left:0%;right:auto;width:20%"
style="left:0%;width:20%"
/>
<div
class="ant-slider-step"
>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:0%"
style="left:0%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:20%"
style="left:20%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:40%"
style="left:40%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:60%"
style="left:60%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:80%"
style="left:80%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:100%"
style="left:100%;transform:translateX(-50%)"
/>
</div>
<div
@ -978,7 +978,7 @@ Array [
aria-valuenow="1"
class="ant-slider-handle"
role="slider"
style="left:20%;right:auto;transform:translateX(-50%)"
style="left:20%;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -986,37 +986,37 @@ Array [
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
style="left:0%;transform:translateX(-50%)"
>
8
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:20%"
style="left:20%;transform:translateX(-50%)"
>
16
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:40%"
style="left:40%;transform:translateX(-50%)"
>
24
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:60%"
style="left:60%;transform:translateX(-50%)"
>
32
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:80%"
style="left:80%;transform:translateX(-50%)"
>
40
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%"
style="left:100%;transform:translateX(-50%)"
>
48
</span>
@ -1030,41 +1030,41 @@ Array [
style="width:50%;margin-bottom:48px"
>
<div
class="ant-slider ant-slider-with-marks"
class="ant-slider ant-slider-horizontal ant-slider-with-marks"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left:0%;right:auto;width:40%"
style="left:0%;width:40%"
/>
<div
class="ant-slider-step"
>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:0%"
style="left:0%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:20%"
style="left:20%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:40%"
style="left:40%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:60%"
style="left:60%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:80%"
style="left:80%;transform:translateX(-50%)"
/>
<span
class="ant-slider-dot"
style="left:100%"
style="left:100%;transform:translateX(-50%)"
/>
</div>
<div
@ -1074,7 +1074,7 @@ Array [
aria-valuenow="2"
class="ant-slider-handle"
role="slider"
style="left:40%;right:auto;transform:translateX(-50%)"
style="left:40%;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -1082,37 +1082,37 @@ Array [
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
style="left:0%;transform:translateX(-50%)"
>
2
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:20%"
style="left:20%;transform:translateX(-50%)"
>
3
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:40%"
style="left:40%;transform:translateX(-50%)"
>
4
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:60%"
style="left:60%;transform:translateX(-50%)"
>
6
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:80%"
style="left:80%;transform:translateX(-50%)"
>
8
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%"
style="left:100%;transform:translateX(-50%)"
>
12
</span>

View File

@ -92,7 +92,7 @@ export { default as Form } from './form';
export { default as Grid } from './grid';
export type { InputProps } from './input';
export type { InputProps, InputRef } from './input';
export { default as Input } from './input';
export type { ImageProps } from './image';

View File

@ -1008,6 +1008,90 @@ exports[`renders ./components/input-number/demo/borderless.md extend context cor
</div>
`;
exports[`renders ./components/input-number/demo/controls.md extend context correctly 1`] = `
<div
class="ant-input-number"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
class="ant-input-number-handler-up-inner"
>
<span
aria-label="arrow-up"
class="anticon anticon-arrow-up"
role="img"
>
<svg
aria-hidden="true"
data-icon="arrow-up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M868 545.5L536.1 163a31.96 31.96 0 00-48.3 0L156 545.5a7.97 7.97 0 006 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z"
/>
</svg>
</span>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
class="ant-input-number-handler-down-inner"
>
<span
aria-label="arrow-down"
class="anticon anticon-arrow-down"
role="img"
>
<svg
aria-hidden="true"
data-icon="arrow-down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861a31.96 31.96 0 0048.3 0L868 478.5c4.5-5.2.8-13.2-6-13.2z"
/>
</svg>
</span>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
`;
exports[`renders ./components/input-number/demo/digit.md extend context correctly 1`] = `
<div
class="ant-input-number"
@ -2097,3 +2181,386 @@ exports[`renders ./components/input-number/demo/size.md extend context correctly
</div>
</div>
`;
exports[`renders ./components/input-number/demo/status.md extend context correctly 1`] = `
<div
class="ant-space ant-space-vertical"
style="width:100%"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-input-number ant-input-number-status-error"
style="width:100%"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-input-number ant-input-number-status-warning"
style="width:100%"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-input-number-affix-wrapper ant-input-number-affix-wrapper-status-error"
style="width:100%"
>
<span
class="ant-input-number-prefix"
>
<span
aria-label="clock-circle"
class="anticon anticon-clock-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="clock-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
/>
</svg>
</span>
</span>
<div
class="ant-input-number ant-input-number-status-error"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-input-number-affix-wrapper ant-input-number-affix-wrapper-status-warning"
style="width:100%"
>
<span
class="ant-input-number-prefix"
>
<span
aria-label="clock-circle"
class="anticon anticon-clock-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="clock-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
/>
</svg>
</span>
</span>
<div
class="ant-input-number ant-input-number-status-warning"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
</div>
</div>
`;

View File

@ -735,6 +735,90 @@ exports[`renders ./components/input-number/demo/borderless.md correctly 1`] = `
</div>
`;
exports[`renders ./components/input-number/demo/controls.md correctly 1`] = `
<div
class="ant-input-number"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
class="ant-input-number-handler-up-inner"
>
<span
aria-label="arrow-up"
class="anticon anticon-arrow-up"
role="img"
>
<svg
aria-hidden="true"
data-icon="arrow-up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M868 545.5L536.1 163a31.96 31.96 0 00-48.3 0L156 545.5a7.97 7.97 0 006 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z"
/>
</svg>
</span>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
class="ant-input-number-handler-down-inner"
>
<span
aria-label="arrow-down"
class="anticon anticon-arrow-down"
role="img"
>
<svg
aria-hidden="true"
data-icon="arrow-down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861a31.96 31.96 0 0048.3 0L868 478.5c4.5-5.2.8-13.2-6-13.2z"
/>
</svg>
</span>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
`;
exports[`renders ./components/input-number/demo/digit.md correctly 1`] = `
<div
class="ant-input-number"
@ -1824,3 +1908,386 @@ exports[`renders ./components/input-number/demo/size.md correctly 1`] = `
</div>
</div>
`;
exports[`renders ./components/input-number/demo/status.md correctly 1`] = `
<div
class="ant-space ant-space-vertical"
style="width:100%"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-input-number ant-input-number-status-error"
style="width:100%"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-input-number ant-input-number-status-warning"
style="width:100%"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-input-number-affix-wrapper ant-input-number-affix-wrapper-status-error"
style="width:100%"
>
<span
class="ant-input-number-prefix"
>
<span
aria-label="clock-circle"
class="anticon anticon-clock-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="clock-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
/>
</svg>
</span>
</span>
<div
class="ant-input-number ant-input-number-status-error"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-input-number-affix-wrapper ant-input-number-affix-wrapper-status-warning"
style="width:100%"
>
<span
class="ant-input-number-prefix"
>
<span
aria-label="clock-circle"
class="anticon anticon-clock-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="clock-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
/>
</svg>
</span>
</span>
<div
class="ant-input-number ant-input-number-status-warning"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
</div>
</div>
`;

View File

@ -1,5 +1,183 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`InputNumber renders correctly when controls has custom upIcon and downIcon 1`] = `
<div
class="ant-input-number"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
class="ant-input-number-handler-up-inner"
>
<span
aria-label="arrow-up"
class="anticon anticon-arrow-up"
role="img"
>
<svg
aria-hidden="true"
data-icon="arrow-up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M868 545.5L536.1 163a31.96 31.96 0 00-48.3 0L156 545.5a7.97 7.97 0 006 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z"
/>
</svg>
</span>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
class="ant-input-number-handler-down-inner"
>
<span
aria-label="arrow-down"
class="anticon anticon-arrow-down"
role="img"
>
<svg
aria-hidden="true"
data-icon="arrow-down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861a31.96 31.96 0 0048.3 0L868 478.5c4.5-5.2.8-13.2-6-13.2z"
/>
</svg>
</span>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
`;
exports[`InputNumber renders correctly when controls is {} 1`] = `
<div
class="ant-input-number"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
`;
exports[`InputNumber renders correctly when controls is boolean 1`] = `
<div
class="ant-input-number"
>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
`;
exports[`InputNumber rtl render component should be rendered correctly in RTL direction 1`] = `
<div
class="ant-input-number ant-input-number-rtl"

View File

@ -1,5 +1,6 @@
import React from 'react';
import { mount } from 'enzyme';
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
import InputNumber from '..';
import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
@ -28,4 +29,41 @@ describe('InputNumber', () => {
expect(onStep).toBeCalledTimes(2);
expect(onStep).toHaveBeenLastCalledWith(1, { offset: 1, type: 'down' });
});
it('renders correctly when controls is boolean', () => {
expect(mount(<InputNumber controls={false} />).render()).toMatchSnapshot();
});
it('renders correctly when controls is {}', () => {
expect(mount(<InputNumber controls={{}} />).render()).toMatchSnapshot();
});
it('renders correctly when controls has custom upIcon and downIcon', () => {
const wrapper = mount(
<InputNumber
controls={{
upIcon: <ArrowUpOutlined />,
downIcon: <ArrowDownOutlined />,
}}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
});
it('should support className', () => {
const wrapper = mount(
<InputNumber
controls={{
upIcon: <ArrowUpOutlined className="my-class-name" />,
downIcon: <ArrowDownOutlined className="my-class-name" />,
}}
/>,
);
expect(wrapper.find('.anticon-arrow-up').getDOMNode().className.includes('my-class-name')).toBe(
true,
);
expect(
wrapper.find('.anticon-arrow-down').getDOMNode().className.includes('my-class-name'),
).toBe(true);
});
});

View File

@ -0,0 +1,25 @@
---
order: 99
debug: true
title:
zh-CN: 图标按钮
en-US: Icon
---
## zh-CN
可以扩展 `controls` 属性用以设置自定义图标。
## en-US
When you need to use a custom `Icon`, you can set the `Icon` component as the property value of `upIcon` and `downIcon`.
```jsx
import { InputNumber } from 'antd';
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
ReactDOM.render(
<InputNumber controls={{ upIcon: <ArrowUpOutlined />, downIcon: <ArrowDownOutlined /> }} />,
mountNode,
);
```

View File

@ -0,0 +1,31 @@
---
order: 19
version: 4.19.0
title:
zh-CN: 自定义状态
en-US: Status
---
## zh-CN
使用 `status` 为 InputNumber 添加状态,可选 `error` 或者 `warning`
## en-US
Add status to InputNumber with `status`, which could be `error` or `warning`.
```tsx
import { InputNumber, Space } from 'antd';
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
const ValidateInputs: React.FC = () => (
<Space direction="vertical" style={{ width: '100%' }}>
<InputNumber status="error" style={{ width: '100%' }} />
<InputNumber status="warning" style={{ width: '100%' }} />
<InputNumber status="error" style={{ width: '100%' }} prefix={<ClockCircleOutlined />} />
<InputNumber status="warning" style={{ width: '100%' }} prefix={<ClockCircleOutlined />} />
</Space>
);
ReactDOM.render(<ValidateInputs />, mountNode);
```

View File

@ -19,7 +19,7 @@ When a numeric value needs to be provided.
| addonBefore | The label text displayed before (on the left side of) the input field | ReactNode | - | |
| autoFocus | If get focus when component mounted | boolean | false | - |
| bordered | Whether has border style | boolean | true | 4.12.0 |
| controls | Whether to show `+-` controls | boolean | true | 4.17.0 |
| controls | Whether to show `+-` controls, or set custom arrows icon | boolean \| { upIcon?: React.ReactNode; downIcon?: React.ReactNode; } | - | 4.19.0 |
| decimalSeparator | Decimal separator | string | - | - |
| defaultValue | The initial value | number | - | - |
| disabled | If disable the input | boolean | false | - |
@ -30,6 +30,7 @@ When a numeric value needs to be provided.
| parser | Specifies the value extracted from formatter | function(string): number | - | - |
| precision | The precision of input value. Will use `formatter` when config of `formatter` | number | - | - |
| readOnly | If readonly the input | boolean | false | - |
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| prefix | The prefix icon for the Input | ReactNode | - | 4.17.0 |
| size | The height of input box | `large` \| `middle` \| `small` | - | - |
| step | The number to which the current value is increased or decreased. It can be an integer or decimal | number \| string | 1 | - |

View File

@ -1,23 +1,32 @@
import * as React from 'react';
import DownOutlined from '@ant-design/icons/DownOutlined';
import UpOutlined from '@ant-design/icons/UpOutlined';
import classNames from 'classnames';
import RcInputNumber, { InputNumberProps as RcInputNumberProps } from 'rc-input-number';
import UpOutlined from '@ant-design/icons/UpOutlined';
import DownOutlined from '@ant-design/icons/DownOutlined';
import * as React from 'react';
import { useContext } from 'react';
import { ConfigContext } from '../config-provider';
import SizeContext, { SizeType } from '../config-provider/SizeContext';
import { FormItemStatusContext } from '../form/context';
import { cloneElement } from '../_util/reactNode';
import {
getFeedbackIcon,
getStatusClassNames,
InputStatus,
getMergedStatus,
} from '../_util/statusUtils';
type ValueType = string | number;
export interface InputNumberProps<T extends ValueType = ValueType>
extends Omit<RcInputNumberProps<T>, 'prefix' | 'size'> {
extends Omit<RcInputNumberProps<T>, 'prefix' | 'size' | 'controls'> {
prefixCls?: string;
addonBefore?: React.ReactNode;
addonAfter?: React.ReactNode;
prefix?: React.ReactNode;
size?: SizeType;
bordered?: boolean;
status?: InputStatus;
controls?: boolean | { upIcon?: React.ReactNode; downIcon?: React.ReactNode };
}
const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props, ref) => {
@ -37,12 +46,33 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
prefix,
bordered = true,
readOnly,
status: customStatus,
controls,
...others
} = props;
const prefixCls = getPrefixCls('input-number', customizePrefixCls);
const upIcon = <UpOutlined className={`${prefixCls}-handler-up-inner`} />;
const downIcon = <DownOutlined className={`${prefixCls}-handler-down-inner`} />;
let upIcon = <UpOutlined className={`${prefixCls}-handler-up-inner`} />;
let downIcon = <DownOutlined className={`${prefixCls}-handler-down-inner`} />;
const controlsTemp = typeof controls === 'boolean' ? controls : undefined;
if (typeof controls === 'object') {
upIcon =
typeof controls.upIcon === 'undefined' ? (
upIcon
) : (
<span className={`${prefixCls}-handler-up-inner`}>{controls.upIcon}</span>
);
downIcon =
typeof controls.downIcon === 'undefined' ? (
downIcon
) : (
<span className={`${prefixCls}-handler-down-inner`}>{controls.downIcon}</span>
);
}
const { hasFeedback, status: contextStatus } = useContext(FormItemStatusContext);
const mergedStatus = getMergedStatus(contextStatus, customStatus);
const mergeSize = customizeSize || size;
const inputNumberClass = classNames(
@ -53,6 +83,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
[`${prefixCls}-readonly`]: readOnly,
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(prefixCls, mergedStatus),
className,
);
@ -64,29 +95,35 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
downHandler={downIcon}
prefixCls={prefixCls}
readOnly={readOnly}
controls={controlsTemp}
{...others}
/>
);
if (prefix != null) {
const affixWrapperCls = classNames(`${prefixCls}-affix-wrapper`, {
[`${prefixCls}-affix-wrapper-focused`]: focused,
[`${prefixCls}-affix-wrapper-disabled`]: props.disabled,
[`${prefixCls}-affix-wrapper-sm`]: size === 'small',
[`${prefixCls}-affix-wrapper-lg`]: size === 'large',
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-affix-wrapper-readonly`]: readOnly,
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
// className will go to addon wrapper
[`${className}`]: !(addonBefore || addonAfter) && className,
});
if (prefix != null || hasFeedback) {
const affixWrapperCls = classNames(
`${prefixCls}-affix-wrapper`,
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
{
[`${prefixCls}-affix-wrapper-focused`]: focused,
[`${prefixCls}-affix-wrapper-disabled`]: props.disabled,
[`${prefixCls}-affix-wrapper-sm`]: size === 'small',
[`${prefixCls}-affix-wrapper-lg`]: size === 'large',
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-affix-wrapper-readonly`]: readOnly,
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
// className will go to addon wrapper
[`${className}`]: !(addonBefore || addonAfter) && className,
},
);
element = (
<div
className={affixWrapperCls}
style={props.style}
onMouseUp={() => inputRef.current!.focus()}
>
<span className={`${prefixCls}-prefix`}>{prefix}</span>
{prefix && <span className={`${prefixCls}-prefix`}>{prefix}</span>}
{cloneElement(element, {
style: null,
value: props.value,
@ -99,6 +136,9 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
props.onBlur?.(event);
},
})}
{hasFeedback && (
<span className={`${prefixCls}-suffix`}>{getFeedbackIcon(prefixCls, mergedStatus)}</span>
)}
</div>
);
}
@ -122,6 +162,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
[`${prefixCls}-group-wrapper-lg`]: size === 'large',
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
},
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
className,
);
element = (

View File

@ -22,7 +22,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/XOS8qZ0kU/InputNumber.svg
| addonBefore | 带标签的 input设置前置标签 | ReactNode | - | 4.17.0 |
| autoFocus | 自动获取焦点 | boolean | false | - |
| bordered | 是否有边框 | boolean | true | 4.12.0 |
| controls | 是否显示增减按钮 | boolean | true | 4.17.0 |
| controls | 是否显示增减按钮,也可设置自定义箭头图标 | boolean \| { upIcon?: React.ReactNode; downIcon?: React.ReactNode; } | - | 4.19.0 |
| decimalSeparator | 小数点 | string | - | - |
| defaultValue | 初始值 | number | - | - |
| disabled | 禁用 | boolean | false | - |
@ -33,6 +33,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/XOS8qZ0kU/InputNumber.svg
| parser | 指定从 `formatter` 里转换回数字的方式,和 `formatter` 搭配使用 | function(string): number | - | - |
| precision | 数值精度,配置 `formatter` 时会以 `formatter` 为准 | number | - | - |
| readOnly | 只读 | boolean | false | - |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| prefix | 带有前缀图标的 input | ReactNode | - | 4.17.0 |
| size | 输入框大小 | `large` \| `middle` \| `small` | - | - |
| step | 每次改变步数,可以为小数 | number \| string | 1 | - |

View File

@ -8,7 +8,7 @@
&-affix-wrapper {
.input();
// or number handler will cover form status
position: static;
position: relative;
display: inline-flex;
width: 90px;
padding: 0;
@ -49,14 +49,33 @@
visibility: hidden;
content: '\a0';
}
.@{ant-prefix}-input-number-handler-wrap {
z-index: 2;
}
}
&-prefix {
&-prefix,
&-suffix {
display: flex;
flex: none;
align-items: center;
pointer-events: none;
}
&-prefix {
margin-inline-end: @input-affix-margin;
}
&-suffix {
position: absolute;
top: 0;
right: 0;
z-index: 1;
height: 100%;
margin-right: @input-padding-horizontal-base;
margin-left: @input-affix-margin;
}
}
.@{ant-prefix}-input-number-group-wrapper .@{ant-prefix}-input-number-affix-wrapper {

View File

@ -2,6 +2,7 @@
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@import './affix';
@import './status';
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
@form-item-prefix-cls: ~'@{ant-prefix}-form-item';

View File

@ -1,2 +1,4 @@
import '../../style/index.less';
import './index.less';
// deps-lint-skip: form

View File

@ -0,0 +1,45 @@
@import '../../input/style/mixin';
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
@input-number-wrapper-cls: @input-number-prefix-cls, ~'@{input-number-prefix-cls}-affix-wrapper';
each(@input-number-wrapper-cls, {
.@{value} {
&-status-error {
.status-color(@value, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
.status-color-common(@input-number-prefix-cls, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline)
}
&-status-warning {
.status-color(@value, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
.status-color-common(@input-number-prefix-cls, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline)
}
}
});
.@{input-number-prefix-cls}-affix-wrapper {
&-status-validating {
.@{input-number-prefix-cls}-feedback-icon {
display: inline-block;
color: @primary-color;
}
}
&-status-success {
.@{input-number-prefix-cls}-feedback-icon {
color: @success-color;
animation-name: diffZoomIn1 !important;
}
}
}
.@{input-number-prefix-cls}-group-wrapper {
&-status-error {
.group-status-color(@input-number-prefix-cls, @error-color, @error-color);
}
&-status-warning {
.group-status-color(@input-number-prefix-cls, @warning-color, @warning-color);
}
}

View File

@ -1,12 +1,13 @@
import * as React from 'react';
import classNames from 'classnames';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import { tuple } from '../_util/type';
import type { InputProps } from './Input';
import classNames from 'classnames';
import * as React from 'react';
import { DirectionType } from '../config-provider';
import { SizeType } from '../config-provider/SizeContext';
import { FormItemStatusContext, FormItemStatusContextProps } from '../form/context';
import { cloneElement } from '../_util/reactNode';
import { getInputClassName, hasPrefixSuffix } from './utils';
import { getMergedStatus, getStatusClassNames, InputStatus } from '../_util/statusUtils';
import { tuple } from '../_util/type';
import type { InputProps } from './Input';
const ClearableInputType = tuple('text', 'input');
@ -40,24 +41,12 @@ export interface ClearableInputProps extends BasicProps {
addonBefore?: React.ReactNode;
addonAfter?: React.ReactNode;
triggerFocus?: () => void;
status?: InputStatus;
}
class ClearableLabeledInput extends React.Component<ClearableInputProps> {
/** @private Do Not use out of this class. We do not promise this is always keep. */
private containerRef = React.createRef<HTMLSpanElement>();
onInputMouseUp: React.MouseEventHandler = e => {
if (this.containerRef.current?.contains(e.target as Element)) {
const { triggerFocus } = this.props;
triggerFocus?.();
}
};
renderClearIcon(prefixCls: string) {
const { allowClear, value, disabled, readOnly, handleReset, suffix } = this.props;
if (!allowClear) {
return null;
}
const { value, disabled, readOnly, handleReset, suffix } = this.props;
const needClear = !disabled && !readOnly && value;
const className = `${prefixCls}-clear-icon`;
return (
@ -78,118 +67,24 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
);
}
renderSuffix(prefixCls: string) {
const { suffix, allowClear } = this.props;
if (suffix || allowClear) {
return (
<span className={`${prefixCls}-suffix`}>
{this.renderClearIcon(prefixCls)}
{suffix}
</span>
);
}
return null;
}
renderLabeledIcon(prefixCls: string, element: React.ReactElement) {
renderTextAreaWithClearIcon(
prefixCls: string,
element: React.ReactElement,
statusContext: FormItemStatusContextProps,
) {
const {
focused,
value,
prefix,
className,
size,
suffix,
disabled,
allowClear,
direction,
className,
style,
readOnly,
direction,
bordered,
hidden,
status: customStatus,
} = this.props;
if (!hasPrefixSuffix(this.props)) {
return cloneElement(element, {
value,
});
}
const suffixNode = this.renderSuffix(prefixCls);
const prefixNode = prefix ? <span className={`${prefixCls}-prefix`}>{prefix}</span> : null;
const { status: contextStatus, hasFeedback } = statusContext;
const affixWrapperCls = classNames(`${prefixCls}-affix-wrapper`, {
[`${prefixCls}-affix-wrapper-focused`]: focused,
[`${prefixCls}-affix-wrapper-disabled`]: disabled,
[`${prefixCls}-affix-wrapper-sm`]: size === 'small',
[`${prefixCls}-affix-wrapper-lg`]: size === 'large',
[`${prefixCls}-affix-wrapper-input-with-clear-btn`]: suffix && allowClear && value,
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-affix-wrapper-readonly`]: readOnly,
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
// className will go to addon wrapper
[`${className}`]: !hasAddon(this.props) && className,
});
return (
<span
ref={this.containerRef}
className={affixWrapperCls}
style={style}
onMouseUp={this.onInputMouseUp}
hidden={hidden}
>
{prefixNode}
{cloneElement(element, {
style: null,
value,
className: getInputClassName(prefixCls, bordered, size, disabled),
})}
{suffixNode}
</span>
);
}
renderInputWithLabel(prefixCls: string, labeledElement: React.ReactElement) {
const { addonBefore, addonAfter, style, size, className, direction, hidden } = this.props;
// Not wrap when there is not addons
if (!hasAddon(this.props)) {
return labeledElement;
}
const wrapperClassName = `${prefixCls}-group`;
const addonClassName = `${wrapperClassName}-addon`;
const addonBeforeNode = addonBefore ? (
<span className={addonClassName}>{addonBefore}</span>
) : null;
const addonAfterNode = addonAfter ? <span className={addonClassName}>{addonAfter}</span> : null;
const mergedWrapperClassName = classNames(`${prefixCls}-wrapper`, wrapperClassName, {
[`${wrapperClassName}-rtl`]: direction === 'rtl',
});
const mergedGroupClassName = classNames(
`${prefixCls}-group-wrapper`,
{
[`${prefixCls}-group-wrapper-sm`]: size === 'small',
[`${prefixCls}-group-wrapper-lg`]: size === 'large',
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
},
className,
);
// Need another wrapper for changing display:table to display:inline-block
// and put style prop in wrapper
return (
<span className={mergedGroupClassName} style={style} hidden={hidden}>
<span className={mergedWrapperClassName}>
{addonBeforeNode}
{cloneElement(labeledElement, { style: null })}
{addonAfterNode}
</span>
</span>
);
}
renderTextAreaWithClearIcon(prefixCls: string, element: React.ReactElement) {
const { value, allowClear, className, style, direction, bordered, hidden } = this.props;
if (!allowClear) {
return cloneElement(element, {
value,
@ -198,6 +93,11 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
const affixWrapperCls = classNames(
`${prefixCls}-affix-wrapper`,
`${prefixCls}-affix-wrapper-textarea-with-clear-btn`,
getStatusClassNames(
`${prefixCls}-affix-wrapper`,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
{
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
@ -217,11 +117,16 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
}
render() {
const { prefixCls, inputType, element } = this.props;
if (inputType === ClearableInputType[0]) {
return this.renderTextAreaWithClearIcon(prefixCls, element);
}
return this.renderInputWithLabel(prefixCls, this.renderLabeledIcon(prefixCls, element));
return (
<FormItemStatusContext.Consumer>
{statusContext => {
const { prefixCls, inputType, element } = this.props;
if (inputType === ClearableInputType[0]) {
return this.renderTextAreaWithClearIcon(prefixCls, element, statusContext);
}
}}
</FormItemStatusContext.Consumer>
);
}
}

View File

@ -1,65 +1,25 @@
import * as React from 'react';
import React, { forwardRef, useContext, useEffect, useRef } from 'react';
import RcInput, { InputProps as RcInputProps, InputRef } from 'rc-input';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import type Group from './Group';
import type Search from './Search';
import type TextArea from './TextArea';
import type Password from './Password';
import { LiteralUnion } from '../_util/type';
import ClearableLabeledInput from './ClearableLabeledInput';
import { ConfigConsumer, ConfigConsumerProps, DirectionType } from '../config-provider';
import { composeRef } from 'rc-util/lib/ref';
import SizeContext, { SizeType } from '../config-provider/SizeContext';
import {
getFeedbackIcon,
getMergedStatus,
getStatusClassNames,
InputStatus,
} from '../_util/statusUtils';
import { ConfigContext } from '../config-provider';
import { FormItemStatusContext } from '../form/context';
import { hasPrefixSuffix } from './utils';
import devWarning from '../_util/devWarning';
import { getInputClassName, hasPrefixSuffix } from './utils';
export interface InputFocusOptions extends FocusOptions {
cursor?: 'start' | 'end' | 'all';
}
export interface ShowCountProps {
formatter: (args: { count: number; maxLength?: number }) => React.ReactNode;
}
export interface InputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix' | 'type'> {
prefixCls?: string;
size?: SizeType;
// ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#%3Cinput%3E_types
type?: LiteralUnion<
| 'button'
| 'checkbox'
| 'color'
| 'date'
| 'datetime-local'
| 'email'
| 'file'
| 'hidden'
| 'image'
| 'month'
| 'number'
| 'password'
| 'radio'
| 'range'
| 'reset'
| 'search'
| 'submit'
| 'tel'
| 'text'
| 'time'
| 'url'
| 'week',
string
>;
onPressEnter?: React.KeyboardEventHandler<HTMLInputElement>;
addonBefore?: React.ReactNode;
addonAfter?: React.ReactNode;
prefix?: React.ReactNode;
suffix?: React.ReactNode;
allowClear?: boolean;
showCount?: boolean | ShowCountProps;
bordered?: boolean;
htmlSize?: number;
}
export type { InputRef };
export function fixControlledValue<T>(value: T) {
if (typeof value === 'undefined' || value === null) {
@ -150,276 +110,133 @@ export function triggerFocus(
}
}
export interface InputState {
value: any;
focused: boolean;
/** `value` from prev props */
prevValue: any;
export interface InputProps
extends Omit<
RcInputProps,
'wrapperClassName' | 'groupClassName' | 'inputClassName' | 'affixWrapperClassName' | 'clearIcon'
> {
size?: SizeType;
status?: InputStatus;
bordered?: boolean;
}
class Input extends React.Component<InputProps, InputState> {
static Group: typeof Group;
const Input = forwardRef<InputRef, InputProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
bordered = true,
status: customStatus,
size: customSize,
onBlur,
onFocus,
suffix,
...rest
} = props;
const { getPrefixCls, direction, input } = React.useContext(ConfigContext);
static Search: typeof Search;
const prefixCls = getPrefixCls('input', customizePrefixCls);
const inputRef = useRef<InputRef>(null);
static TextArea: typeof TextArea;
// ===================== Status =====================
const size = React.useContext(SizeContext);
const mergedSize = customSize || size;
static Password: typeof Password;
// ===================== Status =====================
const { status: contextStatus, hasFeedback } = useContext(FormItemStatusContext);
const mergedStatus = getMergedStatus(contextStatus, customStatus);
static defaultProps = {
type: 'text',
};
input!: HTMLInputElement;
clearableInput!: ClearableLabeledInput;
removePasswordTimeout: any;
direction: DirectionType = 'ltr';
constructor(props: InputProps) {
super(props);
const value = typeof props.value === 'undefined' ? props.defaultValue : props.value;
this.state = {
value,
focused: false,
// eslint-disable-next-line react/no-unused-state
prevValue: props.value,
};
}
static getDerivedStateFromProps(nextProps: InputProps, { prevValue }: InputState) {
const newState: Partial<InputState> = { prevValue: nextProps.value };
if (nextProps.value !== undefined || prevValue !== nextProps.value) {
newState.value = nextProps.value;
}
if (nextProps.disabled) {
newState.focused = false;
}
return newState;
}
componentDidMount() {
this.clearPasswordValueAttribute();
}
// Since polyfill `getSnapshotBeforeUpdate` need work with `componentDidUpdate`.
// We keep an empty function here.
componentDidUpdate() {}
getSnapshotBeforeUpdate(prevProps: InputProps) {
if (hasPrefixSuffix(prevProps) !== hasPrefixSuffix(this.props)) {
// ===================== Focus warning =====================
const inputHasPrefixSuffix = hasPrefixSuffix(props);
const prevHasPrefixSuffix = useRef<boolean>(inputHasPrefixSuffix);
useEffect(() => {
if (inputHasPrefixSuffix && !prevHasPrefixSuffix.current) {
devWarning(
this.input !== document.activeElement,
document.activeElement === inputRef.current?.input,
'Input',
`When Input is focused, dynamic add or remove prefix / suffix will make it lose focus caused by dom structure change. Read more: https://ant.design/components/input/#FAQ`,
);
}
return null;
}
prevHasPrefixSuffix.current = inputHasPrefixSuffix;
}, [inputHasPrefixSuffix]);
componentWillUnmount() {
if (this.removePasswordTimeout) {
clearTimeout(this.removePasswordTimeout);
}
}
focus = (option?: InputFocusOptions) => {
triggerFocus(this.input, option);
// ===================== Remove Password value =====================
const removePasswordTimeoutRef = useRef<number[]>([]);
const removePasswordTimeout = () => {
removePasswordTimeoutRef.current.push(
window.setTimeout(() => {
if (
inputRef.current?.input &&
inputRef.current?.input.getAttribute('type') === 'password' &&
inputRef.current?.input.hasAttribute('value')
) {
inputRef.current?.input.removeAttribute('value');
}
}),
);
};
blur() {
this.input.blur();
}
useEffect(() => {
removePasswordTimeout();
return () => removePasswordTimeoutRef.current.forEach(item => window.clearTimeout(item));
}, []);
setSelectionRange(start: number, end: number, direction?: 'forward' | 'backward' | 'none') {
this.input.setSelectionRange(start, end, direction);
}
select() {
this.input.select();
}
saveClearableInput = (input: ClearableLabeledInput) => {
this.clearableInput = input;
};
saveInput = (input: HTMLInputElement) => {
this.input = input;
};
onFocus: React.FocusEventHandler<HTMLInputElement> = e => {
const { onFocus } = this.props;
this.setState({ focused: true }, this.clearPasswordValueAttribute);
onFocus?.(e);
};
onBlur: React.FocusEventHandler<HTMLInputElement> = e => {
const { onBlur } = this.props;
this.setState({ focused: false }, this.clearPasswordValueAttribute);
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
removePasswordTimeout();
onBlur?.(e);
};
setValue(value: string, callback?: () => void) {
if (this.props.value === undefined) {
this.setState({ value }, callback);
} else {
callback?.();
}
}
handleReset = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
this.setValue('', () => {
this.focus();
});
resolveOnChange(this.input, e, this.props.onChange);
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
removePasswordTimeout();
onFocus?.(e);
};
renderInput = (
prefixCls: string,
size: SizeType | undefined,
bordered: boolean,
input: ConfigConsumerProps['input'] = {},
) => {
const {
className,
addonBefore,
addonAfter,
size: customizeSize,
disabled,
htmlSize,
} = this.props;
// Fix https://fb.me/react-unknown-prop
const otherProps = omit(this.props as InputProps & { inputType: any }, [
'prefixCls',
'onPressEnter',
'addonBefore',
'addonAfter',
'prefix',
'suffix',
'allowClear',
// Input elements must be either controlled or uncontrolled,
// specify either the value prop, or the defaultValue prop, but not both.
'defaultValue',
'size',
'inputType',
'bordered',
'htmlSize',
'showCount',
]);
return (
<input
autoComplete={input.autoComplete}
{...otherProps}
onChange={this.handleChange}
onFocus={this.onFocus}
onBlur={this.onBlur}
onKeyDown={this.handleKeyDown}
className={classNames(
getInputClassName(prefixCls, bordered, customizeSize || size, disabled, this.direction),
{
[className!]: className && !addonBefore && !addonAfter,
},
)}
ref={this.saveInput}
size={htmlSize}
/>
);
};
const suffixNode = (hasFeedback || suffix) && (
<>
{suffix}
{hasFeedback && getFeedbackIcon(prefixCls, mergedStatus)}
</>
);
clearPasswordValueAttribute = () => {
// https://github.com/ant-design/ant-design/issues/20541
this.removePasswordTimeout = setTimeout(() => {
if (
this.input &&
this.input.getAttribute('type') === 'password' &&
this.input.hasAttribute('value')
) {
this.input.removeAttribute('value');
}
});
};
handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setValue(e.target.value, this.clearPasswordValueAttribute);
resolveOnChange(this.input, e, this.props.onChange);
};
handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
const { onPressEnter, onKeyDown } = this.props;
if (onPressEnter && e.keyCode === 13) {
onPressEnter(e);
}
onKeyDown?.(e);
};
renderShowCountSuffix = (prefixCls: string) => {
const { value } = this.state;
const { maxLength, suffix, showCount } = this.props;
// Max length value
const hasMaxLength = Number(maxLength) > 0;
if (suffix || showCount) {
const valueLength = [...fixControlledValue(value)].length;
let dataCount = null;
if (typeof showCount === 'object') {
dataCount = showCount.formatter({ count: valueLength, maxLength });
} else {
dataCount = `${valueLength}${hasMaxLength ? ` / ${maxLength}` : ''}`;
}
return (
<>
{!!showCount && (
<span
className={classNames(`${prefixCls}-show-count-suffix`, {
[`${prefixCls}-show-count-has-suffix`]: !!suffix,
})}
>
{dataCount}
</span>
)}
{suffix}
</>
);
}
return null;
};
renderComponent = ({ getPrefixCls, direction, input }: ConfigConsumerProps) => {
const { value, focused } = this.state;
const { prefixCls: customizePrefixCls, bordered = true } = this.props;
const prefixCls = getPrefixCls('input', customizePrefixCls);
this.direction = direction;
const showCountSuffix = this.renderShowCountSuffix(prefixCls);
return (
<SizeContext.Consumer>
{size => (
<ClearableLabeledInput
size={size}
{...this.props}
prefixCls={prefixCls}
inputType="input"
value={fixControlledValue(value)}
element={this.renderInput(prefixCls, size, bordered, input)}
handleReset={this.handleReset}
ref={this.saveClearableInput}
direction={direction}
focused={focused}
triggerFocus={this.focus}
bordered={bordered}
suffix={showCountSuffix}
/>
)}
</SizeContext.Consumer>
);
};
render() {
return <ConfigConsumer>{this.renderComponent}</ConfigConsumer>;
}
}
return (
<RcInput
ref={composeRef(ref, inputRef)}
prefixCls={prefixCls}
autoComplete={input?.autoComplete}
{...rest}
onBlur={handleBlur}
onFocus={handleFocus}
suffix={suffixNode}
clearIcon={<CloseCircleFilled />}
inputClassName={classNames(
{
[`${prefixCls}-sm`]: mergedSize === 'small',
[`${prefixCls}-lg`]: mergedSize === 'large',
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(prefixCls, mergedStatus),
)}
affixWrapperClassName={classNames(
{
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
},
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
)}
wrapperClassName={classNames({
[`${prefixCls}-group-rtl`]: direction === 'rtl',
})}
groupClassName={classNames(
{
[`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small',
[`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large',
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
},
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
)}
/>
);
});
export default Input;

View File

@ -5,8 +5,8 @@ import EyeOutlined from '@ant-design/icons/EyeOutlined';
import EyeInvisibleOutlined from '@ant-design/icons/EyeInvisibleOutlined';
import { useState } from 'react';
import Input, { InputRef, InputProps } from './Input';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import Input, { InputProps } from './Input';
export interface PasswordProps extends InputProps {
readonly inputPrefixCls?: string;
@ -20,7 +20,7 @@ const ActionMap: Record<string, string> = {
hover: 'onMouseOver',
};
const Password = React.forwardRef<any, PasswordProps>((props, ref) => {
const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
const [visible, setVisible] = useState(false);
const onVisibleChange = () => {

View File

@ -2,7 +2,7 @@ import * as React from 'react';
import classNames from 'classnames';
import { composeRef } from 'rc-util/lib/ref';
import SearchOutlined from '@ant-design/icons/SearchOutlined';
import Input, { InputProps } from './Input';
import Input, { InputProps, InputRef } from './Input';
import Button from '../button';
import SizeContext from '../config-provider/SizeContext';
import { ConfigContext } from '../config-provider';
@ -21,7 +21,7 @@ export interface SearchProps extends InputProps {
loading?: boolean;
}
const Search = React.forwardRef<Input, SearchProps>((props, ref) => {
const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
inputPrefixCls: customizeInputPrefixCls,
@ -42,7 +42,7 @@ const Search = React.forwardRef<Input, SearchProps>((props, ref) => {
const size = customizeSize || contextSize;
const inputRef = React.useRef<Input>(null);
const inputRef = React.useRef<InputRef>(null);
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e && e.target && e.type === 'click' && customOnSearch) {
@ -61,7 +61,7 @@ const Search = React.forwardRef<Input, SearchProps>((props, ref) => {
const onSearch = (e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLInputElement>) => {
if (customOnSearch) {
customOnSearch(inputRef.current?.input.value!, e);
customOnSearch(inputRef.current?.input?.value!, e);
}
};
@ -129,7 +129,7 @@ const Search = React.forwardRef<Input, SearchProps>((props, ref) => {
return (
<Input
ref={composeRef<Input>(inputRef, ref)}
ref={composeRef<InputRef>(inputRef, ref)}
onPressEnter={onSearch}
{...restProps}
size={size}

View File

@ -1,13 +1,20 @@
import * as React from 'react';
import classNames from 'classnames';
import RcTextArea, { TextAreaProps as RcTextAreaProps } from 'rc-textarea';
import ResizableTextArea from 'rc-textarea/lib/ResizableTextArea';
import omit from 'rc-util/lib/omit';
import classNames from 'classnames';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import ClearableLabeledInput from './ClearableLabeledInput';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import { fixControlledValue, resolveOnChange, triggerFocus, InputFocusOptions } from './Input';
import SizeContext, { SizeType } from '../config-provider/SizeContext';
import { FormItemStatusContext } from '../form/context';
import {
getFeedbackIcon,
getStatusClassNames,
InputStatus,
getMergedStatus,
} from '../_util/statusUtils';
import ClearableLabeledInput from './ClearableLabeledInput';
import { fixControlledValue, InputFocusOptions, resolveOnChange, triggerFocus } from './Input';
interface ShowCountProps {
formatter: (args: { count: number; maxLength?: number }) => string;
@ -42,6 +49,7 @@ export interface TextAreaProps extends RcTextAreaProps {
bordered?: boolean;
showCount?: boolean | ShowCountProps;
size?: SizeType;
status?: InputStatus;
}
export interface TextAreaRef {
@ -63,6 +71,7 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
onCompositionStart,
onCompositionEnd,
onChange,
status: customStatus,
...props
},
ref,
@ -70,6 +79,9 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const size = React.useContext(SizeContext);
const { status: contextStatus, hasFeedback } = React.useContext(FormItemStatusContext);
const mergedStatus = getMergedStatus(contextStatus, customStatus);
const innerRef = React.useRef<RcTextArea>(null);
const clearableInputRef = React.useRef<ClearableLabeledInput>(null);
@ -161,12 +173,15 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
const textArea = (
<RcTextArea
{...omit(props, ['allowClear'])}
className={classNames({
[`${prefixCls}-borderless`]: !bordered,
[className!]: className && !showCount,
[`${prefixCls}-sm`]: size === 'small' || customizeSize === 'small',
[`${prefixCls}-lg`]: size === 'large' || customizeSize === 'large',
})}
className={classNames(
{
[`${prefixCls}-borderless`]: !bordered,
[className!]: className && !showCount,
[`${prefixCls}-sm`]: size === 'small' || customizeSize === 'small',
[`${prefixCls}-lg`]: size === 'large' || customizeSize === 'large',
},
getStatusClassNames(prefixCls, mergedStatus),
)}
style={showCount ? undefined : style}
prefixCls={prefixCls}
onCompositionStart={onInternalCompositionStart}
@ -195,12 +210,13 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
handleReset={handleReset}
ref={clearableInputRef}
bordered={bordered}
status={customStatus}
style={showCount ? undefined : style}
/>
);
// Only show text area wrapper when needed
if (showCount) {
if (showCount || hasFeedback) {
const valueLength = [...val].length;
let dataCount = '';
@ -217,14 +233,16 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
`${prefixCls}-textarea`,
{
[`${prefixCls}-textarea-rtl`]: direction === 'rtl',
[`${prefixCls}-textarea-show-count`]: showCount,
},
`${prefixCls}-textarea-show-count`,
getStatusClassNames(`${prefixCls}-textarea`, mergedStatus, hasFeedback),
className,
)}
style={style}
data-count={dataCount}
>
{textareaNode}
{hasFeedback && getFeedbackIcon(prefixCls, mergedStatus)}
</div>
);
}

View File

@ -25,7 +25,7 @@ describe('Input.Password', () => {
it('should support size', () => {
const wrapper = mount(<Password size="large" />);
expect(wrapper.find('input').hasClass('ant-input-lg')).toBe(true);
expect(wrapper.find('.ant-input-affix-wrapper-lg')).toBeTruthy();
expect(wrapper.render()).toMatchSnapshot();
});

View File

@ -2,7 +2,7 @@
exports[`Input.Password rtl render component should be rendered correctly in RTL direction 1`] = `
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-rtl ant-input-password"
class="ant-input-affix-wrapper ant-input-password ant-input-affix-wrapper-rtl"
>
<input
action="click"
@ -159,11 +159,11 @@ exports[`Input.Password should change type when click 3`] = `
exports[`Input.Password should support size 1`] = `
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-lg ant-input-password ant-input-password-large"
class="ant-input-affix-wrapper ant-input-password ant-input-password-large ant-input-affix-wrapper-lg"
>
<input
action="click"
class="ant-input ant-input-lg"
class="ant-input"
type="password"
value=""
/>

View File

@ -2,7 +2,7 @@
exports[`Input.Search rtl render component should be rendered correctly in RTL direction 1`] = `
<span
class="ant-input-group-wrapper ant-input-group-wrapper-rtl ant-input-search ant-input-search-rtl"
class="ant-input-group-wrapper ant-input-search ant-input-search-rtl ant-input-group-wrapper-rtl"
>
<span
class="ant-input-wrapper ant-input-group ant-input-group-rtl"

View File

@ -4857,24 +4857,29 @@ Array [
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>,
@ -5004,7 +5009,7 @@ exports[`renders ./components/input/demo/borderless-debug.md extend context corr
class="ant-input-affix-wrapper ant-input-affix-wrapper-borderless"
>
<input
class="ant-input ant-input-borderless"
class="ant-input"
placeholder="Unbordered"
type="text"
value=""
@ -5013,24 +5018,29 @@ exports[`renders ./components/input/demo/borderless-debug.md extend context corr
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -5043,7 +5053,7 @@ exports[`renders ./components/input/demo/borderless-debug.md extend context corr
</span>
<input
class="ant-input ant-input-borderless"
class="ant-input"
type="text"
value=""
/>
@ -5062,7 +5072,7 @@ exports[`renders ./components/input/demo/borderless-debug.md extend context corr
</span>
<input
class="ant-input ant-input-disabled ant-input-borderless"
class="ant-input ant-input-disabled"
disabled=""
type="text"
value=""
@ -5485,24 +5495,29 @@ exports[`renders ./components/input/demo/group.md extend context correctly 1`] =
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon"
class="ant-input-clear-icon"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -5555,24 +5570,29 @@ exports[`renders ./components/input/demo/group.md extend context correctly 1`] =
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon"
class="ant-input-clear-icon"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -8937,24 +8957,29 @@ exports[`renders ./components/input/demo/search-input.md extend context correctl
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -9018,24 +9043,29 @@ exports[`renders ./components/input/demo/search-input.md extend context correctl
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -9122,7 +9152,7 @@ exports[`renders ./components/input/demo/search-input.md extend context correctl
style="margin-bottom:8px"
>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-input-search-with-button"
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-input-search-with-button ant-input-group-wrapper-lg"
>
<span
class="ant-input-wrapper ant-input-group"
@ -9131,7 +9161,7 @@ exports[`renders ./components/input/demo/search-input.md extend context correctl
class="ant-input-affix-wrapper ant-input-affix-wrapper-lg"
>
<input
class="ant-input ant-input-lg"
class="ant-input"
placeholder="input search text"
type="text"
value=""
@ -9140,24 +9170,29 @@ exports[`renders ./components/input/demo/search-input.md extend context correctl
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -9180,7 +9215,7 @@ exports[`renders ./components/input/demo/search-input.md extend context correctl
class="ant-space-item"
>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-input-search-with-button"
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-input-search-with-button ant-input-group-wrapper-lg"
>
<span
class="ant-input-wrapper ant-input-group"
@ -9189,7 +9224,7 @@ exports[`renders ./components/input/demo/search-input.md extend context correctl
class="ant-input-affix-wrapper ant-input-affix-wrapper-lg"
>
<input
class="ant-input ant-input-lg"
class="ant-input"
placeholder="input search text"
type="text"
value=""
@ -9336,7 +9371,7 @@ Array [
<br />,
<br />,
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-input-search-with-button"
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-input-search-with-button ant-input-group-wrapper-lg"
>
<span
class="ant-input-wrapper ant-input-group"
@ -9450,7 +9485,7 @@ Array [
</span>
</span>
<input
class="ant-input ant-input-lg"
class="ant-input"
placeholder="large size"
type="text"
value=""
@ -9520,7 +9555,7 @@ Array [
</span>
</span>
<input
class="ant-input ant-input-sm"
class="ant-input"
placeholder="small size"
type="text"
value=""
@ -9529,6 +9564,117 @@ Array [
]
`;
exports[`renders ./components/input/demo/status.md extend context correctly 1`] = `
<div
class="ant-space ant-space-vertical"
style="width:100%"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<input
class="ant-input ant-input-status-error"
placeholder="Error"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<input
class="ant-input ant-input-status-warning"
placeholder="Warning"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-status-error"
>
<span
class="ant-input-prefix"
>
<span
aria-label="clock-circle"
class="anticon anticon-clock-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="clock-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
/>
</svg>
</span>
</span>
<input
class="ant-input"
placeholder="Error with prefix"
type="text"
value=""
/>
</span>
</div>
<div
class="ant-space-item"
>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-status-warning"
>
<span
class="ant-input-prefix"
>
<span
aria-label="clock-circle"
class="anticon anticon-clock-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="clock-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
/>
</svg>
</span>
</span>
<input
class="ant-input"
placeholder="Warning with prefix"
type="text"
value=""
/>
</span>
</div>
</div>
`;
exports[`renders ./components/input/demo/textarea.md extend context correctly 1`] = `
Array [
<textarea

View File

@ -1074,24 +1074,29 @@ Array [
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>,
@ -1221,7 +1226,7 @@ exports[`renders ./components/input/demo/borderless-debug.md correctly 1`] = `
class="ant-input-affix-wrapper ant-input-affix-wrapper-borderless"
>
<input
class="ant-input ant-input-borderless"
class="ant-input"
placeholder="Unbordered"
type="text"
value=""
@ -1230,24 +1235,29 @@ exports[`renders ./components/input/demo/borderless-debug.md correctly 1`] = `
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -1260,7 +1270,7 @@ exports[`renders ./components/input/demo/borderless-debug.md correctly 1`] = `
</span>
<input
class="ant-input ant-input-borderless"
class="ant-input"
type="text"
value=""
/>
@ -1279,7 +1289,7 @@ exports[`renders ./components/input/demo/borderless-debug.md correctly 1`] = `
</span>
<input
class="ant-input ant-input-disabled ant-input-borderless"
class="ant-input ant-input-disabled"
disabled=""
type="text"
value=""
@ -1596,24 +1606,29 @@ exports[`renders ./components/input/demo/group.md correctly 1`] = `
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon"
class="ant-input-clear-icon"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -1666,24 +1681,29 @@ exports[`renders ./components/input/demo/group.md correctly 1`] = `
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon"
class="ant-input-clear-icon"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -2711,24 +2731,29 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -2792,24 +2817,29 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -2896,7 +2926,7 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
style="margin-bottom:8px"
>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-input-search-with-button"
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-input-search-with-button ant-input-group-wrapper-lg"
>
<span
class="ant-input-wrapper ant-input-group"
@ -2905,7 +2935,7 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
class="ant-input-affix-wrapper ant-input-affix-wrapper-lg"
>
<input
class="ant-input ant-input-lg"
class="ant-input"
placeholder="input search text"
type="text"
value=""
@ -2914,24 +2944,29 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -2954,7 +2989,7 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
class="ant-space-item"
>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-input-search-with-button"
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-input-search-with-button ant-input-group-wrapper-lg"
>
<span
class="ant-input-wrapper ant-input-group"
@ -2963,7 +2998,7 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
class="ant-input-affix-wrapper ant-input-affix-wrapper-lg"
>
<input
class="ant-input ant-input-lg"
class="ant-input"
placeholder="input search text"
type="text"
value=""
@ -3110,7 +3145,7 @@ Array [
<br />,
<br />,
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-input-search-with-button"
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-input-search-with-button ant-input-group-wrapper-lg"
>
<span
class="ant-input-wrapper ant-input-group"
@ -3224,7 +3259,7 @@ Array [
</span>
</span>
<input
class="ant-input ant-input-lg"
class="ant-input"
placeholder="large size"
type="text"
value=""
@ -3294,7 +3329,7 @@ Array [
</span>
</span>
<input
class="ant-input ant-input-sm"
class="ant-input"
placeholder="small size"
type="text"
value=""
@ -3303,6 +3338,117 @@ Array [
]
`;
exports[`renders ./components/input/demo/status.md correctly 1`] = `
<div
class="ant-space ant-space-vertical"
style="width:100%"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<input
class="ant-input ant-input-status-error"
placeholder="Error"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<input
class="ant-input ant-input-status-warning"
placeholder="Warning"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-status-error"
>
<span
class="ant-input-prefix"
>
<span
aria-label="clock-circle"
class="anticon anticon-clock-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="clock-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
/>
</svg>
</span>
</span>
<input
class="ant-input"
placeholder="Error with prefix"
type="text"
value=""
/>
</span>
</div>
<div
class="ant-space-item"
>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-status-warning"
>
<span
class="ant-input-prefix"
>
<span
aria-label="clock-circle"
class="anticon anticon-clock-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="clock-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
/>
</svg>
</span>
</span>
<input
class="ant-input"
placeholder="Warning with prefix"
type="text"
value=""
/>
</span>
</div>
</div>
`;
exports[`renders ./components/input/demo/textarea.md correctly 1`] = `
Array [
<textarea

View File

@ -13,24 +13,29 @@ exports[`Input allowClear should change type when click 1`] = `
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon"
class="ant-input-clear-icon"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -49,24 +54,29 @@ exports[`Input allowClear should change type when click 2`] = `
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -85,24 +95,29 @@ exports[`Input allowClear should not show icon if defaultValue is undefined, nul
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -121,24 +136,29 @@ exports[`Input allowClear should not show icon if defaultValue is undefined, nul
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -157,24 +177,29 @@ exports[`Input allowClear should not show icon if defaultValue is undefined, nul
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -193,24 +218,29 @@ exports[`Input allowClear should not show icon if value is undefined, null or em
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -229,24 +259,29 @@ exports[`Input allowClear should not show icon if value is undefined, null or em
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>
@ -265,24 +300,29 @@ exports[`Input allowClear should not show icon if value is undefined, null or em
class="ant-input-suffix"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
class="ant-input-clear-icon ant-input-clear-icon-hidden"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span>

View File

@ -1,6 +1,7 @@
import React from 'react';
import { mount } from 'enzyme';
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
import { InputRef } from '../Input';
import Input from '..';
const { TextArea } = Input;
@ -30,8 +31,8 @@ describe('Input.Focus', () => {
});
it('start', () => {
const ref = React.createRef<Input>();
mount(<Input ref={ref} defaultValue="light" />);
const ref = React.createRef<InputRef>();
mount(<TextArea ref={ref} defaultValue="light" />);
ref.current!.focus({ cursor: 'start' });
expect(focus).toHaveBeenCalled();
@ -39,8 +40,8 @@ describe('Input.Focus', () => {
});
it('end', () => {
const ref = React.createRef<Input>();
mount(<Input ref={ref} defaultValue="light" />);
const ref = React.createRef<InputRef>();
mount(<TextArea ref={ref} defaultValue="light" />);
ref.current!.focus({ cursor: 'end' });
expect(focus).toHaveBeenCalled();
@ -62,6 +63,7 @@ describe('Input.Focus', () => {
expect(wrapper.exists('.ant-input-affix-wrapper-focused')).toBeTruthy();
wrapper.setProps({ disabled: true });
wrapper.update();
expect(wrapper.exists('.ant-input-affix-wrapper-focused')).toBeFalsy();
});
});

View File

@ -3,7 +3,6 @@ import { mount } from 'enzyme';
// eslint-disable-next-line import/no-unresolved
import Form from '../../form';
import Input from '..';
import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -18,7 +17,6 @@ describe('Input', () => {
errorSpy.mockRestore();
});
focusTest(Input);
mountTest(Input);
mountTest(Input.Group);
@ -31,8 +29,9 @@ describe('Input', () => {
});
it('select()', () => {
const wrapper = mount(<Input />);
wrapper.instance().select();
const ref = React.createRef();
mount(<Input ref={ref} />);
ref.current?.select();
});
it('should support size', () => {
@ -63,8 +62,8 @@ describe('Input', () => {
expect(errorSpy).not.toHaveBeenCalled();
});
it('trigger warning', () => {
const wrapper = mount(<Input />, { attachTo: document.body });
wrapper.find('input').instance().focus();
const wrapper = mount(<Input />);
wrapper.find('input').first().getDOMNode().focus();
wrapper.setProps({
suffix: 'light',
});
@ -78,10 +77,11 @@ describe('Input', () => {
it('set mouse cursor position', () => {
const defaultValue = '11111';
const valLength = defaultValue.length;
const wrapper = mount(<Input autoFocus defaultValue={defaultValue} />);
wrapper.instance().setSelectionRange(valLength, valLength);
expect(wrapper.instance().input.selectionStart).toEqual(5);
expect(wrapper.instance().input.selectionEnd).toEqual(5);
const ref = React.createRef();
const wrapper = mount(<Input ref={ref} autoFocus defaultValue={defaultValue} />);
ref.current?.setSelectionRange(valLength, valLength);
expect(wrapper.find('input').first().getDOMNode().selectionStart).toEqual(5);
expect(wrapper.find('input').first().getDOMNode().selectionEnd).toEqual(5);
});
});

View File

@ -486,4 +486,27 @@ describe('TextArea allowClear', () => {
wrapper.unmount();
});
// https://github.com/ant-design/ant-design/issues/31200
it('should not lost focus when clear input', () => {
const onBlur = jest.fn();
const wrapper = mount(<TextArea allowClear defaultValue="value" onBlur={onBlur} />, {
attachTo: document.body,
});
wrapper.find('textarea').getDOMNode().focus();
wrapper.find('.ant-input-clear-icon').at(0).simulate('mouseDown');
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
wrapper.find('.ant-input-clear-icon').at(0).simulate('mouseUp');
wrapper.find('.ant-input-clear-icon').at(0).simulate('focus');
wrapper.find('.ant-input-clear-icon').at(0).getDOMNode().click();
expect(onBlur).not.toBeCalled();
wrapper.unmount();
});
it('should focus text area after clear', () => {
const wrapper = mount(<TextArea allowClear defaultValue="111" />, { attachTo: document.body });
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
expect(document.activeElement).toBe(wrapper.find('textarea').at(0).getDOMNode());
wrapper.unmount();
});
});

View File

@ -0,0 +1,31 @@
---
order: 19
version: 4.19.0
title:
zh-CN: 自定义状态
en-US: Status
---
## zh-CN
使用 `status` 为 Input 添加状态,可选 `error` 或者 `warning`
## en-US
Add status to Input with `status`, which could be `error` or `warning`.
```tsx
import { Input, Space } from 'antd';
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
const ValidateInputs: React.FC = () => (
<Space direction="vertical" style={{ width: '100%' }}>
<Input status="error" placeholder="Error" />
<Input status="warning" placeholder="Warning" />
<Input status="error" prefix={<ClockCircleOutlined />} placeholder="Error with prefix" />
<Input status="warning" prefix={<ClockCircleOutlined />} placeholder="Warning with prefix" />
</Space>
);
ReactDOM.render(<ValidateInputs />, mountNode);
```

View File

@ -27,6 +27,7 @@ A basic widget for getting the user input is a text field. Keyboard and mouse ca
| id | The ID for input | string | - | |
| maxLength | The max length | number | - | |
| showCount | Whether show text count | boolean \| { formatter: ({ count: number, maxLength?: number }) => ReactNode } | false | 4.18.0 |
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| prefix | The prefix icon for the Input | ReactNode | - | |
| size | The size of the input box. Note: in the context of a form, the `large` size is used | `large` \| `middle` \| `small` | - | |
| suffix | The suffix icon for the Input | ReactNode | - | |

View File

@ -1,15 +1,26 @@
import Input from './Input';
import * as React from 'react';
import InternalInput, { InputProps, InputRef } from './Input';
import Group from './Group';
import Search from './Search';
import TextArea from './TextArea';
import Password from './Password';
export { InputProps } from './Input';
export { InputProps, InputRef } from './Input';
export { GroupProps } from './Group';
export { SearchProps } from './Search';
export { TextAreaProps } from './TextArea';
export { PasswordProps } from './Password';
interface CompoundedComponent
extends React.ForwardRefExoticComponent<InputProps & React.RefAttributes<InputRef>> {
Group: typeof Group;
Search: typeof Search;
TextArea: typeof TextArea;
Password: typeof Password;
}
const Input = InternalInput as CompoundedComponent;
Input.Group = Group;
Input.Search = Search;
Input.TextArea = TextArea;

View File

@ -28,6 +28,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/xS9YEJhfe/Input.svg
| id | 输入框的 id | string | - | |
| maxLength | 最大长度 | number | - | |
| showCount | 是否展示字数 | boolean \| { formatter: ({ count: number, maxLength?: number }) => ReactNode } | false | 4.18.0 |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| prefix | 带有前缀图标的 input | ReactNode | - | |
| size | 控件大小。注:标准表单内的输入框大小限制为 `large` | `large` \| `middle` \| `small` | - | |
| suffix | 带有后缀图标的 input | ReactNode | - | |

Some files were not shown because too many files have changed in this diff Show More