Merge pull request #24537 from ant-design/master-to-merge-feature

chore: Master to merge feature
This commit is contained in:
二货机器人 2020-05-28 16:49:21 +08:00 committed by GitHub
commit 7c77a764e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
155 changed files with 15571 additions and 2563 deletions

View File

@ -0,0 +1,12 @@
import React from 'react';
import { mount } from 'enzyme';
import TransButton from '../transButton';
describe('transButton component', () => {
it('disabled should update style', () => {
const wrapper = mount(<TransButton disabled />);
expect(wrapper.find('div').first().props().style).toEqual(
expect.objectContaining({ pointerEvents: 'none' }),
);
});
});

View File

@ -18,24 +18,42 @@ describe('Wave component', () => {
it('isHidden works', () => {
const TEST_NODE_ENV = process.env.NODE_ENV;
process.env.NODE_ENV = 'development';
const wrapper = mount(<Wave><button type="button">button</button></Wave>);
const wrapper = mount(
<Wave>
<button type="button">button</button>
</Wave>,
);
expect(wrapper.find('button').getDOMNode().className).toBe('');
wrapper.find('button').getDOMNode().click();
expect(wrapper.find('button').getDOMNode().hasAttribute('ant-click-animating-without-extra-node')).toBe(false);
expect(
wrapper.find('button').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
).toBe(false);
wrapper.unmount();
process.env.NODE_ENV = TEST_NODE_ENV;
});
it('isHidden is mocked', () => {
const wrapper = mount(<Wave><button type="button">button</button></Wave>);
const wrapper = mount(
<Wave>
<button type="button">button</button>
</Wave>,
);
expect(wrapper.find('button').getDOMNode().className).toBe('');
wrapper.find('button').getDOMNode().click();
expect(wrapper.find('button').getDOMNode().getAttribute('ant-click-animating-without-extra-node')).toBe('false');
expect(
wrapper.find('button').getDOMNode().getAttribute('ant-click-animating-without-extra-node'),
).toBe('false');
wrapper.unmount();
});
it('wave color is grey', async () => {
const wrapper = mount(<Wave><button type="button" style={{ borderColor: 'rgb(0, 0, 0)' }}>button</button></Wave>);
const wrapper = mount(
<Wave>
<button type="button" style={{ borderColor: 'rgb(0, 0, 0)' }}>
button
</button>
</Wave>,
);
wrapper.find('button').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
@ -44,7 +62,13 @@ describe('Wave component', () => {
});
it('wave color is not grey', async () => {
const wrapper = mount(<Wave><button type="button" style={{ borderColor: 'red' }}>button</button></Wave>);
const wrapper = mount(
<Wave>
<button type="button" style={{ borderColor: 'red' }}>
button
</button>
</Wave>,
);
wrapper.find('button').getDOMNode().click();
await sleep(200);
const styles = document.getElementsByTagName('style');
@ -54,7 +78,11 @@ describe('Wave component', () => {
});
it('read wave color from border-top-color', async () => {
const wrapper = mount(<Wave><div style={{ borderTopColor: 'blue' }}>button</div></Wave>);
const wrapper = mount(
<Wave>
<div style={{ borderTopColor: 'blue' }}>button</div>
</Wave>,
);
wrapper.find('div').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
@ -64,7 +92,11 @@ describe('Wave component', () => {
});
it('read wave color from background color', async () => {
const wrapper = mount(<Wave><div style={{ backgroundColor: 'green' }}>button</div></Wave>);
const wrapper = mount(
<Wave>
<div style={{ backgroundColor: 'green' }}>button</div>
</Wave>,
);
wrapper.find('div').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
@ -74,7 +106,11 @@ describe('Wave component', () => {
});
it('read wave color from border firstly', async () => {
const wrapper = mount(<Wave><div style={{ borderColor: 'yellow', backgroundColor: 'green' }}>button</div></Wave>);
const wrapper = mount(
<Wave>
<div style={{ borderColor: 'yellow', backgroundColor: 'green' }}>button</div>
</Wave>,
);
wrapper.find('div').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
@ -84,7 +120,13 @@ describe('Wave component', () => {
});
it('hidden element with -leave className', async () => {
const wrapper = mount(<Wave><button type="button" className="xx-leave">button</button></Wave>);
const wrapper = mount(
<Wave>
<button type="button" className="xx-leave">
button
</button>
</Wave>,
);
wrapper.find('button').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
@ -95,7 +137,9 @@ describe('Wave component', () => {
it('ConfigProvider csp', async () => {
const wrapper = mount(
<ConfigProvider csp={{ nonce: 'YourNonceCode' }}>
<Wave><button type="button">button</button></Wave>
<Wave>
<button type="button">button</button>
</Wave>
</ConfigProvider>,
);
wrapper.find('button').getDOMNode().click();

View File

@ -9,6 +9,7 @@ interface TransButtonProps extends React.HTMLAttributes<HTMLDivElement> {
onClick?: (e?: React.MouseEvent<HTMLDivElement>) => void;
noStyle?: boolean;
autoFocus?: boolean;
disabled?: boolean;
}
const inlineStyle: React.CSSProperties = {
@ -63,7 +64,24 @@ class TransButton extends React.Component<TransButtonProps> {
}
render() {
const { style, noStyle, ...restProps } = this.props;
const { style, noStyle, disabled, ...restProps } = this.props;
let mergedStyle: React.CSSProperties = {};
if (!noStyle) {
mergedStyle = {
...inlineStyle,
};
}
if (disabled) {
mergedStyle.pointerEvents = 'none';
}
mergedStyle = {
...mergedStyle,
...style,
};
return (
<div
@ -73,7 +91,7 @@ class TransButton extends React.Component<TransButtonProps> {
{...restProps}
onKeyDown={this.onKeyDown}
onKeyUp={this.onKeyUp}
style={{ ...(!noStyle ? inlineStyle : null), ...style }}
style={mergedStyle}
/>
);
}

View File

@ -40,19 +40,10 @@ describe('Avatar Render', () => {
global.document.body.appendChild(div);
const wrapper = mount(<Avatar src="http://error.url">Fallback</Avatar>, { attachTo: div });
wrapper.instance().setScale = jest.fn(() => {
if (wrapper.state().scale === 0.5) {
return;
}
wrapper.instance().setState({ scale: 0.5 });
});
wrapper.find('img').simulate('error');
const children = wrapper.find('.ant-avatar-string');
expect(children.length).toBe(1);
expect(children.text()).toBe('Fallback');
expect(wrapper.instance().setScale).toHaveBeenCalled();
expect(div.querySelector('.ant-avatar-string').style.transform).toContain('scale(0.5)');
wrapper.detach();
global.document.body.removeChild(div);
@ -136,6 +127,11 @@ describe('Avatar Render', () => {
expect(wrapper.state().scale).toBe(0.32);
});
it('should calculate scale of avatar children correctly with gap', () => {
const wrapper = mount(<Avatar gap={2}>Avatar</Avatar>);
expect(wrapper.state().scale).toBe(0.36);
});
it('should warning when pass a string as icon props', () => {
const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
mount(<Avatar size={64} icon="aa" />);

View File

@ -454,7 +454,16 @@ Array [
type="button"
>
<span>
Change
ChangeUser
</span>
</button>,
<button
class="ant-btn ant-btn-sm"
style="vertical-align:middle"
type="button"
>
<span>
changeGap
</span>
</button>,
]

View File

@ -7,11 +7,11 @@ title:
## zh-CN
对于字符型的头像,当字符串较长时,字体大小可以根据头像宽度自动调整。
对于字符型的头像,当字符串较长时,字体大小可以根据头像宽度自动调整。也可使用 `gap` 来设置字符距离左右两侧边界单位像素。
## en-US
For letter type Avatar, when the letters are too long to display, the font size can be automatically adjusted according to the width of the Avatar.
For letter type Avatar, when the letters are too long to display, the font size can be automatically adjusted according to the width of the Avatar. You can also use `gap` to set the unit distance between left and right sides.
```tsx
import React, { useState } from 'react';
@ -19,18 +19,24 @@ import { Avatar, Button } from 'antd';
const UserList = ['U', 'Lucy', 'Tom', 'Edward'];
const ColorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae'];
const GapList = [4, 3, 2, 1];
const Autoset: React.FC = () => {
const [user, setUser] = useState(UserList[0]);
const [color, setColor] = useState(ColorList[0]);
const [gap, setGap] = useState(GapList[0]);
const changeUser = () => {
const index = UserList.indexOf(user);
setUser(index < UserList.length - 1 ? UserList[index + 1] : UserList[0]);
setColor(index < ColorList.length - 1 ? ColorList[index + 1] : ColorList[0]);
};
const changeGap = () => {
const index = GapList.indexOf(gap);
setGap(index < GapList.length - 1 ? GapList[index + 1] : GapList[0]);
};
return (
<>
<Avatar style={{ backgroundColor: color, verticalAlign: 'middle' }} size="large">
<Avatar style={{ backgroundColor: color, verticalAlign: 'middle' }} size="large" gap={gap}>
{user}
</Avatar>
<Button
@ -38,7 +44,10 @@ const Autoset: React.FC = () => {
style={{ margin: '0 16px', verticalAlign: 'middle' }}
onClick={changeUser}
>
Change
ChangeUser
</Button>
<Button size="small" style={{ verticalAlign: 'middle' }} onClick={changeGap}>
changeGap
</Button>
</>
);

View File

@ -17,3 +17,4 @@ Avatars can be used to represent people or objects. It supports images, `Icon`s,
| srcSet | a list of sources to use for different screen resolutions | string | - | |
| alt | This attribute defines the alternative text describing the image | string | - | |
| onError | handler when img load error, return false to prevent default fallback behavior | () => boolean | - | |
| gap | Letter type unit distance between left and right sides | number | 4 | 4.3.0 |

View File

@ -12,6 +12,7 @@ export interface AvatarProps {
* or a custom number size
* */
size?: 'large' | 'small' | 'default' | number;
gap?: number;
/** Src of image avatar */
src?: string;
/** Srcset of image avatar */
@ -61,10 +62,12 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
}
componentDidUpdate(prevProps: AvatarProps) {
this.setScale();
if (prevProps.src !== this.props.src) {
this.setState({ isImgExist: true, scale: 1 });
}
if (prevProps.children !== this.props.children || prevProps.gap !== this.props.gap) {
this.setScale();
}
}
setScale = () => {
@ -73,20 +76,22 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
}
const childrenWidth = this.avatarChildren.offsetWidth; // offsetWidth avoid affecting be transform scale
const nodeWidth = this.avatarNode.offsetWidth;
const { gap = 4 } = this.props;
// denominator is 0 is no meaning
if (
childrenWidth === 0 ||
nodeWidth === 0 ||
(this.lastChildrenWidth === childrenWidth && this.lastNodeWidth === nodeWidth)
childrenWidth !== 0 &&
nodeWidth !== 0 &&
(this.lastChildrenWidth !== childrenWidth || this.lastNodeWidth !== nodeWidth)
) {
return;
this.lastChildrenWidth = childrenWidth;
this.lastNodeWidth = nodeWidth;
}
if (gap * 2 < nodeWidth) {
this.setState({
scale: nodeWidth - gap * 2 < childrenWidth ? (nodeWidth - gap * 2) / childrenWidth : 1,
});
}
this.lastChildrenWidth = childrenWidth;
this.lastNodeWidth = nodeWidth;
// add 4px gap for each side to get better performance
this.setState({
scale: nodeWidth - 8 < childrenWidth ? (nodeWidth - 8) / childrenWidth : 1,
});
};
handleImgLoadError = () => {
@ -204,7 +209,6 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
return (
<span
{...others}
style={{ ...sizeStyle, ...others.style }}
className={classString}
ref={(node: HTMLElement) => (this.avatarNode = node)}

View File

@ -22,3 +22,4 @@ title: Avatar
| srcSet | 设置图片类头像响应式资源地址 | string | - | |
| alt | 图像无法显示时的替代文本 | string | - | |
| onError | 图片加载失败的事件,返回 false 会关闭组件默认的 fallback 行为 | () => boolean | - | |
| gap | 字符类型距离左右两侧边界单位像素 | number | 4 | 4.3.0 |

View File

@ -3,6 +3,7 @@ import Animate from 'rc-animate';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import classNames from 'classnames';
import omit from 'omit.js';
import VerticalAlignTopOutlined from '@ant-design/icons/VerticalAlignTopOutlined';
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
import { ConfigContext } from '../config-provider';
import getScroll from '../_util/getScroll';
@ -80,7 +81,9 @@ const BackTop: React.FC<BackTopProps> = props => {
const { children } = props;
const defaultElement = (
<div className={`${prefixCls}-content`}>
<div className={`${prefixCls}-icon`} />
<div className={`${prefixCls}-icon`}>
<VerticalAlignTopOutlined />
</div>
</div>
);
return (

View File

@ -37,11 +37,8 @@
}
&-icon {
width: 14px;
height: 16px;
margin: 12px auto;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAoCAYAAACWwljjAAAABGdBTUEAALGPC/xhBQAAAbtJREFUWAntmMtKw0AUhhMvS5cuxILgQlRUpIggIoKIIoigG1eC+AA+jo+i6FIXBfeuXIgoeKVeitVWJX5HWhhDksnUpp3FDPyZk3Nm5nycmZKkXhAEOXSA3lG7muTeRzmfy6HneUvIhnYkQK+Q9NhAA0Opg0vBEhjBKHiyb8iGMyQMOYuK41BcBSypAL+MYXSKjtFAW7EAGEO3qN4uMQbbAkXiSfRQJ1H6a+yhlkKRcAoVFYiweYNjtCVQJJpBz2GCiPt7fBOZQpFgDpUikse5HgnkM4Fi4QX0Fpc5wf9EbLqpUCy4jMoJSXWhFwbMNgWKhVbRhy5jirhs9fy/oFhgHVVTJEs7RLZ8sSEoJm6iz7SZDMbJ+/OKERQTttCXQRLToRUmrKWCYuA2+jbN0MB4OQobYShfdTCgn/sL1K36M7TLrN3n+758aPy2rrpR6+/od5E8tf/A1uLS9aId5T7J3CNYihkQ4D9PiMdMC7mp4rjB9kjFjZp8BlnVHJBuO1yFXIV0FdDF3RlyFdJVQBdv5AxVdIsq8apiZ2PyYO1EVykesGfZEESsCkweyR8MUW+V8uJ1gkYipmpdP1pm2aJVPEGzAAAAAElFTkSuQmCC)
~'100%/100%' no-repeat;
font-size: 24px;
line-height: 40px;
}
}

View File

@ -485,12 +485,17 @@ exports[`renders ./components/badge/demo/change.md correctly 1`] = `
`;
exports[`renders ./components/badge/demo/colorful.md correctly 1`] = `
<div>
<h4
style="margin-bottom:16px"
Array [
<div
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-left"
role="separator"
>
Presets:
</h4>
<span
class="ant-divider-inner-text"
>
Presets
</span>
</div>,
<div>
<div>
<span
@ -674,12 +679,17 @@ exports[`renders ./components/badge/demo/colorful.md correctly 1`] = `
</span>
</span>
</div>
</div>
<h4
style="margin:16px 0"
</div>,
<div
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-left"
role="separator"
>
Custom:
</h4>
<span
class="ant-divider-inner-text"
>
Custom
</span>
</div>,
<div>
<span
class="ant-badge ant-badge-status ant-badge-not-a-wrapper"
@ -736,8 +746,8 @@ exports[`renders ./components/badge/demo/colorful.md correctly 1`] = `
#108ee9
</span>
</span>
</div>
</div>
</div>,
]
`;
exports[`renders ./components/badge/demo/dot.md correctly 1`] = `

View File

@ -14,7 +14,7 @@ title:
We preset a series of colorful Badge styles for use in different situations. You can also set it to a hex color string for custom color.
```jsx
import { Badge } from 'antd';
import { Badge, Divider } from 'antd';
const colors = [
'pink',
@ -33,8 +33,8 @@ const colors = [
];
ReactDOM.render(
<div>
<h4 style={{ marginBottom: 16 }}>Presets:</h4>
<>
<Divider orientation="left">Presets</Divider>
<div>
{colors.map(color => (
<div key={color}>
@ -42,7 +42,7 @@ ReactDOM.render(
</div>
))}
</div>
<h4 style={{ margin: '16px 0' }}>Custom:</h4>
<Divider orientation="left">Custom</Divider>
<div>
<Badge color="#f50" text="#f50" />
<br />
@ -52,7 +52,7 @@ ReactDOM.render(
<br />
<Badge color="#108ee9" text="#108ee9" />
</div>
</div>,
</>,
mountNode,
);
```

View File

@ -1,40 +1,49 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/button/demo/basic.md correctly 1`] = `
<div>
Array [
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
Primary Button
</span>
</button>
</button>,
<button
class="ant-btn"
type="button"
>
<span>
Default
Default Button
</span>
</button>
</button>,
<button
class="ant-btn ant-btn-dashed"
type="button"
>
<span>
Dashed
Dashed Button
</span>
</button>
</button>,
<br />,
<button
class="ant-btn ant-btn-text"
type="button"
>
<span>
Text Button
</span>
</button>,
<button
class="ant-btn ant-btn-link"
type="button"
>
<span>
Link
Link Button
</span>
</button>
</div>
</button>,
]
`;
exports[`renders ./components/button/demo/block.md correctly 1`] = `
@ -185,6 +194,24 @@ exports[`renders ./components/button/demo/disabled.md correctly 1`] = `
</span>
</button>
<br />
<button
class="ant-btn ant-btn-text"
type="button"
>
<span>
Text
</span>
</button>
<button
class="ant-btn ant-btn-text"
disabled=""
type="button"
>
<span>
Text(disabled)
</span>
</button>
<br />
<button
class="ant-btn ant-btn-link ant-btn-dangerous"
type="button"
@ -272,6 +299,14 @@ exports[`renders ./components/button/demo/ghost.md correctly 1`] = `
link
</span>
</button>
<button
class="ant-btn ant-btn-text ant-btn-background-ghost"
type="button"
>
<span>
Text Button
</span>
</button>
<button
class="ant-btn ant-btn-link ant-btn-background-ghost"
type="button"

View File

@ -268,18 +268,6 @@ exports[`Button rtl render component should be rendered correctly in RTL directi
/>
`;
exports[`Button should has click wave effect 1`] = `
<button
ant-click-animating-without-extra-node="true"
class="ant-btn ant-btn-primary"
type="button"
>
<span>
button
</span>
</button>
`;
exports[`Button should merge text if children using variable 1`] = `
<button
class="ant-btn"
@ -291,17 +279,6 @@ exports[`Button should merge text if children using variable 1`] = `
</button>
`;
exports[`Button should not insert space to link button 1`] = `
<button
class="ant-btn ant-btn-link"
type="button"
>
<span>
按钮
</span>
</button>
`;
exports[`Button should not render as link button when href is undefined 1`] = `
<button
class="ant-btn ant-btn-primary"

View File

@ -95,8 +95,11 @@ describe('Button', () => {
});
// https://github.com/ant-design/ant-design/issues/18118
it('should not insert space to link button', () => {
expect(<Button type="link"></Button>).toMatchRenderedSnapshot();
it('should not insert space to link or text button', () => {
const wrapper1 = mount(<Button type="link"></Button>);
expect(wrapper1.text()).toBe('按钮');
const wrapper2 = mount(<Button type="text"></Button>);
expect(wrapper2.text()).toBe('按钮');
});
it('should render empty button without errors', () => {
@ -188,11 +191,31 @@ describe('Button', () => {
expect(<Button>{false}</Button>).toMatchRenderedSnapshot();
});
it('should has click wave effect', async () => {
it('should have click wave effect', async () => {
const wrapper = mount(<Button type="primary">button</Button>);
wrapper.find('.ant-btn').getDOMNode<HTMLButtonElement>().click();
await new Promise(resolve => setTimeout(resolve, 0));
expect(wrapper.render()).toMatchSnapshot();
await sleep(0);
expect(
wrapper.find('.ant-btn').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
).toBe(true);
});
it('should not have click wave effect for link type button', async () => {
const wrapper = mount(<Button type="link">button</Button>);
wrapper.find('.ant-btn').getDOMNode<HTMLButtonElement>().click();
await sleep(0);
expect(
wrapper.find('.ant-btn').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
).toBe(false);
});
it('should not have click wave effect for text type button', async () => {
const wrapper = mount(<Button type="link">button</Button>);
wrapper.find('.ant-btn').getDOMNode<HTMLButtonElement>().click();
await sleep(0);
expect(
wrapper.find('.ant-btn').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
).toBe(false);
});
it('should not render as link button when href is undefined', async () => {
@ -212,7 +235,6 @@ describe('Button', () => {
This {'is'} a test {1}
</Button>,
);
expect(wrapper.render()).toMatchSnapshot();
});

View File

@ -68,7 +68,7 @@ function spaceChildren(children: React.ReactNode, needInserted: boolean) {
);
}
const ButtonTypes = tuple('default', 'primary', 'ghost', 'dashed', 'link');
const ButtonTypes = tuple('default', 'primary', 'ghost', 'dashed', 'link', 'text');
export type ButtonType = typeof ButtonTypes[number];
const ButtonShapes = tuple('circle', 'circle-outline', 'round');
export type ButtonShape = typeof ButtonShapes[number];
@ -131,7 +131,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
const isNeedInserted = () => {
const { icon, children, type } = props;
return React.Children.count(children) === 1 && !icon && type !== 'link';
return React.Children.count(children) === 1 && !icon && type !== 'link' && type !== 'text';
};
const fixTwoCNChar = () => {
@ -265,7 +265,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
</button>
);
if (type === 'link') {
if (type === 'link' || type === 'text') {
return buttonNode;
}

View File

@ -17,12 +17,14 @@ There are `primary` button, `default` button, `dashed` button and `link` button
import { Button } from 'antd';
ReactDOM.render(
<div>
<Button type="primary">Primary</Button>
<Button>Default</Button>
<Button type="dashed">Dashed</Button>
<Button type="link">Link</Button>
</div>,
<>
<Button type="primary">Primary Button</Button>
<Button>Default Button</Button>
<Button type="dashed">Dashed Button</Button>
<br />
<Button type="text">Text Button</Button>
<Button type="link">Link Button</Button>
</>,
mountNode,
);
```

View File

@ -36,6 +36,11 @@ ReactDOM.render(
Link(disabled)
</Button>
<br />
<Button type="text">Text</Button>
<Button type="text" disabled>
Text(disabled)
</Button>
<br />
<Button type="link" danger>
Danger Link
</Button>

View File

@ -25,6 +25,9 @@ ReactDOM.render(
<Button type="dashed" ghost>
link
</Button>
<Button type="text" ghost>
Text Button
</Button>
<Button type="link" ghost>
link
</Button>

View File

@ -15,6 +15,7 @@ In Ant Design we provide 4 types of button.
- Primary button: indicate the main action, one primary button at most in one section.
- Default button: indicate a series of actions without priority.
- Dashed button: used for adding action commonly.
- Text button: used for the most secondary action.
- Link button: used for external links.
And 4 other properties additionally.
@ -39,7 +40,7 @@ To get a customized button, just set `type`/`shape`/`size`/`loading`/`disabled`.
| shape | can be set to `circle`, `round` or omitted | string | - | |
| size | set the size of button | `large` \| `middle` \| `small` | | |
| target | same as target attribute of a, works when href is specified | string | - | |
| type | can be set to `primary` `ghost` `dashed` `link` or omitted (meaning `default`) | string | `default` | |
| type | can be set to `primary` `ghost` `dashed` `danger` `link` `text` or omitted (meaning `default`) | string | `default` | |
| onClick | set the handler to handle `click` event | (event) => void | - | |
| block | option to fit button width to its parent width | boolean | `false` | |
| danger | set the danger status of button | boolean | `false` | |

View File

@ -16,7 +16,8 @@ subtitle: 按钮
- 主按钮:用于主行动点,一个操作区域只能有一个主按钮。
- 默认按钮:用于没有主次之分的一组行动点。
- 虚线按钮:常用于添加操作。
- 链接按钮:用于次要或外链的行动点。
- 文本按钮:用于最次级的行动点。
- 链接按钮:用于作为外链的行动点。
以及四种状态属性与上面配合使用。
@ -42,7 +43,7 @@ subtitle: 按钮
| shape | 设置按钮形状,可选值为 `circle``round` 或者不设 | string | - | |
| size | 设置按钮大小 | `large` \| `middle` \| `small` | 无 | |
| target | 相当于 a 链接的 target 属性href 存在时生效 | string | - | |
| type | 设置按钮类型,可选值为 `primary` `dashed` `link` 或者不设 | string | - | |
| type | 设置按钮类型 | `primary` \| `ghost` \| `dashed` \| `danger` \| `link` \| `text` | - | |
| onClick | 点击按钮时的回调 | (event) => void | - | |
| block | 将按钮宽度调整为其父宽度的选项 | boolean | `false` | |
| danger | 设置危险按钮 | boolean | `false` | |

View File

@ -79,6 +79,10 @@
.btn-link;
}
&-text {
.btn-text;
}
&-dangerous {
.btn-danger-default;
}

View File

@ -147,7 +147,7 @@
}
.button-color(@color; @background; @border) {
color: @color;
background-color: @background;
background: @background;
border-color: @border; // a inside Button which only work in Chrome
// http://stackoverflow.com/a/17253457
> a:only-child {
@ -346,6 +346,9 @@
.btn-link() {
.button-variant-other(@link-color, transparent, transparent);
box-shadow: none;
&:hover {
background: @btn-link-hover-bg;
}
&:hover,
&:focus,
&:active {
@ -353,6 +356,25 @@
}
.button-disabled(@disabled-color; transparent; transparent);
}
// link button style
.btn-text() {
.button-variant-other(@text-color, transparent, transparent);
box-shadow: none;
&:hover,
&:focus {
color: @text-color;
background: @btn-text-hover-bg;
border-color: transparent;
}
&:active {
color: @text-color;
background: fadein(@btn-text-hover-bg, 1%);
border-color: transparent;
}
.button-disabled(@disabled-color; transparent; transparent);
}
// round button
.btn-round(@btnClassName: btn) {
.button-size(@btn-circle-size; @btn-circle-size / 2; @font-size-base; @btn-circle-size);

View File

@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Collapse rtl render component should be rendered correctly in RTL direction 1`] = `
<div
class="ant-collapse ant-collapse-icon-position-right ant-collapse-rtl"
/>
`;

View File

@ -49,12 +49,6 @@ exports[`Collapse could override default openAnimation 1`] = `
</div>
`;
exports[`Collapse rtl render component should be rendered correctly in RTL direction 1`] = `
<div
class="ant-collapse ant-collapse-icon-position-right ant-collapse-rtl"
/>
`;
exports[`Collapse should render extra node of panel 1`] = `
<div
class="ant-collapse ant-collapse-icon-position-left"

View File

@ -0,0 +1,8 @@
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import Collapse from '..';
describe('Collapse', () => {
mountTest(Collapse);
rtlTest(Collapse);
});

View File

@ -1,7 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { sleep } from '../../../tests/utils';
describe('Collapse', () => {
@ -18,8 +16,6 @@ describe('Collapse', () => {
// eslint-disable-next-line global-require
const Collapse = require('..').default;
mountTest(Collapse);
rtlTest(Collapse);
it('should support remove expandIcon', () => {
const wrapper = mount(
@ -65,11 +61,10 @@ describe('Collapse', () => {
</Collapse>,
);
expect(wrapper.find('.ant-collapse-item').hasClass('ant-collapse-item-active')).toBe(false);
wrapper
.find('.ant-collapse-header')
.at(0)
.simulate('click');
wrapper.find('.ant-collapse-header').at(0).simulate('click');
wrapper.update();
await sleep(400);
wrapper.update();
expect(wrapper.find('.ant-collapse-item').hasClass('ant-collapse-item-active')).toBe(true);
});
@ -81,10 +76,7 @@ describe('Collapse', () => {
</Collapse.Panel>
</Collapse>,
);
wrapper
.find('.ant-collapse-header')
.at(0)
.simulate('click');
wrapper.find('.ant-collapse-header').at(0).simulate('click');
expect(wrapper.render()).toMatchSnapshot();
});
});

View File

@ -102,6 +102,16 @@ describe('ConfigProvider', () => {
),
).toMatchSnapshot();
});
it('configProvider virtual and dropdownMatchSelectWidth', () => {
expect(
render(
<ConfigProvider virtual={false} dropdownMatchSelectWidth={false}>
{renderComponent({})}
</ConfigProvider>,
),
).toMatchSnapshot();
});
});
}

View File

@ -26,6 +26,8 @@ export interface ConfigConsumerProps {
space?: {
size?: SizeType | number;
};
virtual?: boolean;
dropdownMatchSelectWidth?: boolean;
}
export const ConfigContext = React.createContext<ConfigConsumerProps>({

View File

@ -47,9 +47,11 @@ Some components use dynamic style to support wave effect. You can config `csp` p
| getTargetContainer | Config Affix, Anchor scroll target container. | () => HTMLElement | () => window | 4.2.0 |
| locale | language package setting, you can find the packages in [antd/es/locale](http://unpkg.com/antd/es/locale/) | object | |
| prefixCls | set prefix class. `Note:` This will discard default styles from `antd`. | string | ant | |
| pageHeader | Unify the ghost of PageHeader, ref [PageHeader](/components/page-header) | { ghost:boolean } | 'true' | |
| pageHeader | Unify the ghost of PageHeader, ref [PageHeader](/components/page-header) | { ghost:boolean } | true | |
| direction | set direction of layout. See [demo](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | |
| space | set Space `size`, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 4.1.0 |
| virtual | Disable virtual scroll when set to `false` | boolean | - | 4.3.0 |
| dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. `false` will disable virtual scroll | boolean \| number | - | 4.3.0 |
## FAQ

View File

@ -9,6 +9,8 @@ import LocaleProvider, { Locale, ANT_MARK } from '../locale-provider';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import { ConfigConsumer, ConfigContext, CSPConfig, ConfigConsumerProps } from './context';
import { SizeType, SizeContextProvider } from './SizeContext';
import message from '../message';
import notification from '../notification';
export { RenderEmptyHandler, ConfigContext, ConfigConsumer, CSPConfig, ConfigConsumerProps };
@ -47,12 +49,25 @@ export interface ConfigProviderProps {
space?: {
size?: SizeType | number;
};
virtual?: boolean;
dropdownMatchSelectWidth?: boolean;
}
class ConfigProvider extends React.Component<ConfigProviderProps> {
getPrefixClsWrapper = (context: ConfigConsumerProps) => {
const ConfigProvider: React.FC<ConfigProviderProps> = props => {
React.useEffect(() => {
if (props.direction) {
message.config({
rtl: props.direction === 'rtl',
});
notification.config({
rtl: props.direction === 'rtl',
});
}
}, [props.direction]);
const getPrefixClsWrapper = (context: ConfigConsumerProps) => {
return (suffixCls: string, customizePrefixCls?: string) => {
const { prefixCls } = this.props;
const { prefixCls } = props;
if (customizePrefixCls) return customizePrefixCls;
@ -62,7 +77,7 @@ class ConfigProvider extends React.Component<ConfigProviderProps> {
};
};
renderProvider = (context: ConfigConsumerProps, legacyLocale: Locale) => {
const renderProvider = (context: ConfigConsumerProps, legacyLocale: Locale) => {
const {
children,
getTargetContainer,
@ -77,16 +92,20 @@ class ConfigProvider extends React.Component<ConfigProviderProps> {
componentSize,
direction,
space,
} = this.props;
virtual,
dropdownMatchSelectWidth,
} = props;
const config: ConfigConsumerProps = {
...context,
getPrefixCls: this.getPrefixClsWrapper(context),
getPrefixCls: getPrefixClsWrapper(context),
csp,
autoInsertSpaceInButton,
locale: locale || legacyLocale,
direction,
space,
virtual,
dropdownMatchSelectWidth,
};
if (getTargetContainer) {
@ -136,17 +155,15 @@ class ConfigProvider extends React.Component<ConfigProviderProps> {
);
};
render() {
return (
<LocaleReceiver>
{(_, __, legacyLocale) => (
<ConfigConsumer>
{context => this.renderProvider(context, legacyLocale as Locale)}
</ConfigConsumer>
)}
</LocaleReceiver>
);
}
}
return (
<LocaleReceiver>
{(_, __, legacyLocale) => (
<ConfigConsumer>
{context => renderProvider(context, legacyLocale as Locale)}
</ConfigConsumer>
)}
</LocaleReceiver>
);
};
export default ConfigProvider;

View File

@ -48,9 +48,11 @@ return (
| getTargetContainer | 配置 Affix、Anchor 滚动监听容器。 | () => HTMLElement | () => window | 4.2.0 |
| locale | 语言包配置,语言包可到 [antd/es/locale](http://unpkg.com/antd/es/locale/) 目录下寻找 | object | - | |
| prefixCls | 设置统一样式前缀。`注意:这将不会应用由 antd 提供的默认样式` | string | ant | |
| pageHeader | 统一设置 PageHeader 的 ghost参考 [PageHeader](/components/page-header) | { ghost: boolean } | 'true' | |
| pageHeader | 统一设置 PageHeader 的 ghost参考 [PageHeader](/components/page-header) | { ghost: boolean } | true | |
| direction | 设置文本展示方向。 [示例](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | |
| space | 设置 Space 的 `size`,参考 [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 4.1.0 |
| virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 4.3.0 |
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`。`false` 时会关闭虚拟滚动 | boolean \| number | - | 4.3.0 |
## FAQ

View File

@ -144,6 +144,7 @@ Added in `4.1.0`.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| allowEmpty | Allow start or end input leave empty | \[boolean, boolean] | \[false, false] | |
| dateRender | Customize date cell. `info` argument is added in 4.3.0 | function(currentDate: moment, today: moment, info: { range: 'start' \| ''end }) => React.ReactNode | - | |
| defaultValue | to set default date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - | |
| defaultPickerValue | to set default picker date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)\] | - | |
| disabled | disable start or end | `[boolean, boolean]` | - | |

View File

@ -146,6 +146,7 @@ import 'moment/locale/zh-cn';
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| allowEmpty | 允许起始项部分为空 | \[boolean, boolean] | \[false, false] | |
| dateRender | 自定义日期单元格的内容。`info` 参数自 4.3.0 添加 | function(currentDate: moment, today: moment, info: { range: 'start' \| ''end }) => React.ReactNode | - | |
| defaultValue | 默认日期 | [moment](http://momentjs.com/)\[] | 无 | |
| defaultPickerValue | 默认面板日期 | [moment](http://momentjs.com/)\[] | 无 | |
| disabled | 禁用起始项 | `[boolean, boolean]` | 无 | |

View File

@ -57,7 +57,7 @@ const Cell: React.FC<CellProps> = ({
{label && (
<span
className={classNames(`${itemPrefixCls}-item-label`, {
[`${itemPrefixCls}-item-colon`]: colon,
[`${itemPrefixCls}-item-no-colon`]: !colon,
})}
>
{label}

View File

@ -22,7 +22,7 @@ exports[`renders ./components/descriptions/demo/basic.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
UserName
</span>
@ -37,7 +37,7 @@ exports[`renders ./components/descriptions/demo/basic.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Telephone
</span>
@ -52,7 +52,7 @@ exports[`renders ./components/descriptions/demo/basic.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Live
</span>
@ -71,7 +71,7 @@ exports[`renders ./components/descriptions/demo/basic.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Remark
</span>
@ -86,7 +86,7 @@ exports[`renders ./components/descriptions/demo/basic.md correctly 1`] = `
colspan="2"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Address
</span>
@ -626,7 +626,7 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Product
</span>
@ -641,7 +641,7 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Billing
</span>
@ -656,7 +656,7 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
time
</span>
@ -675,7 +675,7 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Amount
</span>
@ -690,7 +690,7 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Discount
</span>
@ -705,7 +705,7 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Official
</span>
@ -745,7 +745,7 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
UserName
</span>
@ -755,7 +755,7 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Telephone
</span>
@ -765,7 +765,7 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Live
</span>
@ -813,7 +813,7 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
colspan="2"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Address
</span>
@ -823,7 +823,7 @@ exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Remark
</span>

View File

@ -17,7 +17,7 @@ exports[`Descriptions Descriptions support colon 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label"
class="ant-descriptions-item-label ant-descriptions-item-no-colon"
>
Product
</span>
@ -81,7 +81,7 @@ exports[`Descriptions Descriptions.Item support className 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Product
</span>
@ -115,7 +115,7 @@ exports[`Descriptions column is number 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Product
</span>
@ -130,7 +130,7 @@ exports[`Descriptions column is number 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Billing
</span>
@ -145,7 +145,7 @@ exports[`Descriptions column is number 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
time
</span>
@ -164,7 +164,7 @@ exports[`Descriptions column is number 1`] = `
colspan="3"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Amount
</span>
@ -198,7 +198,7 @@ exports[`Descriptions should work with React Fragment 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
bamboo
</span>
@ -217,7 +217,7 @@ exports[`Descriptions should work with React Fragment 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
bamboo
</span>
@ -236,7 +236,7 @@ exports[`Descriptions should work with React Fragment 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
bamboo
</span>
@ -270,7 +270,7 @@ exports[`Descriptions vertical layout 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Product
</span>
@ -298,7 +298,7 @@ exports[`Descriptions vertical layout 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Billing
</span>
@ -326,7 +326,7 @@ exports[`Descriptions vertical layout 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
time
</span>
@ -354,7 +354,7 @@ exports[`Descriptions vertical layout 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Amount
</span>
@ -397,7 +397,7 @@ exports[`Descriptions when item is rendered conditionally 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Product
</span>
@ -416,7 +416,7 @@ exports[`Descriptions when item is rendered conditionally 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Billing
</span>
@ -435,7 +435,7 @@ exports[`Descriptions when item is rendered conditionally 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
time
</span>
@ -454,7 +454,7 @@ exports[`Descriptions when item is rendered conditionally 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Amount
</span>

View File

@ -39,16 +39,21 @@
line-height: @line-height-base;
&::after {
& when (@descriptions-item-trailing-colon=true) {
content: ':';
}
& when not (@descriptions-item-trailing-colon=true) {
content: ' ';
}
position: relative;
top: -0.5px;
margin: 0 8px 0 2px;
content: ' ';
margin: 0 @descriptions-item-label-colon-margin-right 0
@descriptions-item-label-colon-margin-left;
}
}
&-item-colon {
&::after {
content: ':';
&.@{descriptions-prefix-cls}-item-no-colon::after {
content: ' ';
}
}

View File

@ -34,6 +34,7 @@ const menu = (
3rd menu item
</a>
</Menu.Item>
<Menu.Item danger>a danger item</Menu.Item>
</Menu>
);

View File

@ -1,5 +1,6 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import './status';
@dropdown-prefix-cls: ~'@{ant-prefix}-dropdown';

View File

@ -0,0 +1,12 @@
@import './index';
.@{dropdown-prefix-cls}-menu-item {
&&-danger {
color: @error-color;
&:hover {
color: @text-color-inverse;
background-color: @error-color;
}
}
}

View File

@ -286,16 +286,34 @@ exports[`renders ./components/empty/demo/config-provider.md correctly 1`] = `
/>
</span>
</label>
<span
aria-label="down"
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
role="img"
>
<svg
aria-hidden="true"
class=""
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
class="ant-transfer-list-header-selected"
>
<span>
0 item
</span>
<span
class="ant-transfer-list-header-title"
/>
0 item
</span>
<span
class="ant-transfer-list-header-title"
/>
</div>
<div
class="ant-transfer-list-body"
@ -429,16 +447,34 @@ exports[`renders ./components/empty/demo/config-provider.md correctly 1`] = `
/>
</span>
</label>
<span
aria-label="down"
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
role="img"
>
<svg
aria-hidden="true"
class=""
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
class="ant-transfer-list-header-selected"
>
<span>
0 item
</span>
<span
class="ant-transfer-list-header-title"
/>
0 item
</span>
<span
class="ant-transfer-list-header-title"
/>
</div>
<div
class="ant-transfer-list-body"

View File

@ -47,7 +47,7 @@ export interface FormItemProps extends FormItemLabelProps, FormItemInputProps, R
required?: boolean;
/** Auto passed by List render props. User should not use this. */
fieldKey?: number;
fieldKey?: React.Key | React.Key[];
}
function hasValidName(name?: NamePath): Boolean {
@ -192,6 +192,7 @@ function FormItem(props: FormItemProps): React.ReactElement {
'htmlFor',
'id', // It is deprecated because `htmlFor` is its replacement.
'initialValue',
'isListField',
'label',
'labelAlign',
'labelCol',
@ -256,7 +257,8 @@ function FormItem(props: FormItemProps): React.ReactElement {
if (noStyle) {
nameRef.current = [...mergedName];
if (fieldKey) {
nameRef.current[nameRef.current.length - 1] = fieldKey;
const fieldKeys = Array.isArray(fieldKey) ? fieldKey : [fieldKey];
nameRef.current = [...mergedName.slice(-1), ...fieldKeys];
}
updateItemErrors(nameRef.current.join('__SPLIT__'), errors);
}

View File

@ -1015,6 +1015,89 @@ exports[`renders ./components/form/demo/dynamic-form-item.md correctly 1`] = `
</form>
`;
exports[`renders ./components/form/demo/dynamic-form-items.md correctly 1`] = `
<form
autocomplete="off"
class="ant-form ant-form-horizontal"
id="dynamic_form_nest_item"
>
<div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<button
class="ant-btn ant-btn-dashed ant-btn-block"
type="button"
>
<span
aria-label="plus"
class="anticon anticon-plus"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="plus"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<defs />
<path
d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"
/>
<path
d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"
/>
</svg>
</span>
<span>
Add field
</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<button
class="ant-btn ant-btn-primary"
type="submit"
>
<span>
Submit
</span>
</button>
</div>
</div>
</div>
</div>
</form>
`;
exports[`renders ./components/form/demo/dynamic-rule.md correctly 1`] = `
<form
class="ant-form ant-form-horizontal"

View File

@ -7,11 +7,11 @@ title:
## zh-CN
动态增加、减少表单项。如果需要动态支持多项字段时,可以参考[此处](https://codesandbox.io/s/wonderful-lichterman-br63z)。
动态增加、减少表单项。
## en-US
Add or remove form items dynamically. You can ref [this example](https://codesandbox.io/s/wonderful-lichterman-br63z) if you want to support mutiple fields.
Add or remove form items dynamically.
```jsx
import { Form, Input, Button } from 'antd';

View File

@ -0,0 +1,84 @@
---
order: 4.1
title:
zh-CN: 动态增减嵌套字段
en-US: Dynamic Form nest Items
---
## zh-CN
嵌套表单字段需要对 `field` 进行拓展,将 `field.name``field.fieldKey` 应用于控制字段。
## en-US
Nest dynamic field need extends `field`. Pass `field.name` and `field.fieldKey` to nest item.
```jsx
import { Form, Input, Button, Space } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
const Demo = () => {
const onFinish = values => {
console.log('Received values of form:', values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
return (
<div>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name, 'first']}
fieldKey={[field.fieldKey, 'first']}
rules={[{ required: true, message: 'Missing first name' }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'last']}
fieldKey={[field.fieldKey, 'last']}
rules={[{ required: true, message: 'Missing last name' }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -16,26 +16,27 @@ High performance Form component with data scope management. Including data colle
### Form
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| component | Set the Form rendering element. Do not create a DOM node for `false` | ComponentType \| false | form |
| colon | Configure the default value of `colon` for Form.Item. Indicates whether the colon after the label is displayed (only effective when prop layout is horizontal) | boolean | true |
| fields | Control of form fields through state management (such as redux). Not recommended for non-strong demand. View [example](#components-form-demo-global-state) | [FieldData](#FieldData)\[] | - |
| form | Form control instance created by `Form.useForm()`. Automatically created when not provided | [FormInstance](#FormInstance) | - |
| hideRequiredMark | Hide required mark for all form items | boolean | false |
| initialValues | Set value by Form initialization or reset | object | - |
| labelAlign | text align of label of all items | `left` \| `right` | `right` |
| labelCol | label layout, like `<Col>` component. Set `span` `offset` value like `{span: 3, offset: 12}` or `sm: {span: 3, offset: 12}` | [object](/components/grid/#Col) | - |
| layout | Form layout | `horizontal` \| `vertical` \| `inline` | `horizontal` |
| name | Form name. Will be the prefix of Field `id` | string | - |
| scrollToFirstError | Auto scroll to first failed field when submit | false | - |
| size | Set field component size (antd components only) | `small` \| `middle` \| `large` | - |
| validateMessages | Validation prompt template, description [see below](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - |
| wrapperCol | The layout for input controls, same as `labelCol` | [object](/components/grid/#Col) | - |
| onFinish | Trigger after submitting the form and verifying data successfully | Function(values) | - |
| onFinishFailed | Trigger after submitting the form and verifying data failed | Function({ values, errorFields, outOfDate }) | - |
| onFieldsChange | Trigger when field updated | Function(changedFields, allFields) | - |
| onValuesChange | Trigger when value updated | Function(changedValues, allValues) | - |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| component | Set the Form rendering element. Do not create a DOM node for `false` | ComponentType \| false | form | |
| colon | Configure the default value of `colon` for Form.Item. Indicates whether the colon after the label is displayed (only effective when prop layout is horizontal) | boolean | true | |
| fields | Control of form fields through state management (such as redux). Not recommended for non-strong demand. View [example](#components-form-demo-global-state) | [FieldData](#FieldData)\[] | - | |
| form | Form control instance created by `Form.useForm()`. Automatically created when not provided | [FormInstance](#FormInstance) | - | |
| hideRequiredMark | Hide required mark for all form items | boolean | false | |
| initialValues | Set value by Form initialization or reset | object | - | |
| labelAlign | text align of label of all items | `left` \| `right` | `right` | |
| labelCol | label layout, like `<Col>` component. Set `span` `offset` value like `{span: 3, offset: 12}` or `sm: {span: 3, offset: 12}` | [object](/components/grid/#Col) | - | |
| layout | Form layout | `horizontal` \| `vertical` \| `inline` | `horizontal` | |
| name | Form name. Will be the prefix of Field `id` | string | - | |
| scrollToFirstError | Auto scroll to first failed field when submit | false | - | |
| size | Set field component size (antd components only) | `small` \| `middle` \| `large` | - | |
| validateMessages | Validation prompt template, description [see below](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - | |
| validateTrigger | Config field validate trigger | string \| string[] | 'onChange' | 4.3.0 |
| wrapperCol | The layout for input controls, same as `labelCol` | [object](/components/grid/#Col) | - | |
| onFinish | Trigger after submitting the form and verifying data successfully | Function(values) | - | |
| onFinishFailed | Trigger after submitting the form and verifying data failed | Function({ values, errorFields, outOfDate }) | - | |
| onFieldsChange | Trigger when field updated | Function(changedFields, allFields) | - | |
| onValuesChange | Trigger when value updated | Function(changedValues, allValues) | - | |
### validateMessages

View File

@ -17,26 +17,27 @@ title: Form
### Form
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| component | 设置 Form 渲染元素,为 `false` 则不创建 DOM 节点 | ComponentType \| false | form |
| colon | 配置 Form.Item 的 `colon` 的默认值。表示是否显示 label 后面的冒号 (只有在属性 layout 为 horizontal 时有效) | boolean | true |
| fields | 通过状态管理(如 redux控制表单字段如非强需求不推荐使用。查看[示例](#components-form-demo-global-state) | [FieldData](#FieldData)\[] | - |
| form | 经 `Form.useForm()` 创建的 form 控制实例,不提供时会自动创建 | [FormInstance](#FormInstance) | - |
| hideRequiredMark | 隐藏所有表单项的必选标记 | boolean | false |
| initialValues | 表单默认值,只有初始化以及重置时生效 | object | - |
| labelAlign | label 标签的文本对齐方式 | `left` \| `right` | `right` |
| labelCol | label 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}``sm: {span: 3, offset: 12}` | [object](/components/grid/#Col) | - |
| layout | 表单布局 | `horizontal` \| `vertical` \| `inline` | `horizontal` |
| name | 表单名称,会作为表单字段 `id` 前缀使用 | string | - |
| scrollToFirstError | 提交失败自动滚动到第一个错误字段 | false | - |
| size | 设置字段组件的尺寸(仅限 antd 组件) | `small` \| `middle` \| `large` | - |
| validateMessages | 验证提示模板,说明[见下](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - |
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | [object](/components/grid/#Col) | - |
| onFinish | 提交表单且数据验证成功后回调事件 | Function(values) | - |
| onFinishFailed | 提交表单且数据验证失败后回调事件 | Function({ values, errorFields, outOfDate }) | - |
| onFieldsChange | 字段更新时触发回调事件 | Function(changedFields, allFields) | - |
| onValuesChange | 字段值更新时触发回调事件 | Function(changedValues, allValues) | - |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| component | 设置 Form 渲染元素,为 `false` 则不创建 DOM 节点 | ComponentType \| false | form | |
| colon | 配置 Form.Item 的 `colon` 的默认值。表示是否显示 label 后面的冒号 (只有在属性 layout 为 horizontal 时有效) | boolean | true | |
| fields | 通过状态管理(如 redux控制表单字段如非强需求不推荐使用。查看[示例](#components-form-demo-global-state) | [FieldData](#FieldData)\[] | - | |
| form | 经 `Form.useForm()` 创建的 form 控制实例,不提供时会自动创建 | [FormInstance](#FormInstance) | - | |
| hideRequiredMark | 隐藏所有表单项的必选标记 | boolean | false | |
| initialValues | 表单默认值,只有初始化以及重置时生效 | object | - | |
| labelAlign | label 标签的文本对齐方式 | `left` \| `right` | `right` | |
| labelCol | label 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}``sm: {span: 3, offset: 12}` | [object](/components/grid/#Col) | - | |
| layout | 表单布局 | `horizontal` \| `vertical` \| `inline` | `horizontal` | |
| name | 表单名称,会作为表单字段 `id` 前缀使用 | string | - | |
| scrollToFirstError | 提交失败自动滚动到第一个错误字段 | false | - | |
| size | 设置字段组件的尺寸(仅限 antd 组件) | `small` \| `middle` \| `large` | - | |
| validateMessages | 验证提示模板,说明[见下](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - | |
| validateTrigger | 统一设置字段校验规则 | string \| string[] | 'onChange' | 4.3.0 |
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | [object](/components/grid/#Col) | - | |
| onFinish | 提交表单且数据验证成功后回调事件 | Function(values) | - | |
| onFinishFailed | 提交表单且数据验证失败后回调事件 | Function({ values, errorFields, outOfDate }) | - | |
| onFieldsChange | 字段更新时触发回调事件 | Function(changedFields, allFields) | - | |
| onValuesChange | 字段值更新时触发回调事件 | Function(changedValues, allValues) | - | |
### validateMessages

View File

@ -11,6 +11,7 @@ export interface PasswordProps extends InputProps {
readonly inputPrefixCls?: string;
readonly action?: string;
visibilityToggle?: boolean;
iconRender?: (visible: boolean) => React.ReactNode;
}
export interface PasswordState {
@ -28,6 +29,7 @@ export default class Password extends React.Component<PasswordProps, PasswordSta
static defaultProps = {
action: 'click',
visibilityToggle: true,
iconRender: (visible: boolean) => (visible ? <EyeOutlined /> : <EyeInvisibleOutlined />),
};
state: PasswordState = {
@ -44,9 +46,10 @@ export default class Password extends React.Component<PasswordProps, PasswordSta
};
getIcon = (prefixCls: string) => {
const { action } = this.props;
const { action, iconRender = () => null } = this.props;
const { visible } = this.state;
const iconTrigger = ActionMap[action!] || '';
const icon = this.state.visible ? EyeOutlined : EyeInvisibleOutlined;
const icon = iconRender(visible);
const iconProps = {
[iconTrigger]: this.onVisibleChange,
className: `${prefixCls}-icon`,
@ -62,7 +65,7 @@ export default class Password extends React.Component<PasswordProps, PasswordSta
e.preventDefault();
},
};
return React.createElement(icon as React.ComponentType, iconProps);
return React.cloneElement(React.isValidElement(icon) ? icon : <span>{icon}</span>, iconProps);
};
saveInput = (instance: Input) => {
@ -102,7 +105,7 @@ export default class Password extends React.Component<PasswordProps, PasswordSta
});
const props = {
...omit(restProps, ['suffix']),
...omit(restProps, ['suffix', 'iconRender']),
type: this.state.visible ? 'text' : 'password',
className: inputClassName,
prefixCls: inputPrefixCls,

View File

@ -1918,45 +1918,97 @@ exports[`renders ./components/input/demo/group.md correctly 1`] = `
`;
exports[`renders ./components/input/demo/password-input.md correctly 1`] = `
<span
class="ant-input-password ant-input-affix-wrapper"
<div
class="ant-space ant-space-vertical"
>
<input
action="click"
class="ant-input"
placeholder="input password"
type="password"
value=""
/>
<span
class="ant-input-suffix"
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<span
aria-label="eye-invisible"
class="anticon anticon-eye-invisible ant-input-password-icon"
role="img"
tabindex="-1"
class="ant-input-password ant-input-affix-wrapper"
>
<svg
aria-hidden="true"
class=""
data-icon="eye-invisible"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<input
action="click"
class="ant-input"
placeholder="input password"
type="password"
value=""
/>
<span
class="ant-input-suffix"
>
<path
d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"
/>
<path
d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"
/>
</svg>
<span
aria-label="eye-invisible"
class="anticon anticon-eye-invisible ant-input-password-icon"
role="img"
tabindex="-1"
>
<svg
aria-hidden="true"
class=""
data-icon="eye-invisible"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"
/>
<path
d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"
/>
</svg>
</span>
</span>
</span>
</span>
</span>
</div>
<div
class="ant-space-item"
>
<span
class="ant-input-password ant-input-affix-wrapper"
>
<input
action="click"
class="ant-input"
placeholder="input password"
type="password"
value=""
/>
<span
class="ant-input-suffix"
>
<span
aria-label="eye-invisible"
class="anticon anticon-eye-invisible ant-input-password-icon"
role="img"
tabindex="-1"
>
<svg
aria-hidden="true"
class=""
data-icon="eye-invisible"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"
/>
<path
d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"
/>
</svg>
</span>
</span>
</span>
</div>
</div>
`;
exports[`renders ./components/input/demo/presuffix.md correctly 1`] = `

View File

@ -14,7 +14,17 @@ title:
Input type of password.
```jsx
import { Input } from 'antd';
import { Input, Space } from 'antd';
import { EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons';
ReactDOM.render(<Input.Password placeholder="input password" />, mountNode);
ReactDOM.render(
<Space direction="vertical">
<Input.Password placeholder="input password" />
<Input.Password
placeholder="input password"
iconRender={visible => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
/>
</Space>,
mountNode,
);
```

View File

@ -34,7 +34,7 @@
}
.disabled() {
color: @disabled-color;
color: @input-disabled-color;
background-color: @input-disabled-bg;
cursor: not-allowed;
opacity: 1;

View File

@ -44,6 +44,12 @@ const localeValues: Locale = {
searchPlaceholder: 'Search here',
itemUnit: 'item',
itemsUnit: 'items',
remove: 'Remove',
selectCurrent: 'Select current page',
removeCurrent: 'Remove current page',
selectAll: 'Select all data',
removeAll: 'Remove all data',
selectInvert: 'Invert current page',
},
Upload: {
uploading: 'Uploading...',

View File

@ -44,6 +44,12 @@ const localeValues: Locale = {
searchPlaceholder: '请输入搜索内容',
itemUnit: '项',
itemsUnit: '项',
remove: '删除',
selectCurrent: '全选当页',
removeCurrent: '删除当页',
selectAll: '全选所有',
removeAll: '删除全部',
selectInvert: '反选当页',
},
Upload: {
uploading: '文件上传中',

View File

@ -17,6 +17,7 @@ export interface MenuItemProps
disabled?: boolean;
level?: number;
icon?: React.ReactNode;
danger?: boolean;
title?: React.ReactNode;
children?: React.ReactNode;
className?: string;
@ -56,7 +57,7 @@ export default class MenuItem extends React.Component<MenuItemProps> {
renderItem = ({ siderCollapsed }: SiderContextProps) => {
const { level, className, children, rootPrefixCls } = this.props;
const { title, icon, ...rest } = this.props;
const { title, icon, danger, ...rest } = this.props;
return (
<MenuContext.Consumer>
@ -87,6 +88,7 @@ export default class MenuItem extends React.Component<MenuItemProps> {
<Item
{...rest}
className={classNames(className, {
[`${rootPrefixCls}-item-danger`]: danger,
[`${rootPrefixCls}-item-only-child`]:
(icon ? childrenLength + 1 : childrenLength) === 1,
})}

View File

@ -1156,7 +1156,7 @@ Array [
Option 3
</li>
<li
class="ant-menu-item ant-menu-item-only-child"
class="ant-menu-item ant-menu-item-danger ant-menu-item-only-child"
role="menuitem"
style="padding-left:48px"
>

View File

@ -61,7 +61,9 @@ class Sider extends React.Component {
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
<Menu.Item key="4" danger>
Option 4
</Menu.Item>
</SubMenu>
<SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
<Menu.Item key="5">Option 5</Menu.Item>

View File

@ -58,6 +58,7 @@ More layouts with navigation: [Layout](/components/layout).
| key | Unique ID of the menu item | string | | |
| title | Set display title for collapsed item | string | | |
| icon | icon of the menu item | ReactNode | | 4.2.0 |
| danger | Display the danger style | boolean | false | 4.3.0 |
> Note: `icon` is a newly added prop in`4.2.0`. For previous versions, please use the following method to define the icon.
>

View File

@ -59,6 +59,7 @@ subtitle: 导航菜单
| key | item 的唯一标志 | string | | |
| title | 设置收缩时展示的悬浮标题 | string | | |
| icon | 菜单图标 | ReactNode | | 4.2.0 |
| danger | 展示错误状态样式 | boolean | false | 4.3.0 |
> 注意:`icon` 是 `4.2.0` 新增的属性,之前的版本请使用下面的方式定义图标。
>

View File

@ -1,5 +1,6 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import './status';
@menu-prefix-cls: ~'@{ant-prefix}-menu';

View File

@ -0,0 +1,47 @@
@import './index';
.@{menu-prefix-cls} {
// Danger
&-item-danger&-item {
color: @menu-highlight-danger-color;
&:hover,
&-active {
color: @menu-highlight-danger-color;
}
&:active {
background: @menu-item-active-danger-bg;
}
&-selected {
color: @menu-highlight-danger-color;
> a,
> a:hover {
color: @menu-highlight-danger-color;
}
}
.@{menu-prefix-cls}:not(.@{menu-prefix-cls}-horizontal) &-selected {
background-color: @menu-item-active-danger-bg;
}
.@{menu-prefix-cls}-inline &::after {
border-right-color: @menu-highlight-danger-color;
}
}
// ==================== Dark ====================
&-dark &-item-danger&-item {
&,
&:hover,
& > a {
color: @menu-dark-danger-color;
}
}
&-dark&-dark:not(&-horizontal) &-item-danger&-item-selected {
color: @menu-dark-highlight-color;
background-color: @menu-dark-item-active-danger-bg;
}
}

View File

@ -1,5 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/message/demo/custom-style.md correctly 1`] = `
<button
class="ant-btn"
type="button"
>
<span>
Customized style
</span>
</button>
`;
exports[`renders ./components/message/demo/duration.md correctly 1`] = `
<button
class="ant-btn"

View File

@ -0,0 +1,30 @@
---
order: 6
title:
zh-CN: 自定义样式
en-US: Customized style
---
## zh-CN
使用 `style``className` 来定义样式。
## en-US
The `style` and `className` are available to customize Message.
```jsx
import { message, Button } from 'antd';
const success = () => {
message.success({
content: 'This is a prompt message with custom className and style',
className: 'custom-class',
style: {
marginTop: '50%',
},
});
};
ReactDOM.render(<Button onClick={success}>Customized style</Button>, mountNode);
```

View File

@ -55,6 +55,8 @@ The properties of config are as follows:
| onClose | Specify a function that will be called when the message is closed | function | - |
| icon | Customized Icon | ReactNode | - |
| key | The unique identifier of the Message | string\|number | - |
| className | Customized CSS class | string | - |
| style | Customized inline style | [React.CSSProperties](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e434515761b36830c3e58a970abf5186f005adac/types/react/index.d.ts#L794) | - |
### Global static methods
@ -65,6 +67,10 @@ Methods for global configuration and destruction are also provided:
#### message.config
> When you use `ConfigProvider` for global configuration, the system will automatically start RTL mode by default.(4.3.0+)
>
> When you want to use it alone, you can start the RTL mode through the following settings.
```js
message.config({
top: 100,

View File

@ -60,6 +60,8 @@ export interface ArgsProps {
onClose?: () => void;
icon?: React.ReactNode;
key?: string | number;
style?: React.CSSProperties;
className?: string;
}
const iconMap = {
@ -91,7 +93,8 @@ function notice(args: ArgsProps): MessageType {
instance.notice({
key: target,
duration,
style: {},
style: args.style || {},
className: args.className,
content: (
<div className={messageClass}>
{args.icon || (IconComponent && <IconComponent />)}

View File

@ -49,13 +49,15 @@ title: Message
`config` 对象属性如下:
| 参数 | 说明 | 类型 | 默认值 |
| -------- | --------------------------------------------- | -------------- | ------ |
| content | 提示内容 | ReactNode | - |
| duration | 自动关闭的延时,单位秒。设为 0 时不自动关闭。 | number | 3 |
| onClose | 关闭时触发的回调函数 | Function | - |
| icon | 自定义图标 | ReactNode | - |
| key | 当前提示的唯一标志 | string\|number | - |
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| content | 提示内容 | ReactNode | - |
| duration | 自动关闭的延时,单位秒。设为 0 时不自动关闭。 | number | 3 |
| onClose | 关闭时触发的回调函数 | Function | - |
| icon | 自定义图标 | ReactNode | - |
| key | 当前提示的唯一标志 | string\|number | - |
| className | 自定义 CSS class | string | - |
| style | 自定义内联样式 | [React.CSSProperties](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e434515761b36830c3e58a970abf5186f005adac/types/react/index.d.ts#L794) | - |
### 全局方法
@ -66,6 +68,10 @@ title: Message
#### message.config
> 当你使用 `ConfigProvider` 进行全局化配置时,系统会默认自动开启 RTL 模式。(4.3.0+)
>
> 当你想单独使用,可通过如下设置开启 RTL 模式。
```js
message.config({
top: 100,

View File

@ -98,6 +98,21 @@ describe('notification', () => {
expect(document.querySelectorAll('.ant-notification-rtl').length).toBe(1);
});
it('should be able to config prefixCls', () => {
notification.config({
prefixCls: 'prefix-test',
});
notification.open({
message: 'Notification Title',
duration: 0,
});
expect(document.querySelectorAll('.ant-notification-notice').length).toBe(0);
expect(document.querySelectorAll('.prefix-test-notice').length).toBe(1);
notification.config({
prefixCls: 'ant-notification',
});
});
it('should be able to open with icon', async () => {
const openNotificationWithIcon = async type => {
const iconPrefix = '.ant-notification-notice-icon';

View File

@ -50,6 +50,10 @@ The properties of config are as follows:
- `notification.config(options)`
> When you use `ConfigProvider` for global configuration, the system will automatically start RTL mode by default.(4.3.0+)
>
> When you want to use it alone, you can start the RTL mode through the following settings.
```js
notification.config({
placement: 'bottomRight',

View File

@ -19,6 +19,7 @@ const notificationInstance: {
let defaultDuration = 4.5;
let defaultTop = 24;
let defaultBottom = 24;
let defaultPrefixCls = 'ant-notification';
let defaultPlacement: NotificationPlacement = 'topRight';
let defaultGetContainer: () => HTMLElement;
let defaultCloseIcon: React.ReactNode;
@ -27,6 +28,7 @@ export interface ConfigProps {
top?: number;
bottom?: number;
duration?: number;
prefixCls?: string;
placement?: NotificationPlacement;
getContainer?: () => HTMLElement;
closeIcon?: React.ReactNode;
@ -35,7 +37,10 @@ export interface ConfigProps {
let rtl = false;
function setNotificationConfig(options: ConfigProps) {
const { duration, placement, bottom, top, getContainer, closeIcon } = options;
const { duration, placement, bottom, top, getContainer, closeIcon, prefixCls } = options;
if (prefixCls !== undefined) {
defaultPrefixCls = prefixCls;
}
if (duration !== undefined) {
defaultDuration = duration;
}
@ -109,7 +114,7 @@ function getNotificationInstance(
getContainer = defaultGetContainer,
closeIcon = defaultCloseIcon,
} = args;
const outerPrefixCls = args.prefixCls || 'ant-notification';
const outerPrefixCls = args.prefixCls || defaultPrefixCls;
const prefixCls = `${outerPrefixCls}-notice`;
const cacheKey = `${outerPrefixCls}-${placement}`;

View File

@ -51,6 +51,10 @@ config 参数如下:
- `notification.config(options)`
> 当你使用 `ConfigProvider` 进行全局化配置时,系统会默认自动开启 RTL 模式。(4.3.0+)
>
> 当你想单独使用,可通过如下设置开启 RTL 模式。
```js
notification.config({
placement: 'bottomRight',

View File

@ -104,7 +104,7 @@ exports[`renders ./components/page-header/demo/actions.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Created
</span>
@ -119,7 +119,7 @@ exports[`renders ./components/page-header/demo/actions.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Association
</span>
@ -136,7 +136,7 @@ exports[`renders ./components/page-header/demo/actions.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Creation Time
</span>
@ -155,7 +155,7 @@ exports[`renders ./components/page-header/demo/actions.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Effective Time
</span>
@ -170,7 +170,7 @@ exports[`renders ./components/page-header/demo/actions.md correctly 1`] = `
colspan="2"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Remarks
</span>
@ -828,7 +828,7 @@ exports[`renders ./components/page-header/demo/ghost.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Created
</span>
@ -843,7 +843,7 @@ exports[`renders ./components/page-header/demo/ghost.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Association
</span>
@ -860,7 +860,7 @@ exports[`renders ./components/page-header/demo/ghost.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Creation Time
</span>
@ -879,7 +879,7 @@ exports[`renders ./components/page-header/demo/ghost.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Effective Time
</span>
@ -894,7 +894,7 @@ exports[`renders ./components/page-header/demo/ghost.md correctly 1`] = `
colspan="2"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Remarks
</span>
@ -1024,7 +1024,7 @@ exports[`renders ./components/page-header/demo/responsive.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Created
</span>
@ -1039,7 +1039,7 @@ exports[`renders ./components/page-header/demo/responsive.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Association
</span>
@ -1060,7 +1060,7 @@ exports[`renders ./components/page-header/demo/responsive.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Creation Time
</span>
@ -1075,7 +1075,7 @@ exports[`renders ./components/page-header/demo/responsive.md correctly 1`] = `
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Effective Time
</span>
@ -1094,7 +1094,7 @@ exports[`renders ./components/page-header/demo/responsive.md correctly 1`] = `
colspan="2"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
class="ant-descriptions-item-label"
>
Remarks
</span>

View File

@ -2,8 +2,8 @@
exports[`Popconfirm rtl render component should be rendered correctly in RTL direction 1`] = `<span />`;
exports[`Popconfirm should show overlay when trigger is clicked 1`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-inner-content\\"><div class=\\"ant-popover-message\\"><span role=\\"img\\" aria-label=\\"exclamation-circle\\" class=\\"anticon anticon-exclamation-circle\\"><svg viewBox=\\"64 64 896 896\\" focusable=\\"false\\" class=\\"\\" data-icon=\\"exclamation-circle\\" width=\\"1em\\" height=\\"1em\\" fill=\\"currentColor\\" aria-hidden=\\"true\\"><path d=\\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z\\"></path></svg></span><div class=\\"ant-popover-message-title\\">code</div></div><div class=\\"ant-popover-buttons\\"><button type=\\"button\\" class=\\"ant-btn ant-btn-sm\\"><span>Cancel</span></button><button type=\\"button\\" class=\\"ant-btn ant-btn-primary ant-btn-sm\\"><span>OK</span></button></div></div></div></div>"`;
exports[`Popconfirm should show overlay when trigger is clicked 1`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"><span class=\\"ant-popover-arrow-content\\"></span></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-inner-content\\"><div class=\\"ant-popover-message\\"><span role=\\"img\\" aria-label=\\"exclamation-circle\\" class=\\"anticon anticon-exclamation-circle\\"><svg viewBox=\\"64 64 896 896\\" focusable=\\"false\\" class=\\"\\" data-icon=\\"exclamation-circle\\" width=\\"1em\\" height=\\"1em\\" fill=\\"currentColor\\" aria-hidden=\\"true\\"><path d=\\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z\\"></path></svg></span><div class=\\"ant-popover-message-title\\">code</div></div><div class=\\"ant-popover-buttons\\"><button type=\\"button\\" class=\\"ant-btn ant-btn-sm\\"><span>Cancel</span></button><button type=\\"button\\" class=\\"ant-btn ant-btn-primary ant-btn-sm\\"><span>OK</span></button></div></div></div></div>"`;
exports[`Popconfirm should show overlay when trigger is clicked 2`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-inner-content\\"><div class=\\"ant-popover-message\\"><span role=\\"img\\" aria-label=\\"exclamation-circle\\" class=\\"anticon anticon-exclamation-circle\\"><svg viewBox=\\"64 64 896 896\\" focusable=\\"false\\" class=\\"\\" data-icon=\\"exclamation-circle\\" width=\\"1em\\" height=\\"1em\\" fill=\\"currentColor\\" aria-hidden=\\"true\\"><path d=\\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z\\"></path></svg></span><div class=\\"ant-popover-message-title\\">code</div></div><div class=\\"ant-popover-buttons\\"><button type=\\"button\\" class=\\"ant-btn ant-btn-sm\\"><span>Cancel</span></button><button type=\\"button\\" class=\\"ant-btn ant-btn-primary ant-btn-sm\\"><span>OK</span></button></div></div></div></div>"`;
exports[`Popconfirm should show overlay when trigger is clicked 2`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"><span class=\\"ant-popover-arrow-content\\"></span></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-inner-content\\"><div class=\\"ant-popover-message\\"><span role=\\"img\\" aria-label=\\"exclamation-circle\\" class=\\"anticon anticon-exclamation-circle\\"><svg viewBox=\\"64 64 896 896\\" focusable=\\"false\\" class=\\"\\" data-icon=\\"exclamation-circle\\" width=\\"1em\\" height=\\"1em\\" fill=\\"currentColor\\" aria-hidden=\\"true\\"><path d=\\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z\\"></path></svg></span><div class=\\"ant-popover-message-title\\">code</div></div><div class=\\"ant-popover-buttons\\"><button type=\\"button\\" class=\\"ant-btn ant-btn-sm\\"><span>Cancel</span></button><button type=\\"button\\" class=\\"ant-btn ant-btn-primary ant-btn-sm\\"><span>OK</span></button></div></div></div></div>"`;
exports[`Popconfirm shows content for render functions 1`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-inner-content\\"><div class=\\"ant-popover-message\\"><span role=\\"img\\" aria-label=\\"exclamation-circle\\" class=\\"anticon anticon-exclamation-circle\\"><svg viewBox=\\"64 64 896 896\\" focusable=\\"false\\" class=\\"\\" data-icon=\\"exclamation-circle\\" width=\\"1em\\" height=\\"1em\\" fill=\\"currentColor\\" aria-hidden=\\"true\\"><path d=\\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z\\"></path></svg></span><div class=\\"ant-popover-message-title\\">some-title</div></div><div class=\\"ant-popover-buttons\\"><button type=\\"button\\" class=\\"ant-btn ant-btn-sm\\"><span>Cancel</span></button><button type=\\"button\\" class=\\"ant-btn ant-btn-primary ant-btn-sm\\"><span>OK</span></button></div></div></div></div>"`;
exports[`Popconfirm shows content for render functions 1`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"><span class=\\"ant-popover-arrow-content\\"></span></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-inner-content\\"><div class=\\"ant-popover-message\\"><span role=\\"img\\" aria-label=\\"exclamation-circle\\" class=\\"anticon anticon-exclamation-circle\\"><svg viewBox=\\"64 64 896 896\\" focusable=\\"false\\" class=\\"\\" data-icon=\\"exclamation-circle\\" width=\\"1em\\" height=\\"1em\\" fill=\\"currentColor\\" aria-hidden=\\"true\\"><path d=\\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z\\"></path></svg></span><div class=\\"ant-popover-message-title\\">some-title</div></div><div class=\\"ant-popover-buttons\\"><button type=\\"button\\" class=\\"ant-btn ant-btn-sm\\"><span>Cancel</span></button><button type=\\"button\\" class=\\"ant-btn ant-btn-primary ant-btn-sm\\"><span>OK</span></button></div></div></div></div>"`;

View File

@ -176,4 +176,23 @@ describe('Popconfirm', () => {
triggerNode.simulate('click');
expect(ref.current.getPopupDomNode()).toBeFalsy();
});
it('should be closed by pressing ESC', () => {
const onVisibleChange = jest.fn();
const wrapper = mount(
<Popconfirm
title="title"
mouseEnterDelay={0}
mouseLeaveDelay={0}
onVisibleChange={onVisibleChange}
>
<span>Delete</span>
</Popconfirm>,
);
const triggerNode = wrapper.find('span').at(0);
triggerNode.simulate('click');
expect(onVisibleChange).toHaveBeenLastCalledWith(true, undefined);
triggerNode.simulate('keydown', { key: 'Escape', keyCode: 27 });
expect(onVisibleChange).toHaveBeenLastCalledWith(false, eventObject);
});
});

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import KeyCode from 'rc-util/lib/KeyCode';
import Tooltip, { AbstractTooltipProps } from '../tooltip';
import Button from '../button';
import { LegacyButtonType, NativeButtonProps, convertLegacyProps } from '../button/button';
@ -7,6 +8,7 @@ import LocaleReceiver from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale/default';
import { ConfigContext } from '../config-provider';
import { getRenderPropValue, RenderFunction } from '../_util/getRenderPropValue';
import { cloneElement } from '../_util/reactNode';
export interface PopconfirmProps extends AbstractTooltipProps {
title: React.ReactNode | RenderFunction;
@ -19,7 +21,10 @@ export interface PopconfirmProps extends AbstractTooltipProps {
okButtonProps?: NativeButtonProps;
cancelButtonProps?: NativeButtonProps;
icon?: React.ReactNode;
onVisibleChange?: (visible: boolean, e?: React.MouseEvent<HTMLElement>) => void;
onVisibleChange?: (
visible: boolean,
e?: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLDivElement>,
) => void;
}
export interface PopconfirmState {
@ -46,7 +51,10 @@ const Popconfirm = React.forwardRef<unknown, PopconfirmProps>((props, ref) => {
}
}, [props.defaultVisible]);
const settingVisible = (value: boolean, e?: React.MouseEvent<HTMLButtonElement>) => {
const settingVisible = (
value: boolean,
e?: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLDivElement>,
) => {
if (!('visible' in props)) {
setVisible(value);
}
@ -72,6 +80,12 @@ const Popconfirm = React.forwardRef<unknown, PopconfirmProps>((props, ref) => {
}
};
const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.keyCode === KeyCode.ESC && visible) {
settingVisible(false, e);
}
};
const onVisibleChange = (value: boolean) => {
const { disabled } = props;
if (disabled) {
@ -107,7 +121,7 @@ const Popconfirm = React.forwardRef<unknown, PopconfirmProps>((props, ref) => {
const { getPrefixCls } = React.useContext(ConfigContext);
const { prefixCls: customizePrefixCls, placement, ...restProps } = props;
const { prefixCls: customizePrefixCls, placement, children, ...restProps } = props;
const prefixCls = getPrefixCls('popover', customizePrefixCls);
const overlay = (
@ -125,7 +139,14 @@ const Popconfirm = React.forwardRef<unknown, PopconfirmProps>((props, ref) => {
visible={visible}
overlay={overlay}
ref={ref as any}
/>
>
{cloneElement(children, {
onKeyDown: (e: React.KeyboardEvent<any>) => {
children?.props.onKeyDown?.(e);
onKeyDown(e);
},
})}
</Tooltip>
);
});

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Popover handles empty title/content props safely 1`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-inner-content\\"></div></div></div>"`;
exports[`Popover handles empty title/content props safely 1`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"><span class=\\"ant-popover-arrow-content\\"></span></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-inner-content\\"></div></div></div>"`;
exports[`Popover should be rendered correctly in RTL direction 1`] = `
Array [
@ -19,7 +19,11 @@ Array [
>
<div
class="ant-popover-arrow"
/>
>
<span
class="ant-popover-arrow-content"
/>
</div>
<div
class="ant-popover-inner"
role="tooltip"
@ -39,6 +43,6 @@ Array [
]
`;
exports[`Popover should show overlay when trigger is clicked 1`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-title\\">code</div><div class=\\"ant-popover-inner-content\\">console.log('hello world')</div></div></div>"`;
exports[`Popover should show overlay when trigger is clicked 1`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"><span class=\\"ant-popover-arrow-content\\"></span></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-title\\">code</div><div class=\\"ant-popover-inner-content\\">console.log('hello world')</div></div></div>"`;
exports[`Popover shows content for render functions 1`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-title\\">some-title</div><div class=\\"ant-popover-inner-content\\">some-content</div></div></div>"`;
exports[`Popover shows content for render functions 1`] = `"<div class=\\"ant-popover-content\\"><div class=\\"ant-popover-arrow\\"><span class=\\"ant-popover-arrow-content\\"></span></div><div class=\\"ant-popover-inner\\" role=\\"tooltip\\"><div class=\\"ant-popover-title\\">some-title</div><div class=\\"ant-popover-inner-content\\">some-content</div></div></div>"`;

View File

@ -81,6 +81,8 @@ class Select<ValueType extends SelectValue = SelectValue> extends React.Componen
getPrefixCls,
renderEmpty,
direction,
virtual,
dropdownMatchSelectWidth,
}: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,
@ -142,6 +144,8 @@ class Select<ValueType extends SelectValue = SelectValue> extends React.Componen
return (
<RcSelect<ValueType>
ref={this.selectRef}
virtual={virtual}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
{...selectProps}
listHeight={listHeight}
listItemHeight={listItemHeight}

View File

@ -21,6 +21,7 @@ export interface SkeletonProps {
avatar?: SkeletonAvatarProps | boolean;
title?: SkeletonTitleProps | boolean;
paragraph?: SkeletonParagraphProps | boolean;
round?: boolean;
}
function getComponentProps<T>(prop: T | boolean | undefined): T | {} {
@ -80,6 +81,7 @@ const Skeleton = (props: SkeletonProps) => {
title,
paragraph,
active,
round,
} = props;
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
@ -143,6 +145,7 @@ const Skeleton = (props: SkeletonProps) => {
[`${prefixCls}-with-avatar`]: hasAvatar,
[`${prefixCls}-active`]: active,
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-round`]: round,
});
return (

View File

@ -450,6 +450,30 @@ exports[`Skeleton rtl render component should be rendered correctly in RTL direc
</div>
`;
exports[`Skeleton should round title and paragraph 1`] = `
<div
class="ant-skeleton ant-skeleton-round"
>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width: 38%;"
/>
<ul
class="ant-skeleton-paragraph"
>
<li />
<li />
<li
style="width: 61%;"
/>
</ul>
</div>
</div>
`;
exports[`Skeleton should square avatar 1`] = `
<div
class="ant-skeleton ant-skeleton-with-avatar"

View File

@ -28,6 +28,11 @@ describe('Skeleton', () => {
expect(wrapperSmall.render()).toMatchSnapshot();
});
it('should round title and paragraph', () => {
const wrapperSmall = genSkeleton({ round: true, title: true, paragraph: true });
expect(wrapperSmall.render()).toMatchSnapshot();
})
describe('avatar', () => {
it('size', () => {
const wrapperSmall = genSkeleton({ avatar: { size: 'small' } });

View File

@ -25,6 +25,7 @@ Provide a placeholder while you wait for content to load, or to visualise conten
| loading | Display the skeleton when `true` | boolean | - |
| paragraph | Show paragraph placeholder | boolean \| [SkeletonParagraphProps](#SkeletonParagraphProps) | true |
| title | Show title placeholder | boolean \| [SkeletonTitleProps](#SkeletonTitleProps) | true |
| round | Show paragraph and title radius when `true` | boolean | false |
### SkeletonAvatarProps

View File

@ -26,6 +26,7 @@ cols: 1
| loading | 为 `true` 时,显示占位图。反之则直接展示子组件 | boolean | - |
| paragraph | 是否显示段落占位图 | boolean \| [SkeletonParagraphProps](#SkeletonParagraphProps) | true |
| title | 是否显示标题占位图 | boolean \| [SkeletonTitleProps](#SkeletonTitleProps) | true |
| round | 为 `true` 时,段落和标题显示圆角 | boolean | false |
### SkeletonAvatarProps

View File

@ -72,6 +72,13 @@
}
}
&-round &-content {
.@{skeleton-title-prefix-cls},
.@{skeleton-paragraph-prefix-cls} > li {
border-radius: 100px;
}
}
// With active animation
&.@{skeleton-prefix-cls}-active {
& .@{skeleton-prefix-cls}-content {

View File

@ -964,7 +964,11 @@ exports[`renders ./components/slider/demo/show-tooltip.md correctly 1`] = `
>
<div
class="ant-tooltip-arrow"
/>
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"

View File

@ -64,7 +64,11 @@ exports[`Slider should render in RTL direction 1`] = `
>
<div
class="ant-tooltip-arrow"
/>
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
@ -91,7 +95,11 @@ exports[`Slider should show tooltip when hovering slider handler 1`] = `
>
<div
class="ant-tooltip-arrow"
/>
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
@ -113,7 +121,11 @@ exports[`Slider should show tooltip when hovering slider handler 2`] = `
>
<div
class="ant-tooltip-arrow"
/>
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"

View File

@ -1,516 +1,17 @@
/* stylelint-disable at-rule-no-unknown */
// Reboot
//
// Normalization of HTML elements, manually forked from Normalize.css to remove
// styles targeting irrelevant browsers while applying new styles.
//
// Normalize is licensed MIT. https://github.com/necolas/normalize.css
// HTML & Body reset
@{html-selector},
body {
.square(100%);
}
// remove the clear button of a text input control in IE10+
input::-ms-clear,
input::-ms-reveal {
display: none;
}
// Document
//
// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
// 2. Change the default font family in all browsers.
// 3. Correct the line height in all browsers.
// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.
// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so
// we force a non-overlapping, non-auto-hiding scrollbar to counteract.
// 6. Change the default tap highlight to be completely transparent in iOS.
*,
*::before,
*::after {
box-sizing: border-box; // 1
}
@{html-selector} {
font-family: sans-serif; // 2
line-height: 1.15; // 3
-webkit-text-size-adjust: 100%; // 4
-ms-text-size-adjust: 100%; // 4
-ms-overflow-style: scrollbar; // 5
-webkit-tap-highlight-color: fade(@black, 0%); // 6
}
// IE10+ doesn't honor `<meta name="viewport">` in some cases.
@-ms-viewport {
width: device-width;
}
// Shim for "new" HTML5 structural elements to display correctly (IE10, older browsers)
article,
aside,
dialog,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section {
display: block;
}
// Body
//
// 1. remove the margin in all browsers.
// 2. As a best practice, apply a default `body-background`.
body {
margin: 0; // 1
color: @text-color;
font-size: @font-size-base;
font-family: @font-family;
font-variant: @font-variant-base;
line-height: @line-height-base;
background-color: @body-background; // 2
font-feature-settings: @font-feature-settings-base;
}
// Suppress the focus outline on elements that cannot be accessed via keyboard.
// This prevents an unwanted focus outline from appearing around elements that
// might still respond to pointer events.
//
// Credit: https://github.com/suitcss/base
[tabindex='-1']:focus {
outline: none !important;
}
// Content grouping
//
// 1. Add the correct box sizing in Firefox.
// 2. Show the overflow in Edge and IE.
hr {
box-sizing: content-box; // 1
height: 0; // 1
overflow: visible; // 2
}
//
// Typography
//
// remove top margins from headings
//
// By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top
// margin for easier control within type scales as it avoids margin collapsing.
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 0;
margin-bottom: 0.5em;
color: @heading-color;
font-weight: 500;
}
// Reset margins on paragraphs
//
// Similarly, the top margin on `<p>`s get reset. However, we also reset the
// bottom margin to use `em` units instead of `em`.
p {
margin-top: 0;
margin-bottom: 1em;
}
// Abbreviations
//
// 1. remove the bottom border in Firefox 39-.
// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
// 3. Add explicit cursor to indicate changed behavior.
// 4. Duplicate behavior to the data-* attribute for our tooltip plugin
abbr[title],
abbr[data-original-title] {
// 4
text-decoration: underline; // 2
text-decoration: underline dotted; // 2
border-bottom: 0; // 1
cursor: help; // 3
}
address {
margin-bottom: 1em;
font-style: normal;
line-height: inherit;
}
input[type='text'],
input[type='password'],
input[type='number'],
textarea {
-webkit-appearance: none;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1em;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 500;
}
dd {
margin-bottom: 0.5em;
margin-left: 0; // Undo browser default
}
blockquote {
margin: 0 0 1em;
}
dfn {
font-style: italic; // Add the correct font style in Android 4.3-
}
b,
strong {
font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari
}
small {
font-size: 80%; // Add the correct font size in all browsers
}
//
// Prevent `sub` and `sup` elements from affecting the line height in
// all browsers.
//
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
//
// Links
//
a {
color: @link-color;
text-decoration: @link-decoration;
background-color: transparent; // remove the gray background on active links in IE 10.
outline: none;
cursor: pointer;
transition: color 0.3s;
-webkit-text-decoration-skip: objects; // remove gaps in links underline in iOS 8+ and Safari 8+.
&:hover {
color: @link-hover-color;
// Config global less under antd
[class^=~'@{ant-prefix}-'],
[class*=~' @{ant-prefix}-'] {
// remove the clear button of a text input control in IE10+
&::-ms-clear,
input::-ms-clear,
input::-ms-reveal {
display: none;
}
&:active {
color: @link-active-color;
}
&:active,
&:hover {
text-decoration: @link-hover-decoration;
outline: 0;
}
// https://github.com/ant-design/ant-design/issues/22503
&:focus {
text-decoration: @link-focus-decoration;
outline: @link-focus-outline;
}
&[disabled] {
color: @disabled-color;
cursor: not-allowed;
pointer-events: none;
&,
*,
*::before,
*::after {
box-sizing: border-box; // 1
}
}
//
// Code
//
pre,
code,
kbd,
samp {
font-size: 1em; // Correct the odd `em` font sizing in all browsers.
font-family: @code-family;
}
pre {
// remove browser default top margin
margin-top: 0;
// Reset browser default of `1em` to use `em`s
margin-bottom: 1em;
// Don't allow content to break outside
overflow: auto;
}
//
// Figures
//
figure {
// Apply a consistent margin strategy (matches our type styles).
margin: 0 0 1em;
}
//
// Images and content
//
img {
vertical-align: middle;
border-style: none; // remove the border on images inside links in IE 10-.
}
svg:not(:root) {
overflow: hidden; // Hide the overflow in IE
}
// Avoid 300ms click delay on touch devices that support the `touch-action` CSS property.
//
// In particular, unlike most other browsers, IE11+Edge on Windows 10 on touch devices and IE Mobile 10-11
// DON'T remove the click delay when `<meta name="viewport" content="width=device-width">` is present.
// However, they DO support emoving the click delay via `touch-action: manipulation`.
// See:
// * https://getbootstrap.com/docs/4.0/content/reboot/#click-delay-optimization-for-touch
// * http://caniuse.com/#feat=css-touch-action
// * https://patrickhlauke.github.io/touch/tests/results/#suppressing-300ms-delay
a,
area,
button,
[role='button'],
input:not([type='range']),
label,
select,
summary,
textarea {
touch-action: manipulation;
}
//
// Tables
//
table {
border-collapse: collapse; // Prevent double borders
}
caption {
padding-top: 0.75em;
padding-bottom: 0.3em;
color: @text-color-secondary;
text-align: left;
caption-side: bottom;
}
th {
// Matches default `<td>` alignment by inheriting from the `<body>`, or the
// closest parent with a set `text-align`.
text-align: inherit;
}
//
// Forms
//
input,
button,
select,
optgroup,
textarea {
margin: 0; // remove the margin in Firefox and Safari
color: inherit;
font-size: inherit;
font-family: inherit;
line-height: inherit;
}
button,
input {
overflow: visible; // Show the overflow in Edge
}
button,
select {
text-transform: none; // remove the inheritance of text transform in Firefox
}
// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
// controls in Android 4.
// 2. Correct the inability to style clickable types in iOS and Safari.
button,
@{html-selector} [type="button"], /* 1 */
[type="reset"],
[type="submit"] {
-webkit-appearance: button; // 2
}
// remove inner border and padding from Firefox, but don't restore the outline like Normalize.
button::-moz-focus-inner,
[type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner,
[type='submit']::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type='radio'],
input[type='checkbox'] {
box-sizing: border-box; // 1. Add the correct box sizing in IE 10-
padding: 0; // 2. remove the padding in IE 10-
}
input[type='date'],
input[type='time'],
input[type='datetime-local'],
input[type='month'] {
// remove the default appearance of temporal inputs to avoid a Mobile Safari
// bug where setting a custom line-height prevents text from being vertically
// centered within the input.
// See https://bugs.webkit.org/show_bug.cgi?id=139848
// and https://github.com/twbs/bootstrap/issues/11266
-webkit-appearance: listbox;
}
textarea {
overflow: auto; // remove the default vertical scrollbar in IE.
// Textareas should really only resize vertically so they don't break their (horizontal) containers.
resize: vertical;
}
fieldset {
// Browsers set a default `min-width: min-content;` on fieldsets,
// unlike e.g. `<div>`s, which have `min-width: 0;` by default.
// So we reset that to ensure fieldsets behave more like a standard block element.
// See https://github.com/twbs/bootstrap/issues/12359
// and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements
min-width: 0;
margin: 0;
// Reset the default outline behavior of fieldsets so they don't affect page layout.
padding: 0;
border: 0;
}
// 1. Correct the text wrapping in Edge and IE.
// 2. Correct the color inheritance from `fieldset` elements in IE.
legend {
display: block;
width: 100%;
max-width: 100%; // 1
margin-bottom: 0.5em;
padding: 0;
color: inherit; // 2
font-size: 1.5em;
line-height: inherit;
white-space: normal; // 1
}
progress {
vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.
}
// Correct the cursor style of incement and decement buttons in Chrome.
[type='number']::-webkit-inner-spin-button,
[type='number']::-webkit-outer-spin-button {
height: auto;
}
[type='search'] {
// This overrides the extra rounded corners on search inputs in iOS so that our
// `.form-control` class can properly style them. Note that this cannot simply
// be added to `.form-control` as it's not specific enough. For details, see
// https://github.com/twbs/bootstrap/issues/11586.
outline-offset: -2px; // 2. Correct the outline style in Safari.
-webkit-appearance: none;
}
//
// remove the inner padding and cancel buttons in Chrome and Safari on macOS.
//
[type='search']::-webkit-search-cancel-button,
[type='search']::-webkit-search-decoration {
-webkit-appearance: none;
}
//
// 1. Correct the inability to style clickable types in iOS and Safari.
// 2. Change font properties to `inherit` in Safari.
//
::-webkit-file-upload-button {
font: inherit; // 2
-webkit-appearance: button; // 1
}
//
// Correct element displays
//
output {
display: inline-block;
}
summary {
display: list-item; // Add the correct display in all browsers
}
template {
display: none; // Add the correct display in IE
}
// Always hide an element with the `hidden` HTML attribute (from PureCSS).
// Needed for proper display in IE 10-.
[hidden] {
display: none !important;
}
mark {
padding: 0.2em;
background-color: @yellow-1;
}
::selection {
color: @text-color-inverse;
background: @text-selection-bg;
}
// Utility classes
.clearfix {
.clearfix();
}

View File

@ -0,0 +1,501 @@
/* stylelint-disable at-rule-no-unknown */
// Reboot
//
// Normalization of HTML elements, manually forked from Normalize.css to remove
// styles targeting irrelevant browsers while applying new styles.
//
// Normalize is licensed MIT. https://github.com/necolas/normalize.css
// HTML & Body reset
@{html-selector},
body {
.square(100%);
}
// remove the clear button of a text input control in IE10+
input::-ms-clear,
input::-ms-reveal {
display: none;
}
// Document
//
// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
// 2. Change the default font family in all browsers.
// 3. Correct the line height in all browsers.
// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.
// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so
// we force a non-overlapping, non-auto-hiding scrollbar to counteract.
// 6. Change the default tap highlight to be completely transparent in iOS.
*,
*::before,
*::after {
box-sizing: border-box; // 1
}
@{html-selector} {
font-family: sans-serif; // 2
line-height: 1.15; // 3
-webkit-text-size-adjust: 100%; // 4
-ms-text-size-adjust: 100%; // 4
-ms-overflow-style: scrollbar; // 5
-webkit-tap-highlight-color: fade(@black, 0%); // 6
}
// IE10+ doesn't honor `<meta name="viewport">` in some cases.
@-ms-viewport {
width: device-width;
}
// Body
//
// 1. remove the margin in all browsers.
// 2. As a best practice, apply a default `body-background`.
body {
margin: 0; // 1
color: @text-color;
font-size: @font-size-base;
font-family: @font-family;
font-variant: @font-variant-base;
line-height: @line-height-base;
background-color: @body-background; // 2
font-feature-settings: @font-feature-settings-base;
}
// Suppress the focus outline on elements that cannot be accessed via keyboard.
// This prevents an unwanted focus outline from appearing around elements that
// might still respond to pointer events.
//
// Credit: https://github.com/suitcss/base
[tabindex='-1']:focus {
outline: none !important;
}
// Content grouping
//
// 1. Add the correct box sizing in Firefox.
// 2. Show the overflow in Edge and IE.
hr {
box-sizing: content-box; // 1
height: 0; // 1
overflow: visible; // 2
}
//
// Typography
//
// remove top margins from headings
//
// By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top
// margin for easier control within type scales as it avoids margin collapsing.
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 0;
margin-bottom: 0.5em;
color: @heading-color;
font-weight: 500;
}
// Reset margins on paragraphs
//
// Similarly, the top margin on `<p>`s get reset. However, we also reset the
// bottom margin to use `em` units instead of `em`.
p {
margin-top: 0;
margin-bottom: 1em;
}
// Abbreviations
//
// 1. remove the bottom border in Firefox 39-.
// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
// 3. Add explicit cursor to indicate changed behavior.
// 4. Duplicate behavior to the data-* attribute for our tooltip plugin
abbr[title],
abbr[data-original-title] {
// 4
text-decoration: underline; // 2
text-decoration: underline dotted; // 2
border-bottom: 0; // 1
cursor: help; // 3
}
address {
margin-bottom: 1em;
font-style: normal;
line-height: inherit;
}
input[type='text'],
input[type='password'],
input[type='number'],
textarea {
-webkit-appearance: none;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1em;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 500;
}
dd {
margin-bottom: 0.5em;
margin-left: 0; // Undo browser default
}
blockquote {
margin: 0 0 1em;
}
dfn {
font-style: italic; // Add the correct font style in Android 4.3-
}
b,
strong {
font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari
}
small {
font-size: 80%; // Add the correct font size in all browsers
}
//
// Prevent `sub` and `sup` elements from affecting the line height in
// all browsers.
//
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
//
// Links
//
a {
color: @link-color;
text-decoration: @link-decoration;
background-color: transparent; // remove the gray background on active links in IE 10.
outline: none;
cursor: pointer;
transition: color 0.3s;
-webkit-text-decoration-skip: objects; // remove gaps in links underline in iOS 8+ and Safari 8+.
&:hover {
color: @link-hover-color;
}
&:active {
color: @link-active-color;
}
&:active,
&:hover {
text-decoration: @link-hover-decoration;
outline: 0;
}
// https://github.com/ant-design/ant-design/issues/22503
&:focus {
text-decoration: @link-focus-decoration;
outline: @link-focus-outline;
}
&[disabled] {
color: @disabled-color;
cursor: not-allowed;
pointer-events: none;
}
}
//
// Code
//
pre,
code,
kbd,
samp {
font-size: 1em; // Correct the odd `em` font sizing in all browsers.
font-family: @code-family;
}
pre {
// remove browser default top margin
margin-top: 0;
// Reset browser default of `1em` to use `em`s
margin-bottom: 1em;
// Don't allow content to break outside
overflow: auto;
}
//
// Figures
//
figure {
// Apply a consistent margin strategy (matches our type styles).
margin: 0 0 1em;
}
//
// Images and content
//
img {
vertical-align: middle;
border-style: none; // remove the border on images inside links in IE 10-.
}
svg:not(:root) {
overflow: hidden; // Hide the overflow in IE
}
// Avoid 300ms click delay on touch devices that support the `touch-action` CSS property.
//
// In particular, unlike most other browsers, IE11+Edge on Windows 10 on touch devices and IE Mobile 10-11
// DON'T remove the click delay when `<meta name="viewport" content="width=device-width">` is present.
// However, they DO support emoving the click delay via `touch-action: manipulation`.
// See:
// * https://getbootstrap.com/docs/4.0/content/reboot/#click-delay-optimization-for-touch
// * http://caniuse.com/#feat=css-touch-action
// * https://patrickhlauke.github.io/touch/tests/results/#suppressing-300ms-delay
a,
area,
button,
[role='button'],
input:not([type='range']),
label,
select,
summary,
textarea {
touch-action: manipulation;
}
//
// Tables
//
table {
border-collapse: collapse; // Prevent double borders
}
caption {
padding-top: 0.75em;
padding-bottom: 0.3em;
color: @text-color-secondary;
text-align: left;
caption-side: bottom;
}
th {
// Matches default `<td>` alignment by inheriting from the `<body>`, or the
// closest parent with a set `text-align`.
text-align: inherit;
}
//
// Forms
//
input,
button,
select,
optgroup,
textarea {
margin: 0; // remove the margin in Firefox and Safari
color: inherit;
font-size: inherit;
font-family: inherit;
line-height: inherit;
}
button,
input {
overflow: visible; // Show the overflow in Edge
}
button,
select {
text-transform: none; // remove the inheritance of text transform in Firefox
}
// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
// controls in Android 4.
// 2. Correct the inability to style clickable types in iOS and Safari.
button,
@{html-selector} [type="button"], /* 1 */
[type="reset"],
[type="submit"] {
-webkit-appearance: button; // 2
}
// remove inner border and padding from Firefox, but don't restore the outline like Normalize.
button::-moz-focus-inner,
[type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner,
[type='submit']::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type='radio'],
input[type='checkbox'] {
box-sizing: border-box; // 1. Add the correct box sizing in IE 10-
padding: 0; // 2. remove the padding in IE 10-
}
input[type='date'],
input[type='time'],
input[type='datetime-local'],
input[type='month'] {
// remove the default appearance of temporal inputs to avoid a Mobile Safari
// bug where setting a custom line-height prevents text from being vertically
// centered within the input.
// See https://bugs.webkit.org/show_bug.cgi?id=139848
// and https://github.com/twbs/bootstrap/issues/11266
-webkit-appearance: listbox;
}
textarea {
overflow: auto; // remove the default vertical scrollbar in IE.
// Textareas should really only resize vertically so they don't break their (horizontal) containers.
resize: vertical;
}
fieldset {
// Browsers set a default `min-width: min-content;` on fieldsets,
// unlike e.g. `<div>`s, which have `min-width: 0;` by default.
// So we reset that to ensure fieldsets behave more like a standard block element.
// See https://github.com/twbs/bootstrap/issues/12359
// and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements
min-width: 0;
margin: 0;
// Reset the default outline behavior of fieldsets so they don't affect page layout.
padding: 0;
border: 0;
}
// 1. Correct the text wrapping in Edge and IE.
// 2. Correct the color inheritance from `fieldset` elements in IE.
legend {
display: block;
width: 100%;
max-width: 100%; // 1
margin-bottom: 0.5em;
padding: 0;
color: inherit; // 2
font-size: 1.5em;
line-height: inherit;
white-space: normal; // 1
}
progress {
vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.
}
// Correct the cursor style of incement and decement buttons in Chrome.
[type='number']::-webkit-inner-spin-button,
[type='number']::-webkit-outer-spin-button {
height: auto;
}
[type='search'] {
// This overrides the extra rounded corners on search inputs in iOS so that our
// `.form-control` class can properly style them. Note that this cannot simply
// be added to `.form-control` as it's not specific enough. For details, see
// https://github.com/twbs/bootstrap/issues/11586.
outline-offset: -2px; // 2. Correct the outline style in Safari.
-webkit-appearance: none;
}
//
// remove the inner padding and cancel buttons in Chrome and Safari on macOS.
//
[type='search']::-webkit-search-cancel-button,
[type='search']::-webkit-search-decoration {
-webkit-appearance: none;
}
//
// 1. Correct the inability to style clickable types in iOS and Safari.
// 2. Change font properties to `inherit` in Safari.
//
::-webkit-file-upload-button {
font: inherit; // 2
-webkit-appearance: button; // 1
}
//
// Correct element displays
//
output {
display: inline-block;
}
summary {
display: list-item; // Add the correct display in all browsers
}
template {
display: none; // Add the correct display in IE
}
// Always hide an element with the `hidden` HTML attribute (from PureCSS).
// Needed for proper display in IE 10-.
[hidden] {
display: none !important;
}
mark {
padding: 0.2em;
background-color: @yellow-1;
}
::selection {
color: @text-color-inverse;
background: @text-selection-bg;
}
// Utility classes
.clearfix {
.clearfix();
}

View File

@ -1,4 +1,5 @@
@import '../mixins/index';
@import 'base';
@import 'global';
@import 'iconfont';
@import 'motion';

View File

@ -221,6 +221,7 @@
@btn-default-ghost-border: fade(@white, 25%);
@btn-link-ghost-color: @text-color;
@btn-text-hover-bg: rgba(255, 255, 255, 0.03);
// Checkbox
// ---

View File

@ -219,6 +219,8 @@
@btn-group-border: @primary-5;
@btn-link-ghost-color: @component-background;
@btn-link-hover-bg: transparent;
@btn-text-hover-bg: rgba(0, 0, 0, 0.018);
// Checkbox
@checkbox-size: 16px;
@ -235,6 +237,9 @@
@descriptions-middle-padding: @padding-sm @padding-lg;
@descriptions-small-padding: @padding-xs @padding-md;
@descriptions-item-padding-bottom: @padding-md;
@descriptions-item-trailing-colon: true;
@descriptions-item-label-colon-margin-right: 8px;
@descriptions-item-label-colon-margin-left: 2px;
// Dropdown
@dropdown-selected-color: @primary-color;
@ -395,6 +400,7 @@
@input-disabled-bg: @disabled-bg;
@input-outline-offset: 0 0;
@input-icon-hover-color: fade(@black, 85%);
@input-disabled-color: @disabled-color;
// Mentions
// ---
@ -512,7 +518,9 @@
@menu-popup-bg: @component-background;
@menu-item-color: @text-color;
@menu-highlight-color: @primary-color;
@menu-highlight-danger-color: @error-color;
@menu-item-active-bg: @primary-1;
@menu-item-active-danger-bg: @red-1;
@menu-item-active-border-width: 3px;
@menu-item-group-title-color: @text-color-secondary;
@menu-item-vertical-margin: 4px;
@ -527,11 +535,13 @@
// dark theme
@menu-dark-color: @text-color-secondary-dark;
@menu-dark-danger-color: @error-color;
@menu-dark-bg: @layout-header-background;
@menu-dark-arrow-color: #fff;
@menu-dark-submenu-bg: #000c17;
@menu-dark-highlight-color: #fff;
@menu-dark-item-active-bg: @primary-color;
@menu-dark-item-active-danger-bg: @error-color;
@menu-dark-selected-item-icon-color: @white;
@menu-dark-selected-item-text-color: @white;
@menu-dark-item-hover-bg: transparent;

View File

@ -76,12 +76,7 @@ describe('Table', () => {
};
const wrapper = mount(<Table loading={loading} />);
expect(wrapper.find('.ant-spin')).toHaveLength(0);
expect(
wrapper
.find('.ant-table-placeholder')
.hostNodes()
.text(),
).not.toEqual('');
expect(wrapper.find('.ant-table-placeholder').hostNodes().text()).not.toEqual('');
loading.spinning = true;
wrapper.setProps({ loading });
@ -179,4 +174,51 @@ describe('Table', () => {
);
wrapper.simulate('touchmove');
});
it('renders ellipsis by showTitle option', () => {
const data = [
{
id: '1',
age: 32,
},
{
id: '2',
age: 42,
},
];
const columns = [
{ title: 'id', dataKey: 'id', ellipsis: { showTitle: false } },
{ title: 'age', dataKey: 'age', ellipsis: { showTitle: false } },
];
const wrapper = mount(<Table columns={columns} dataSource={data} />);
wrapper.find('td').forEach(td => {
expect(td.hasClass('ant-table-cell-ellipsis')).toBeTruthy();
});
});
it('not renders ellipsis origin html title', () => {
const data = [
{
id: '1',
age: 32,
},
{
id: '2',
age: 42,
},
];
const columns = [
{ title: 'id', dataKey: 'id', ellipsis: { showTitle: true } },
{ title: 'age', dataKey: 'age', ellipsis: { showTitle: true } },
];
const wrapper = mount(<Table columns={columns} dataSource={data} />);
wrapper.find('.ant-table-thead th').forEach(td => {
expect(td.getDOMNode().attributes.getNamedItem('title')).toBeTruthy();
});
wrapper.find('.ant-table-tbody td').forEach(td => {
expect(td.getDOMNode().attributes.getNamedItem('title')).toBeFalsy();
});
});
});

View File

@ -233,7 +233,11 @@ exports[`Table.filter renders custom filter icon with right Tooltip title 1`] =
>
<div
class="ant-tooltip-arrow"
/>
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"

View File

@ -4674,6 +4674,300 @@ exports[`renders ./components/table/demo/ellipsis.md correctly 1`] = `
</div>
`;
exports[`renders ./components/table/demo/ellipsis-custom-tooltip.md correctly 1`] = `
<div
class="ant-table-wrapper"
>
<div
class="ant-spin-nested-loading"
>
<div
class="ant-spin-container"
>
<div
class="ant-table"
>
<div
class="ant-table-container"
>
<div
class="ant-table-content"
>
<table
style="table-layout:fixed"
>
<colgroup>
<col
style="width:150px;min-width:150px"
/>
<col
style="width:80px;min-width:80px"
/>
</colgroup>
<thead
class="ant-table-thead"
>
<tr>
<th
class="ant-table-cell"
>
Name
</th>
<th
class="ant-table-cell"
>
Age
</th>
<th
class="ant-table-cell ant-table-cell-ellipsis"
title="Address"
>
Address
</th>
<th
class="ant-table-cell ant-table-cell-ellipsis"
title="Long Column Long Column Long Column"
>
Long Column Long Column Long Column
</th>
<th
class="ant-table-cell ant-table-cell-ellipsis"
title="Long Column Long Column"
>
Long Column Long Column
</th>
<th
class="ant-table-cell ant-table-cell-ellipsis"
title="Long Column"
>
Long Column
</th>
</tr>
</thead>
<tbody
class="ant-table-tbody"
>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="1"
>
<td
class="ant-table-cell"
>
<a>
John Brown
</a>
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
New York No. 1 Lake Park, New York No. 1 Lake Park
</span>
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
New York No. 1 Lake Park, New York No. 1 Lake Park
</span>
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
New York No. 1 Lake Park, New York No. 1 Lake Park
</span>
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
New York No. 1 Lake Park, New York No. 1 Lake Park
</span>
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="2"
>
<td
class="ant-table-cell"
>
<a>
Jim Green
</a>
</td>
<td
class="ant-table-cell"
>
42
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
London No. 2 Lake Park, London No. 2 Lake Park
</span>
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
London No. 2 Lake Park, London No. 2 Lake Park
</span>
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
London No. 2 Lake Park, London No. 2 Lake Park
</span>
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
London No. 2 Lake Park, London No. 2 Lake Park
</span>
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="3"
>
<td
class="ant-table-cell"
>
<a>
Joe Black
</a>
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
Sidney No. 1 Lake Park, Sidney No. 1 Lake Park
</span>
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
Sidney No. 1 Lake Park, Sidney No. 1 Lake Park
</span>
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
Sidney No. 1 Lake Park, Sidney No. 1 Lake Park
</span>
</td>
<td
class="ant-table-cell ant-table-cell-ellipsis"
>
<span>
Sidney No. 1 Lake Park, Sidney No. 1 Lake Park
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<ul
class="ant-pagination ant-table-pagination ant-table-pagination-right"
unselectable="unselectable"
>
<li
aria-disabled="true"
class="ant-pagination-prev ant-pagination-disabled"
title="Previous Page"
>
<a
class="ant-pagination-item-link"
disabled=""
>
<span
aria-label="left"
class="anticon anticon-left"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
/>
</svg>
</span>
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
tabindex="0"
title="1"
>
<a>
1
</a>
</li>
<li
aria-disabled="true"
class="ant-pagination-next ant-pagination-disabled"
title="Next Page"
>
<a
class="ant-pagination-item-link"
disabled=""
>
<span
aria-label="right"
class="anticon anticon-right"
role="img"
>
<svg
aria-hidden="true"
class=""
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>
</a>
</li>
</ul>
</div>
</div>
</div>
`;
exports[`renders ./components/table/demo/expand.md correctly 1`] = `
<div
class="ant-table-wrapper"

View File

@ -0,0 +1,109 @@
---
order: 29
title:
en-US: ellipsis column custom tooltip
zh-CN: 自定义单元格省略提示
---
## zh-CN
设置 `column.ellipsis.showTitle` 关闭单元格内容自动省略后默认的 `title` 提示, 使用 `Tooltip` 替代。
## en-US
Ellipsis cell content via setting `column.ellipsis.showTitle`, use `Tooltip` instead of the html title attribute.
```jsx
import { Table, Tooltip } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: text => <a>{text}</a>,
width: 150,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 80,
},
{
title: 'Address',
dataIndex: 'address',
key: 'address 1',
ellipsis: {
showTitle: false,
},
render: address => (
<Tooltip placement="topLeft" title={address}>
{address}
</Tooltip>
),
},
{
title: 'Long Column Long Column Long Column',
dataIndex: 'address',
key: 'address 2',
ellipsis: {
showTitle: false,
},
render: address => (
<Tooltip placement="topLeft" title={address}>
{address}
</Tooltip>
),
},
{
title: 'Long Column Long Column',
dataIndex: 'address',
key: 'address 3',
ellipsis: {
showTitle: false,
},
render: address => (
<Tooltip placement="topLeft" title={address}>
{address}
</Tooltip>
),
},
{
title: 'Long Column',
dataIndex: 'address',
key: 'address 4',
ellipsis: {
showTitle: false,
},
render: address => (
<Tooltip placement="topLeft" title={address}>
{address}
</Tooltip>
),
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park, New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park, London No. 2 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park, Sidney No. 1 Lake Park',
},
];
ReactDOM.render(<Table columns={columns} dataSource={data} />, mountNode);
```

View File

@ -111,36 +111,37 @@ Same as `onRow` `onHeaderRow` `onCell` `onHeaderCell`
One of the Table `columns` prop for describing the table's columns, Column has the same API.
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| align | specify which way that column is aligned | `left` \| `right` \| `center` | `left` |
| ellipsis | ellipsis cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is true. | boolean | false |
| className | className of this column | string | - |
| colSpan | Span of this column's title | number | - |
| dataIndex | Display field of the data record, support nest path by string array | string \| string\[] | - |
| defaultFilteredValue | Default filtered values | string\[] | - | |
| defaultSortOrder | Default order of sorted values | `ascend` \| `descend` | - |
| filterDropdown | Customized filter overlay | React.ReactNode \| (props: [FilterDropdownProps](https://git.io/fjP5h)) => React.ReactNode | - |
| filterDropdownVisible | Whether `filterDropdown` is visible | boolean | - |
| filtered | Whether the `dataSource` is filtered | boolean | `false` |
| filteredValue | Controlled filtered value, filter icon will highlight | string\[] | - |
| filterIcon | Customized filter icon | ReactNode\|(filtered: boolean) => ReactNode | `false` |
| filterMultiple | Whether multiple filters can be selected | boolean | `true` |
| filters | Filter menu config | object\[] | - |
| fixed | (IE not support) Set column to be fixed: `true`(same as left) `'left'` `'right'` | boolean\|string | `false` |
| key | Unique key of this column, you can ignore this prop if you've set a unique `dataIndex` | string | - |
| render | Renderer of the table cell. The return value should be a ReactNode, or an object for [colSpan/rowSpan config](#components-table-demo-colspan-rowspan) | Function(text, record, index) {} | - |
| responsive | The list of breakpoints at which to display this column. Always visible if not set. | [Breakpoint](https://github.com/ant-design/ant-design/blob/015109b42b85c63146371b4e32b883cf97b088e8/components/_util/responsiveObserve.ts#L1)\[] | - |
| sorter | Sort function for local sort, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. If you need sort buttons only, set to `true` | Function\|boolean | - |
| sortOrder | Order of sorted values: `'ascend'` `'descend'` `false` | boolean\|string | - |
| sortDirections | supported sort way, override `sortDirections` in `Table`, could be `'ascend'`, `'descend'` | Array | `['ascend', 'descend']` |
| title | Title of this column | ReactNode\|({ sortOrder, sortColumn, filters }) => ReactNode | - |
| width | Width of this column ([width not working?](https://github.com/ant-design/ant-design/issues/13825#issuecomment-449889241)) | string\|number | - |
| onCell | Set props on per cell | Function(record, rowIndex) | - |
| onFilter | Callback executed when the confirm filter button is clicked | Function | - |
| onFilterDropdownVisibleChange | Callback executed when `filterDropdownVisible` is changed | function(visible) {} | - |
| onHeaderCell | Set props on per header cell | Function(column) | - |
| showSorterTooltip | header show next sorter direction tooltip, override `showSorterTooltip` in table | boolean | `true` |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| align | specify which way that column is aligned | `left` \| `right` \| `center` | `left` | |
| ellipsis | ellipsis cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is `true` or `{ showTitle?: boolean }`. | boolean \| { showTitle?: boolean } | false | showTitle: 4.3.0 |
| className | className of this column | string | - | |
| colSpan | Span of this column's title | number | - | |
| dataIndex | Display field of the data record, support nest path by string array | string \| string\[] | - | |
| defaultFilteredValue | Default filtered values | string\[] | - | | |
| defaultSortOrder | Default order of sorted values | `ascend` \| `descend` | - | |
| filterDropdown | Customized filter overlay | React.ReactNode \| (props: [FilterDropdownProps](https://git.io/fjP5h)) => React.ReactNode | - | |
| filterDropdownVisible | Whether `filterDropdown` is visible | boolean | - | |
| filtered | Whether the `dataSource` is filtered | boolean | `false` | |
| filteredValue | Controlled filtered value, filter icon will highlight | string\[] | - | |
| filterIcon | Customized filter icon | ReactNode\|(filtered: boolean) => ReactNode | `false` | |
| filterMultiple | Whether multiple filters can be selected | boolean | `true` | |
| filters | Filter menu config | object\[] | - | |
| fixed | (IE not support) Set column to be fixed: `true`(same as left) `'left'` `'right'` | boolean\|string | `false` | |
| key | Unique key of this column, you can ignore this prop if you've set a unique `dataIndex` | string | - | |
| render | Renderer of the table cell. The return value should be a ReactNode, or an object for [colSpan/rowSpan config](#components-table-demo-colspan-rowspan) | Function(text, record, index) {} | - | |
| responsive | The list of breakpoints at which to display this column. Always visible if not set. | [Breakpoint](https://github.com/ant-design/ant-design/blob/015109b42b85c63146371b4e32b883cf97b088e8/components/_util/responsiveObserve.ts#L1)\[] | - | 4.2.0 |
| shouldCellUpdate | Control cell render logic | (record) => boolean | - | 4.3.0 |
| sorter | Sort function for local sort, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. If you need sort buttons only, set to `true` | Function\|boolean | - | |
| sortOrder | Order of sorted values: `'ascend'` `'descend'` `false` | boolean\|string | - | |
| sortDirections | supported sort way, override `sortDirections` in `Table`, could be `'ascend'`, `'descend'` | Array | `['ascend', 'descend']` | |
| title | Title of this column | ReactNode\|({ sortOrder, sortColumn, filters }) => ReactNode | - | |
| width | Width of this column ([width not working?](https://github.com/ant-design/ant-design/issues/13825#issuecomment-449889241)) | string\|number | - | |
| onCell | Set props on per cell | Function(record, rowIndex) | - | |
| onFilter | Callback executed when the confirm filter button is clicked | Function | - | |
| onFilterDropdownVisibleChange | Callback executed when `filterDropdownVisible` is changed | function(visible) {} | - | |
| onHeaderCell | Set props on per header cell | Function(column) | - | |
| showSorterTooltip | header show next sorter direction tooltip, override `showSorterTooltip` in table | boolean | `true` | |
### ColumnGroup
@ -293,4 +294,4 @@ In order to improve user experience, Pagination show size changer by default whe
### Why Table fully render when state change?
Table can not tell what state used in `columns.render`, so it always need fully render to avoid sync issue. But you can use `components` to customize component for conditional render. ref [#23763](https://github.com/ant-design/ant-design/issues/23763).
Table can not tell what state used in `columns.render`, so it always need fully render to avoid sync issue. You can use `column.shouldCellUpdate` to control render.

View File

@ -116,36 +116,37 @@ const columns = [
列描述数据对象,是 columns 中的一项Column 使用相同的 API。
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| align | 设置列的对齐方式 | `left` \| `right` \| `center` | `left` |
| ellipsis | 超过宽度将自动省略,暂不支持和排序筛选一起使用。<br />设置为 `true` 时,表格布局将变成 `tableLayout="fixed"`。 | boolean | false |
| className | 列样式类名 | string | - |
| colSpan | 表头列合并,设置为 0 时,不渲染 | number | - |
| dataIndex | 列数据在数据项中对应的路径,支持通过数组查询嵌套路径 | string \| string\[] | - |
| defaultFilteredValue | 默认筛选值 | string\[] | - |
| defaultSortOrder | 默认排序顺序 | `ascend` \| `descend` | - |
| filterDropdown | 可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互 | React.ReactNode \| (props: [FilterDropdownProps](https://git.io/fjP5h)) => React.ReactNode | - |
| filterDropdownVisible | 用于控制自定义筛选菜单是否可见 | boolean | - |
| filtered | 标识数据是否经过过滤,筛选图标会高亮 | boolean | false |
| filteredValue | 筛选的受控属性,外界可用此控制列的筛选状态,值为已筛选的 value 数组 | string\[] | - |
| filterIcon | 自定义 filter 图标。 | ReactNode\|(filtered: boolean) => ReactNode | false |
| filterMultiple | 是否多选 | boolean | true |
| filters | 表头的筛选菜单项 | object\[] | - |
| fixed | IE 下无效)列是否固定,可选 `true`(等效于 left) `'left'` `'right'` | boolean\|string | false |
| key | React 需要的 key如果已经设置了唯一的 `dataIndex`,可以忽略这个属性 | string | - |
| render | 生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引,@return 里面可以设置表格[行/列合并](#components-table-demo-colspan-rowspan) | Function(text, record, index) {} | - |
| responsive | 响应式 breakpoint 配置列表。未设置则始终可见。 | [Breakpoint](https://github.com/ant-design/ant-design/blob/015109b42b85c63146371b4e32b883cf97b088e8/components/_util/responsiveObserve.ts#L1)\[] | - |
| sorter | 排序函数,本地排序使用一个函数(参考 [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 的 compareFunction),需要服务端排序可设为 true | Function\|boolean | - |
| sortOrder | 排序的受控属性,外界可用此控制列的排序,可设置为 `'ascend'` `'descend'` `false` | boolean\|string | - |
| sortDirections | 支持的排序方式,覆盖`Table`中`sortDirections` 取值为 `'ascend'` `'descend'` | Array | `['ascend', 'descend']` |
| title | 列头显示文字(函数用法 `3.10.0` 后支持) | ReactNode\|({ sortOrder, sortColumn, filters }) => ReactNode | - |
| width | 列宽度([指定了也不生效?](https://github.com/ant-design/ant-design/issues/13825#issuecomment-449889241) | string\|number | - |
| onCell | 设置单元格属性 | Function(record, rowIndex) | - |
| onFilter | 本地模式下,确定筛选的运行函数 | Function | - |
| onFilterDropdownVisibleChange | 自定义筛选菜单可见变化时调用 | function(visible) {} | - |
| onHeaderCell | 设置头部单元格属性 | Function(column) | - |
| showSorterTooltip | 表头显示下一次排序的 tooltip 提示, 覆盖 table 中`showSorterTooltip` | boolean | `true` |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| align | 设置列的对齐方式 | `left` \| `right` \| `center` | `left` | |
| ellipsis | 超过宽度将自动省略,暂不支持和排序筛选一起使用。<br />设置为 `true``{ showTitle?: boolean }` 时,表格布局将变成 `tableLayout="fixed"`。 | boolean \| { showTitle?: boolean } | false | showTitle: 4.3.0 |
| className | 列样式类名 | string | - | |
| colSpan | 表头列合并,设置为 0 时,不渲染 | number | - | |
| dataIndex | 列数据在数据项中对应的路径,支持通过数组查询嵌套路径 | string \| string\[] | - | |
| defaultFilteredValue | 默认筛选值 | string\[] | - | |
| defaultSortOrder | 默认排序顺序 | `ascend` \| `descend` | - | |
| filterDropdown | 可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互 | React.ReactNode \| (props: [FilterDropdownProps](https://git.io/fjP5h)) => React.ReactNode | - | |
| filterDropdownVisible | 用于控制自定义筛选菜单是否可见 | boolean | - | |
| filtered | 标识数据是否经过过滤,筛选图标会高亮 | boolean | false | |
| filteredValue | 筛选的受控属性,外界可用此控制列的筛选状态,值为已筛选的 value 数组 | string\[] | - | |
| filterIcon | 自定义 filter 图标。 | ReactNode\|(filtered: boolean) => ReactNode | false | |
| filterMultiple | 是否多选 | boolean | true | |
| filters | 表头的筛选菜单项 | object\[] | - | |
| fixed | IE 下无效)列是否固定,可选 `true`(等效于 left) `'left'` `'right'` | boolean\|string | false | |
| key | React 需要的 key如果已经设置了唯一的 `dataIndex`,可以忽略这个属性 | string | - | |
| render | 生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引,@return 里面可以设置表格[行/列合并](#components-table-demo-colspan-rowspan) | Function(text, record, index) {} | - | |
| responsive | 响应式 breakpoint 配置列表。未设置则始终可见。 | [Breakpoint](https://github.com/ant-design/ant-design/blob/015109b42b85c63146371b4e32b883cf97b088e8/components/_util/responsiveObserve.ts#L1)\[] | - | 4.2.0 |
| shouldCellUpdate | 自定义单元格渲染时机 | (record) => boolean | - | 4.3.0 |
| sorter | 排序函数,本地排序使用一个函数(参考 [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 的 compareFunction),需要服务端排序可设为 true | Function\|boolean | - | |
| sortOrder | 排序的受控属性,外界可用此控制列的排序,可设置为 `'ascend'` `'descend'` `false` | boolean\|string | - | |
| sortDirections | 支持的排序方式,覆盖`Table`中`sortDirections` 取值为 `'ascend'` `'descend'` | Array | `['ascend', 'descend']` | |
| title | 列头显示文字(函数用法 `3.10.0` 后支持) | ReactNode\|({ sortOrder, sortColumn, filters }) => ReactNode | - | |
| width | 列宽度([指定了也不生效?](https://github.com/ant-design/ant-design/issues/13825#issuecomment-449889241) | string\|number | - | |
| onCell | 设置单元格属性 | Function(record, rowIndex) | - | |
| onFilter | 本地模式下,确定筛选的运行函数 | Function | - | |
| onFilterDropdownVisibleChange | 自定义筛选菜单可见变化时调用 | function(visible) {} | - | |
| onHeaderCell | 设置头部单元格属性 | Function(column) | - | |
| showSorterTooltip | 表头显示下一次排序的 tooltip 提示, 覆盖 table 中`showSorterTooltip` | boolean | `true` | |
### ColumnGroup
@ -297,4 +298,4 @@ Table 移除了在 v3 中废弃的 `onRowClick`、`onRowDoubleClick`、`onRowMou
### 为什么 更新 state 会导致全表渲染?
由于 `columns` 支持 `render` 方法,因而 Table 无法知道哪些单元会受到影响。你可以通过 `components` 属性来精确控制子元素的渲染,请参考 [#23763](https://github.com/ant-design/ant-design/issues/23763)
由于 `columns` 支持 `render` 方法,因而 Table 无法知道哪些单元会受到影响。你可以通过 `column.shouldCellUpdate` 来控制单元格的渲染

View File

@ -1251,15 +1251,15 @@ exports[`renders ./components/tabs/demo/editable-card.md correctly 1`] = `
<div>
Tab 1
<span
aria-label="close"
class="anticon anticon-close ant-tabs-close-x"
aria-label="close-circle"
class="anticon anticon-close-circle ant-tabs-close-x"
role="img"
tabindex="-1"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
@ -1267,7 +1267,10 @@ exports[`renders ./components/tabs/demo/editable-card.md correctly 1`] = `
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-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 670a8.32 8.32 0 00-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"
/>
<path
d="M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
</svg>
</span>
@ -1285,15 +1288,15 @@ exports[`renders ./components/tabs/demo/editable-card.md correctly 1`] = `
<div>
Tab 2
<span
aria-label="close"
class="anticon anticon-close ant-tabs-close-x"
aria-label="close-circle"
class="anticon anticon-close-circle ant-tabs-close-x"
role="img"
tabindex="-1"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
@ -1301,7 +1304,10 @@ exports[`renders ./components/tabs/demo/editable-card.md correctly 1`] = `
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-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 670a8.32 8.32 0 00-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"
/>
<path
d="M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
</svg>
</span>

View File

@ -15,6 +15,7 @@ Only card type Tabs support adding & closable. +Use `closable={false}` to disabl
```jsx
import { Tabs } from 'antd';
import { CloseCircleOutlined } from '@ant-design/icons';
const { TabPane } = Tabs;
@ -81,7 +82,12 @@ class Demo extends React.Component {
onEdit={this.onEdit}
>
{this.state.panes.map(pane => (
<TabPane tab={pane.title} key={pane.key} closable={pane.closable}>
<TabPane
tab={pane.title}
key={pane.key}
closable={pane.closable}
closeIcon={<CloseCircleOutlined />}
>
{pane.content}
</TabPane>
))}

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