Merge branch 'feature' into master-to-merge

This commit is contained in:
偏右 2019-09-14 15:40:33 +08:00 committed by GitHub
commit 1200f7fe00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 4140 additions and 1928 deletions

View File

@ -62,6 +62,8 @@ export interface AnchorProps {
) => void;
/** Scroll to target offset value, if none, it's offsetTop prop value or 0. */
targetOffset?: number;
/** Listening event when scrolling change active link */
onChange?: (currentActiveLink: string) => void;
}
export interface AnchorState {
@ -205,7 +207,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
handleScrollTo = (link: string) => {
const { offsetTop, getContainer, targetOffset } = this.props as AnchorDefaultProps;
this.setState({ activeLink: link });
this.setCurrentActiveLink(link);
const container = getContainer();
const scrollTop = getScroll(container, true);
const sharpLinkMatch = sharpMatcherRegx.exec(link);
@ -234,21 +236,30 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
this.inkNode = node;
};
setCurrentActiveLink = (link: string) => {
const { activeLink } = this.state;
const { onChange } = this.props;
if (activeLink !== link) {
this.setState({
activeLink: link,
});
if (onChange) {
onChange(link);
}
}
};
handleScroll = () => {
if (this.animating) {
return;
}
const { activeLink } = this.state;
const { offsetTop, bounds, targetOffset } = this.props;
const currentActiveLink = this.getCurrentAnchor(
targetOffset !== undefined ? targetOffset : offsetTop || 0,
bounds,
);
if (activeLink !== currentActiveLink) {
this.setState({
activeLink: currentActiveLink,
});
}
this.setCurrentActiveLink(currentActiveLink);
};
updateInk = () => {

View File

@ -322,4 +322,18 @@ describe('Anchor Render', () => {
dateNowMock.mockRestore();
jest.useRealTimers();
});
it('Anchor onChange prop', async () => {
const onChange = jest.fn();
const wrapper = mount(
<Anchor onChange={onChange}>
<Link href="#API1" title="API1" />
<Link href="#API2" title="API2" />
</Anchor>,
);
expect(onChange).toHaveBeenCalledTimes(1);
wrapper.instance().handleScrollTo('#API2');
expect(onChange).toHaveBeenCalledTimes(2);
expect(onChange).toHaveBeenCalledWith('#API2');
});
});

View File

@ -166,6 +166,80 @@ exports[`renders ./components/anchor/demo/customizeHighlight.md correctly 1`] =
</div>
`;
exports[`renders ./components/anchor/demo/onChange.md correctly 1`] = `
<div
class="ant-anchor-wrapper"
style="max-height:100vh"
>
<div
class="ant-anchor fixed"
>
<div
class="ant-anchor-ink"
>
<span
class="ant-anchor-ink-ball"
/>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-basic"
title="Basic demo"
>
Basic demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-static"
title="Static demo"
>
Static demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#API"
title="API"
>
API
</a>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#Anchor-Props"
title="Anchor Props"
>
Anchor Props
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#Link-Props"
title="Link Props"
>
Link Props
</a>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/anchor/demo/onClick.md correctly 1`] = `
<div
class="ant-anchor-wrapper"

View File

@ -0,0 +1,36 @@
---
order: 6
title:
zh-CN: 监听锚点链接改变
en-US: Listening for anchor link change
---
## zh-CN
监听锚点链接改变
## en-US
Listening for anchor link change.
```jsx
import { Anchor } from 'antd';
const { Link } = Anchor;
const onChange = link => {
console.log('Anchor:OnChange', link);
};
ReactDOM.render(
<Anchor affix={false} onChange={onChange}>
<Link href="#components-anchor-demo-basic" title="Basic demo" />
<Link href="#components-anchor-demo-static" title="Static demo" />
<Link href="#API" title="API">
<Link href="#Anchor-Props" title="Anchor Props" />
<Link href="#Link-Props" title="Link Props" />
</Link>
</Anchor>,
mountNode,
);
```

View File

@ -1,5 +1,5 @@
---
order: 4
order: 5
title:
zh-CN: 设置锚点滚动偏移量
en-US: Set Anchor scroll offset

View File

@ -26,6 +26,7 @@ For displaying anchor hyperlinks on page and jumping between them.
| onClick | set the handler to handle `click` event | Function(e: Event, link: Object) | - | 3.9.0 |
| getCurrentAnchor | Customize the anchor highlight | () => string | - | 3.22.0 |
| targetOffset | Anchor scroll offset, default as `offsetTop`, [example](#components-anchor-demo-targetOffset) | number | `offsetTop` | 3.22.0 |
| onChange | Listening for anchor link change | (currentActiveLink: string) => void | | 3.24.0 |
### Link Props

View File

@ -27,6 +27,7 @@ title: Anchor
| onClick | `click` 事件的 handler | Function(e: Event, link: Object) | - | 3.9.0 |
| getCurrentAnchor | 自定义高亮的锚点 | () => string | - | 3.22.0 |
| targetOffset | 锚点滚动偏移量,默认与 offsetTop 相同,[例子](#components-anchor-demo-targetOffset) | number | `offsetTop` | 3.22.0 |
| onChange | 监听锚点链接改变 | (currentActiveLink: string) => void | | 3.24.0 |
### Link Props

View File

@ -11085,7 +11085,7 @@ exports[`ConfigProvider components Slider configProvider 1`] = `
/>
<div
class="config-slider-track"
style="left:0%;width:0%"
style="left:0%;right:auto;width:0%"
/>
<div
class="config-slider-step"
@ -11097,7 +11097,7 @@ exports[`ConfigProvider components Slider configProvider 1`] = `
aria-valuenow="0"
class="config-slider-handle config-tooltip-open"
role="slider"
style="left:0%"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -11134,7 +11134,7 @@ exports[`ConfigProvider components Slider normal 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:0%"
style="left:0%;right:auto;width:0%"
/>
<div
class="ant-slider-step"
@ -11146,7 +11146,7 @@ exports[`ConfigProvider components Slider normal 1`] = `
aria-valuenow="0"
class="ant-slider-handle ant-tooltip-open"
role="slider"
style="left:0%"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -11183,7 +11183,7 @@ exports[`ConfigProvider components Slider prefixCls 1`] = `
/>
<div
class="prefix-Slider-track"
style="left:0%;width:0%"
style="left:0%;right:auto;width:0%"
/>
<div
class="prefix-Slider-step"
@ -11195,7 +11195,7 @@ exports[`ConfigProvider components Slider prefixCls 1`] = `
aria-valuenow="0"
class="prefix-Slider-handle prefix-Slider-tooltip-open"
role="slider"
style="left:0%"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -17309,7 +17309,7 @@ exports[`ConfigProvider components Upload configProvider 1`] = `
class="config-upload-list config-upload-list-text"
>
<div
class="config-upload-list-item config-upload-list-item-done"
class="config-upload-list-item config-upload-list-item-done config-upload-list-item-list-type-text"
>
<div
class="config-upload-list-item-info"
@ -17335,34 +17335,67 @@ exports[`ConfigProvider components Upload configProvider 1`] = `
</svg>
</i>
<span
class="config-upload-list-item-name"
class="config-upload-list-item-name config-upload-list-item-name-icon-count-2"
title="xxx.png"
>
xxx.png
<span
class="config-upload-list-item-card-actions "
>
<a
title="Download file"
>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</a>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
</div>
</span>
@ -17379,7 +17412,7 @@ exports[`ConfigProvider components Upload normal 1`] = `
class="ant-upload-list ant-upload-list-text"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-text"
>
<div
class="ant-upload-list-item-info"
@ -17405,34 +17438,67 @@ exports[`ConfigProvider components Upload normal 1`] = `
</svg>
</i>
<span
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
title="xxx.png"
>
xxx.png
<span
class="ant-upload-list-item-card-actions "
>
<a
title="Download file"
>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</a>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
</div>
</span>
@ -17449,7 +17515,7 @@ exports[`ConfigProvider components Upload prefixCls 1`] = `
class="ant-upload-list ant-upload-list-text"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-text"
>
<div
class="ant-upload-list-item-info"
@ -17475,34 +17541,67 @@ exports[`ConfigProvider components Upload prefixCls 1`] = `
</svg>
</i>
<span
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
title="xxx.png"
>
xxx.png
<span
class="ant-upload-list-item-card-actions "
>
<a
title="Download file"
>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</a>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
</div>
</span>

View File

@ -3343,7 +3343,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:0%"
style="left:0%;right:auto;width:0%"
/>
<div
class="ant-slider-step"
@ -3380,7 +3380,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
aria-valuenow="0"
class="ant-slider-handle"
role="slider"
style="left:0%"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -3388,37 +3388,37 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:0%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
>
A
</span>
<span
class="ant-slider-mark-text"
style="left:20%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:20%"
>
B
</span>
<span
class="ant-slider-mark-text"
style="left:40%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:40%"
>
C
</span>
<span
class="ant-slider-mark-text"
style="left:60%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:60%"
>
D
</span>
<span
class="ant-slider-mark-text"
style="left:80%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:80%"
>
E
</span>
<span
class="ant-slider-mark-text"
style="left:100%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%"
>
F
</span>

View File

@ -480,7 +480,7 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:20%"
style="left:0%;right:auto;width:20%"
/>
<div
class="ant-slider-step"
@ -517,7 +517,7 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
aria-valuenow="1"
class="ant-slider-handle"
role="slider"
style="left:20%"
style="left:20%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -525,37 +525,37 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:0%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
>
8
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:20%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:20%"
>
16
</span>
<span
class="ant-slider-mark-text"
style="left:40%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:40%"
>
24
</span>
<span
class="ant-slider-mark-text"
style="left:60%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:60%"
>
32
</span>
<span
class="ant-slider-mark-text"
style="left:80%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:80%"
>
40
</span>
<span
class="ant-slider-mark-text"
style="left:100%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%"
>
48
</span>
@ -578,7 +578,7 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:40%"
style="left:0%;right:auto;width:40%"
/>
<div
class="ant-slider-step"
@ -615,7 +615,7 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
aria-valuenow="2"
class="ant-slider-handle"
role="slider"
style="left:40%"
style="left:40%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -623,37 +623,37 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:0%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
>
2
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:20%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:20%"
>
3
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:40%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:40%"
>
4
</span>
<span
class="ant-slider-mark-text"
style="left:60%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:60%"
>
6
</span>
<span
class="ant-slider-mark-text"
style="left:80%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:80%"
>
8
</span>
<span
class="ant-slider-mark-text"
style="left:100%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%"
>
12
</span>

View File

@ -14,7 +14,8 @@ export interface SearchProps extends InputProps {
| React.MouseEvent<HTMLElement>
| React.KeyboardEvent<HTMLInputElement>,
) => void;
enterButton?: boolean | React.ReactNode;
enterButton?: React.ReactNode;
loading?: boolean;
}
export default class Search extends React.Component<SearchProps, any> {
@ -39,7 +40,10 @@ export default class Search extends React.Component<SearchProps, any> {
};
onSearch = (e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLInputElement>) => {
const { onSearch } = this.props;
const { onSearch, loading } = this.props;
if (loading) return;
if (onSearch) {
onSearch(this.input.input.value, e);
}
@ -54,8 +58,26 @@ export default class Search extends React.Component<SearchProps, any> {
this.input.blur();
}
renderLoading = (prefixCls: string) => {
const { enterButton, size } = this.props;
if (enterButton) {
return (
<Button className={`${prefixCls}-button`} type="primary" size={size} key="enterButton">
<Icon type="loading" />
</Button>
);
}
return <Icon className={`${prefixCls}-icon`} type="loading" />;
};
renderSuffix = (prefixCls: string) => {
const { suffix, enterButton } = this.props;
const { suffix, enterButton, loading } = this.props;
if (loading && !enterButton) {
return [suffix, this.renderLoading(prefixCls)];
}
if (enterButton) return suffix;
const node = (
@ -81,10 +103,15 @@ export default class Search extends React.Component<SearchProps, any> {
};
renderAddonAfter = (prefixCls: string) => {
const { enterButton, size, disabled, addonAfter } = this.props;
if (!enterButton) return addonAfter;
const { enterButton, size, disabled, addonAfter, loading } = this.props;
const btnClassName = `${prefixCls}-button`;
if (loading && enterButton) {
return [this.renderLoading(prefixCls), addonAfter];
}
if (!enterButton) return addonAfter;
let button: React.ReactNode;
const enterButtonAsElement = enterButton as React.ReactElement<any>;
if (enterButtonAsElement.type === Button || enterButtonAsElement.type === 'button') {

View File

@ -152,4 +152,11 @@ describe('Input.Search', () => {
expect(onSearch).toHaveBeenLastCalledWith('', expect.anything());
expect(onChange).toHaveBeenCalled();
});
it('should support loading', () => {
const wrapper = mount(<Search loading />);
const wrapperWithEnterButton = mount(<Search loading enterButton />);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapperWithEnterButton.render()).toMatchSnapshot();
});
});

View File

@ -150,3 +150,82 @@ exports[`Input.Search should support custom button 1`] = `
</span>
</span>
`;
exports[`Input.Search should support loading 1`] = `
<span
class="ant-input-search ant-input-affix-wrapper"
>
<input
class="ant-input"
type="text"
value=""
/>
<span
class="ant-input-suffix"
>
<i
aria-label="icon: loading"
class="anticon anticon-loading ant-input-search-icon"
>
<svg
aria-hidden="true"
class="anticon-spin"
data-icon="loading"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
/>
</svg>
</i>
</span>
</span>
`;
exports[`Input.Search should support loading 2`] = `
<span
class="ant-input-search ant-input-search-enter-button ant-input-group-wrapper"
>
<span
class="ant-input-wrapper ant-input-group"
>
<input
class="ant-input"
type="text"
value=""
/>
<span
class="ant-input-group-addon"
>
<button
class="ant-btn ant-input-search-button ant-btn-primary"
type="button"
>
<i
aria-label="icon: loading"
class="anticon anticon-loading"
>
<svg
aria-hidden="true"
class="anticon-spin"
data-icon="loading"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
/>
</svg>
</i>
</button>
</span>
</span>
</span>
`;

View File

@ -1811,6 +1811,169 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
</div>
`;
exports[`renders ./components/input/demo/search-input-loading.md correctly 1`] = `
<div>
<span
class="ant-input-search ant-input-affix-wrapper"
>
<input
class="ant-input"
placeholder="input search loading deault"
type="text"
value=""
/>
<span
class="ant-input-suffix"
>
<i
aria-label="icon: loading"
class="anticon anticon-loading ant-input-search-icon"
>
<svg
aria-hidden="true"
class="anticon-spin"
data-icon="loading"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
/>
</svg>
</i>
</span>
</span>
<br />
<br />
<span
class="ant-input-search ant-input-affix-wrapper"
>
<input
class="ant-input"
placeholder="input search loading with suffix"
type="text"
value=""
/>
<span
class="ant-input-suffix"
>
suffix
<i
aria-label="icon: loading"
class="anticon anticon-loading ant-input-search-icon"
>
<svg
aria-hidden="true"
class="anticon-spin"
data-icon="loading"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
/>
</svg>
</i>
</span>
</span>
<br />
<br />
<span
class="ant-input-search ant-input-search-enter-button ant-input-group-wrapper"
>
<span
class="ant-input-wrapper ant-input-group"
>
<input
class="ant-input"
placeholder="input search loading with enterButton"
type="text"
value=""
/>
<span
class="ant-input-group-addon"
>
<button
class="ant-btn ant-input-search-button ant-btn-primary"
type="button"
>
<i
aria-label="icon: loading"
class="anticon anticon-loading"
>
<svg
aria-hidden="true"
class="anticon-spin"
data-icon="loading"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
/>
</svg>
</i>
</button>
</span>
</span>
</span>
<br />
<br />
<span
class="ant-input-search ant-input-search-enter-button ant-input-group-wrapper"
>
<span
class="ant-input-wrapper ant-input-group"
>
<input
class="ant-input"
placeholder="input search loading with enterButton and addonAfter"
type="text"
value=""
/>
<span
class="ant-input-group-addon"
>
<button
class="ant-btn ant-input-search-button ant-btn-primary"
type="button"
>
<i
aria-label="icon: loading"
class="anticon anticon-loading"
>
<svg
aria-hidden="true"
class="anticon-spin"
data-icon="loading"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
/>
</svg>
</i>
</button>
addonAfter
</span>
</span>
</span>
</div>
`;
exports[`renders ./components/input/demo/size.md correctly 1`] = `
<div
class="example-input"

View File

@ -0,0 +1,41 @@
---
order: 5
title:
zh-CN: 搜索框 loading
en-US: Search box with loading
---
## zh-CN
用于 `onSearch` 的时候展示 `loading`
## en-US
Search loading when onSearch.
```jsx
import { Input } from 'antd';
const { Search } = Input;
ReactDOM.render(
<div>
<Search placeholder="input search loading deault" loading />
<br />
<br />
<Search placeholder="input search loading with suffix" loading suffix="suffix" />
<br />
<br />
<Search placeholder="input search loading with enterButton" loading enterButton />
<br />
<br />
<Search
placeholder="input search loading with enterButton and addonAfter"
loading
enterButton
addonAfter="addonAfter"
/>
</div>,
mountNode,
);
```

View File

@ -56,6 +56,7 @@ The rest of the props of `Input.TextArea` are the same as the original [textarea
| --- | --- | --- | --- | --- |
| enterButton | to show an enter button after input. This prop is conflict with addon. | boolean\|ReactNode | false | |
| onSearch | The callback function that is triggered when you click on the search-icon or press Enter key. | function(value, event) | | |
| loading | Search box with loading. | boolean | | |
Supports all props of `Input`.

View File

@ -55,6 +55,7 @@ Input 的其他属性和 React 自带的 [input](https://facebook.github.io/reac
| --- | --- | --- | --- | --- |
| enterButton | 是否有确认按钮,可设为按钮文字。该属性会与 addon 冲突。 | boolean\|ReactNode | false | |
| onSearch | 点击搜索或按下回车键时的回调 | function(value, event) | | |
| loading | 搜索 loading | boolean | | |
其余属性和 Input 一致。

View File

@ -26,7 +26,6 @@
border: 0;
.@{search-prefix}-button {
width: 100%;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}

View File

@ -42,6 +42,7 @@ export default {
removeFile: 'Remove file',
uploadError: 'Upload error',
previewFile: 'Preview file',
downloadFile: 'Download file',
},
Empty: {
description: 'No Data',

View File

@ -42,6 +42,7 @@ export default {
removeFile: '删除文件',
uploadError: '上传错误',
previewFile: '预览文件',
downloadFile: '下载文件',
},
Empty: {
description: '暂无数据',

View File

@ -10,7 +10,7 @@ exports[`renders ./components/slider/demo/basic.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:30%"
style="left:0%;right:auto;width:30%"
/>
<div
class="ant-slider-step"
@ -22,7 +22,7 @@ exports[`renders ./components/slider/demo/basic.md correctly 1`] = `
aria-valuenow="30"
class="ant-slider-handle"
role="slider"
style="left:30%"
style="left:30%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -37,7 +37,7 @@ exports[`renders ./components/slider/demo/basic.md correctly 1`] = `
/>
<div
class="ant-slider-track ant-slider-track-1"
style="left:20%;width:30%"
style="left:20%;right:auto;width:30%"
/>
<div
class="ant-slider-step"
@ -49,7 +49,7 @@ exports[`renders ./components/slider/demo/basic.md correctly 1`] = `
aria-valuenow="20"
class="ant-slider-handle ant-slider-handle-1"
role="slider"
style="left:20%"
style="left:20%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -59,7 +59,7 @@ exports[`renders ./components/slider/demo/basic.md correctly 1`] = `
aria-valuenow="50"
class="ant-slider-handle ant-slider-handle-2"
role="slider"
style="left:50%"
style="left:50%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -90,7 +90,7 @@ exports[`renders ./components/slider/demo/event.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:30%"
style="left:0%;right:auto;width:30%"
/>
<div
class="ant-slider-step"
@ -102,7 +102,7 @@ exports[`renders ./components/slider/demo/event.md correctly 1`] = `
aria-valuenow="30"
class="ant-slider-handle"
role="slider"
style="left:30%"
style="left:30%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -117,7 +117,7 @@ exports[`renders ./components/slider/demo/event.md correctly 1`] = `
/>
<div
class="ant-slider-track ant-slider-track-1"
style="left:20%;width:30%"
style="left:20%;right:auto;width:30%"
/>
<div
class="ant-slider-step"
@ -129,7 +129,7 @@ exports[`renders ./components/slider/demo/event.md correctly 1`] = `
aria-valuenow="20"
class="ant-slider-handle ant-slider-handle-1"
role="slider"
style="left:20%"
style="left:20%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -139,7 +139,7 @@ exports[`renders ./components/slider/demo/event.md correctly 1`] = `
aria-valuenow="50"
class="ant-slider-handle ant-slider-handle-2"
role="slider"
style="left:50%"
style="left:50%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -181,7 +181,7 @@ exports[`renders ./components/slider/demo/icon-slider.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:0%"
style="left:0%;right:auto;width:0%"
/>
<div
class="ant-slider-step"
@ -193,7 +193,7 @@ exports[`renders ./components/slider/demo/icon-slider.md correctly 1`] = `
aria-valuenow="0"
class="ant-slider-handle"
role="slider"
style="left:0%"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -239,7 +239,7 @@ exports[`renders ./components/slider/demo/input-number.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:0%"
style="left:0%;right:auto;width:0%"
/>
<div
class="ant-slider-step"
@ -251,7 +251,7 @@ exports[`renders ./components/slider/demo/input-number.md correctly 1`] = `
aria-valuenow="1"
class="ant-slider-handle"
role="slider"
style="left:0%"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -357,7 +357,7 @@ exports[`renders ./components/slider/demo/input-number.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:0%"
style="left:0%;right:auto;width:0%"
/>
<div
class="ant-slider-step"
@ -369,7 +369,7 @@ exports[`renders ./components/slider/demo/input-number.md correctly 1`] = `
aria-valuenow="0"
class="ant-slider-handle"
role="slider"
style="left:0%"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -477,7 +477,7 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:37%"
style="left:0%;right:auto;width:37%"
/>
<div
class="ant-slider-step"
@ -506,7 +506,7 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
aria-valuenow="37"
class="ant-slider-handle"
role="slider"
style="left:37%"
style="left:37%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -514,25 +514,25 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:0%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
>
0°C
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:26%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:26%"
>
26°C
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:37%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:37%"
>
37°C
</span>
<span
class="ant-slider-mark-text"
style="left:100%;transform:translateX(-50%);-ms-transform:translateX(-50%);color:#f50"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%;color:#f50"
>
<strong>
100°C
@ -548,7 +548,7 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
/>
<div
class="ant-slider-track ant-slider-track-1"
style="left:26%;width:11%"
style="left:26%;right:auto;width:11%"
/>
<div
class="ant-slider-step"
@ -577,7 +577,7 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
aria-valuenow="26"
class="ant-slider-handle ant-slider-handle-1"
role="slider"
style="left:26%"
style="left:26%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -587,7 +587,7 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
aria-valuenow="37"
class="ant-slider-handle ant-slider-handle-2"
role="slider"
style="left:37%"
style="left:37%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -595,25 +595,25 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
>
<span
class="ant-slider-mark-text"
style="left:0%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
>
0°C
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:26%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:26%"
>
26°C
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:37%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:37%"
>
37°C
</span>
<span
class="ant-slider-mark-text"
style="left:100%;transform:translateX(-50%);-ms-transform:translateX(-50%);color:#f50"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%;color:#f50"
>
<strong>
100°C
@ -657,7 +657,7 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
aria-valuenow="37"
class="ant-slider-handle"
role="slider"
style="left:37%"
style="left:37%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -665,25 +665,25 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
>
<span
class="ant-slider-mark-text"
style="left:0%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
>
0°C
</span>
<span
class="ant-slider-mark-text"
style="left:26%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:26%"
>
26°C
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:37%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:37%"
>
37°C
</span>
<span
class="ant-slider-mark-text"
style="left:100%;transform:translateX(-50%);-ms-transform:translateX(-50%);color:#f50"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%;color:#f50"
>
<strong>
100°C
@ -702,7 +702,7 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:37%"
style="left:0%;right:auto;width:37%"
/>
<div
class="ant-slider-step"
@ -731,7 +731,7 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
aria-valuenow="37"
class="ant-slider-handle"
role="slider"
style="left:37%"
style="left:37%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -739,25 +739,25 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:0%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
>
0°C
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:26%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:26%"
>
26°C
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:37%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:37%"
>
37°C
</span>
<span
class="ant-slider-mark-text"
style="left:100%;transform:translateX(-50%);-ms-transform:translateX(-50%);color:#f50"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%;color:#f50"
>
<strong>
100°C
@ -776,7 +776,7 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:37%"
style="left:0%;right:auto;width:37%"
/>
<div
class="ant-slider-step"
@ -805,7 +805,7 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
aria-valuenow="37"
class="ant-slider-handle"
role="slider"
style="left:37%"
style="left:37%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -813,25 +813,25 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:0%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
>
0°C
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:26%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:26%"
>
26°C
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="left:37%;transform:translateX(-50%);-ms-transform:translateX(-50%)"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:37%"
>
37°C
</span>
<span
class="ant-slider-mark-text"
style="left:100%;transform:translateX(-50%);-ms-transform:translateX(-50%);color:#f50"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%;color:#f50"
>
<strong>
100°C
@ -842,6 +842,87 @@ exports[`renders ./components/slider/demo/mark.md correctly 1`] = `
</div>
`;
exports[`renders ./components/slider/demo/reverse.md correctly 1`] = `
<div>
<div
class="ant-slider"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="right:0%;left:auto;width:30%"
/>
<div
class="ant-slider-step"
/>
<div
aria-disabled="false"
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="30"
class="ant-slider-handle"
role="slider"
style="right:30%;left:auto;transform:translateX(+50%)"
tabindex="0"
/>
<div
class="ant-slider-mark"
/>
</div>
<div
class="ant-slider"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track ant-slider-track-1"
style="right:20%;left:auto;width:30%"
/>
<div
class="ant-slider-step"
/>
<div
aria-disabled="false"
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="20"
class="ant-slider-handle ant-slider-handle-1"
role="slider"
style="right:20%;left:auto;transform:translateX(+50%)"
tabindex="0"
/>
<div
aria-disabled="false"
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="50"
class="ant-slider-handle ant-slider-handle-2"
role="slider"
style="right:50%;left:auto;transform:translateX(+50%)"
tabindex="0"
/>
<div
class="ant-slider-mark"
/>
</div>
Reversed:
<button
aria-checked="true"
checked=""
class="ant-switch-small ant-switch ant-switch-checked"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</button>
</div>
`;
exports[`renders ./components/slider/demo/show-tooltip.md correctly 1`] = `
<div
class="ant-slider"
@ -851,7 +932,7 @@ exports[`renders ./components/slider/demo/show-tooltip.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:30%"
style="left:0%;right:auto;width:30%"
/>
<div
class="ant-slider-step"
@ -863,7 +944,7 @@ exports[`renders ./components/slider/demo/show-tooltip.md correctly 1`] = `
aria-valuenow="30"
class="ant-slider-handle ant-tooltip-open"
role="slider"
style="left:30%"
style="left:30%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div>
@ -901,7 +982,7 @@ exports[`renders ./components/slider/demo/tip-formatter.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:0%"
style="left:0%;right:auto;width:0%"
/>
<div
class="ant-slider-step"
@ -913,7 +994,7 @@ exports[`renders ./components/slider/demo/tip-formatter.md correctly 1`] = `
aria-valuenow="0"
class="ant-slider-handle"
role="slider"
style="left:0%"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -928,7 +1009,7 @@ exports[`renders ./components/slider/demo/tip-formatter.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="left:0%;width:0%"
style="left:0%;right:auto;width:0%"
/>
<div
class="ant-slider-step"
@ -940,7 +1021,7 @@ exports[`renders ./components/slider/demo/tip-formatter.md correctly 1`] = `
aria-valuenow="0"
class="ant-slider-handle"
role="slider"
style="left:0%"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
@ -965,7 +1046,7 @@ exports[`renders ./components/slider/demo/vertical.md correctly 1`] = `
/>
<div
class="ant-slider-track"
style="bottom:0%;height:30%"
style="bottom:0%;top:auto;height:30%"
/>
<div
class="ant-slider-step"
@ -977,7 +1058,7 @@ exports[`renders ./components/slider/demo/vertical.md correctly 1`] = `
aria-valuenow="30"
class="ant-slider-handle"
role="slider"
style="bottom:30%"
style="bottom:30%;top:auto;transform:translateY(+50%)"
tabindex="0"
/>
<div
@ -996,7 +1077,7 @@ exports[`renders ./components/slider/demo/vertical.md correctly 1`] = `
/>
<div
class="ant-slider-track ant-slider-track-1"
style="bottom:20%;height:30%"
style="bottom:20%;top:auto;height:30%"
/>
<div
class="ant-slider-step"
@ -1008,7 +1089,7 @@ exports[`renders ./components/slider/demo/vertical.md correctly 1`] = `
aria-valuenow="20"
class="ant-slider-handle ant-slider-handle-1"
role="slider"
style="bottom:20%"
style="bottom:20%;top:auto;transform:translateY(+50%)"
tabindex="0"
/>
<div
@ -1018,7 +1099,7 @@ exports[`renders ./components/slider/demo/vertical.md correctly 1`] = `
aria-valuenow="50"
class="ant-slider-handle ant-slider-handle-2"
role="slider"
style="bottom:50%"
style="bottom:50%;top:auto;transform:translateY(+50%)"
tabindex="0"
/>
<div
@ -1037,7 +1118,7 @@ exports[`renders ./components/slider/demo/vertical.md correctly 1`] = `
/>
<div
class="ant-slider-track ant-slider-track-1"
style="bottom:26%;height:11%"
style="bottom:26%;top:auto;height:11%"
/>
<div
class="ant-slider-step"
@ -1066,7 +1147,7 @@ exports[`renders ./components/slider/demo/vertical.md correctly 1`] = `
aria-valuenow="26"
class="ant-slider-handle ant-slider-handle-1"
role="slider"
style="bottom:26%"
style="bottom:26%;top:auto;transform:translateY(+50%)"
tabindex="0"
/>
<div
@ -1076,7 +1157,7 @@ exports[`renders ./components/slider/demo/vertical.md correctly 1`] = `
aria-valuenow="37"
class="ant-slider-handle ant-slider-handle-2"
role="slider"
style="bottom:37%"
style="bottom:37%;top:auto;transform:translateY(+50%)"
tabindex="0"
/>
<div

View File

@ -0,0 +1,41 @@
---
order: 8
title:
zh-CN: 反向
en-US: Reverse
---
## zh-CN
设置 `reverse` 可以将滑动条置反。
## en-US
Using `reverse` to render slider reversely.
```jsx
import { Slider, Switch } from 'antd';
class Demo extends React.Component {
state = {
reverse: true,
};
handleReverseChange = reverse => {
this.setState({ reverse });
};
render() {
const { reverse } = this.state;
return (
<div>
<Slider defaultValue={30} reverse={reverse} />
<Slider range defaultValue={[20, 50]} reverse={reverse} />
Reversed: <Switch size="small" checked={reverse} onChange={this.handleReverseChange} />
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -23,6 +23,7 @@ To input a value in a range.
| max | The maximum value the slider can slide to | number | 100 | |
| min | The minimum value the slider can slide to. | number | 0 | |
| range | dual thumb mode | boolean | false | |
| reverse | reverse the component | boolean | false | 3.24.0 |
| step | The granularity the slider can step through values. Must greater than 0, and be divided by (max - min) . When `marks` no null, `step` can be `null`. | number\|null | 1 | |
| tipFormatter | Slider will pass its value to `tipFormatter`, and display its value in Tooltip, and hide Tooltip when return value is null. | Function\|null | IDENTITY | |
| value | The value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|number\[] | |

View File

@ -24,6 +24,7 @@ title: Slider
| max | 最大值 | number | 100 | |
| min | 最小值 | number | 0 | |
| range | 双滑块模式 | boolean | false | |
| reverse | 反向坐标轴 | boolean | false | 3.24.0 |
| step | 步长,取值必须大于 0并且可被 (max - min) 整除。当 `marks` 不为空对象时,可以设置 `step``null`,此时 Slider 的可选值仅有 marks 标出来的部分。 | number\|null | 1 | |
| tipFormatter | Slider 会把当前值传给 `tipFormatter`,并在 Tooltip 中显示 `tipFormatter` 的返回值,若为 null则隐藏 Tooltip。 | Function\|null | IDENTITY | |
| value | 设置当前取值。当 `range``false` 时,使用 `number`,否则用 `[number, number]` | number\|number\[] | | |

View File

@ -1228,8 +1228,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
mergedLocale.emptyText = renderEmpty('Table');
}
const classString = classNames({
[`${prefixCls}-${this.props.size}`]: true,
const classString = classNames(`${prefixCls}-${this.props.size}`, {
[`${prefixCls}-bordered`]: this.props.bordered,
[`${prefixCls}-empty`]: !data.length,
[`${prefixCls}-without-column-header`]: !showHeader,

View File

@ -11,7 +11,7 @@ exports[`Table.rowSelection fix selection column on the left 1`] = `
class="ant-spin-container"
>
<div
class="ant-table ant-table-default ant-table-scroll-position-left"
class="ant-table ant-table-default ant-table-scroll-position-left ant-table-layout-fixed"
>
<div
class="ant-table-content"

File diff suppressed because it is too large Load Diff

View File

@ -453,7 +453,7 @@ exports[`Table renders empty table with fixed columns 1`] = `
class="ant-spin-container"
>
<div
class="ant-table ant-table-default ant-table-empty ant-table-scroll-position-left"
class="ant-table ant-table-default ant-table-empty ant-table-scroll-position-left ant-table-layout-fixed"
>
<div
class="ant-table-content"
@ -491,7 +491,7 @@ exports[`Table renders empty table with fixed columns 1`] = `
>
<tr>
<th
class="ant-table-fixed-columns-in-body"
class="ant-table-fixed-columns-in-body ant-table-row-cell-break-word"
>
<span
class="ant-table-header-column"
@ -509,7 +509,7 @@ exports[`Table renders empty table with fixed columns 1`] = `
</span>
</th>
<th
class="ant-table-fixed-columns-in-body"
class="ant-table-fixed-columns-in-body ant-table-row-cell-break-word"
>
<span
class="ant-table-header-column"
@ -671,7 +671,7 @@ exports[`Table renders empty table with fixed columns 1`] = `
</span>
</th>
<th
class="ant-table-fixed-columns-in-body"
class="ant-table-fixed-columns-in-body ant-table-row-cell-break-word"
>
<span
class="ant-table-header-column"
@ -771,7 +771,7 @@ exports[`Table renders empty table with fixed columns 1`] = `
>
<tr>
<th
class=""
class="ant-table-row-cell-break-word"
>
<span
class="ant-table-header-column"
@ -789,7 +789,7 @@ exports[`Table renders empty table with fixed columns 1`] = `
</span>
</th>
<th
class=""
class="ant-table-row-cell-break-word"
>
<span
class="ant-table-header-column"
@ -838,7 +838,7 @@ exports[`Table renders empty table with fixed columns 1`] = `
>
<tr>
<th
class=""
class="ant-table-row-cell-break-word"
>
<span
class="ant-table-header-column"

View File

@ -1,5 +1,5 @@
---
order: 27
order: 100
title:
en-US: Dynamic Settings
zh-CN: 动态控制表格属性
@ -16,21 +16,17 @@ Select different settings to see the result.
```jsx
import { Table, Icon, Switch, Radio, Form, Divider } from 'antd';
const FormItem = Form.Item;
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: 150,
render: text => <a>{text}</a>,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 70,
},
{
title: 'Address',
@ -40,7 +36,6 @@ const columns = [
{
title: 'Action',
key: 'action',
width: 360,
render: (text, record) => (
<span>
<a>Action 一 {record.name}</a>
@ -86,6 +81,7 @@ class Demo extends React.Component {
rowSelection: {},
scroll: undefined,
hasData: true,
tableLayout: undefined,
};
handleToggle = prop => enable => {
@ -96,10 +92,18 @@ class Demo extends React.Component {
this.setState({ size: e.target.value });
};
handleTableLayoutChange = e => {
this.setState({ tableLayout: e.target.value });
};
handleExpandChange = enable => {
this.setState({ expandedRowRender: enable ? expandedRowRender : undefined });
};
handleEllipsisChange = enable => {
this.setState({ ellipsis: enable });
};
handleTitleChange = enable => {
this.setState({ title: enable ? title : undefined });
};
@ -135,56 +139,71 @@ class Demo extends React.Component {
const { state } = this;
return (
<div>
<div className="components-table-demo-control-bar">
<Form layout="inline">
<FormItem label="Bordered">
<Switch checked={state.bordered} onChange={this.handleToggle('bordered')} />
</FormItem>
<FormItem label="loading">
<Switch checked={state.loading} onChange={this.handleToggle('loading')} />
</FormItem>
<FormItem label="Title">
<Switch checked={!!state.title} onChange={this.handleTitleChange} />
</FormItem>
<FormItem label="Column Header">
<Switch checked={!!state.showHeader} onChange={this.handleHeaderChange} />
</FormItem>
<FormItem label="Footer">
<Switch checked={!!state.footer} onChange={this.handleFooterChange} />
</FormItem>
<FormItem label="Expandable">
<Switch checked={!!state.expandedRowRender} onChange={this.handleExpandChange} />
</FormItem>
<FormItem label="Checkbox">
<Switch checked={!!state.rowSelection} onChange={this.handleRowSelectionChange} />
</FormItem>
<FormItem label="Fixed Header">
<Switch checked={!!state.scroll} onChange={this.handleScollChange} />
</FormItem>
<FormItem label="Has Data">
<Switch checked={!!state.hasData} onChange={this.handleDataChange} />
</FormItem>
<FormItem label="Size">
<Radio.Group size="default" value={state.size} onChange={this.handleSizeChange}>
<Radio.Button value="default">Default</Radio.Button>
<Radio.Button value="middle">Middle</Radio.Button>
<Radio.Button value="small">Small</Radio.Button>
</Radio.Group>
</FormItem>
<FormItem label="Pagination">
<Radio.Group
value={state.pagination ? state.pagination.position : 'none'}
onChange={this.handlePaginationChange}
>
<Radio.Button value="top">Top</Radio.Button>
<Radio.Button value="bottom">Bottom</Radio.Button>
<Radio.Button value="both">Both</Radio.Button>
<Radio.Button value="none">None</Radio.Button>
</Radio.Group>
</FormItem>
</Form>
</div>
<Table {...this.state} columns={columns} dataSource={state.hasData ? data : null} />
<Form
layout="inline"
className="components-table-demo-control-bar"
style={{ marginBottom: 16 }}
>
<Form.Item label="Bordered">
<Switch checked={state.bordered} onChange={this.handleToggle('bordered')} />
</Form.Item>
<Form.Item label="loading">
<Switch checked={state.loading} onChange={this.handleToggle('loading')} />
</Form.Item>
<Form.Item label="Title">
<Switch checked={!!state.title} onChange={this.handleTitleChange} />
</Form.Item>
<Form.Item label="Column Header">
<Switch checked={!!state.showHeader} onChange={this.handleHeaderChange} />
</Form.Item>
<Form.Item label="Footer">
<Switch checked={!!state.footer} onChange={this.handleFooterChange} />
</Form.Item>
<Form.Item label="Expandable">
<Switch checked={!!state.expandedRowRender} onChange={this.handleExpandChange} />
</Form.Item>
<Form.Item label="Checkbox">
<Switch checked={!!state.rowSelection} onChange={this.handleRowSelectionChange} />
</Form.Item>
<Form.Item label="Fixed Header">
<Switch checked={!!state.scroll} onChange={this.handleScollChange} />
</Form.Item>
<Form.Item label="Has Data">
<Switch checked={!!state.hasData} onChange={this.handleDataChange} />
</Form.Item>
<Form.Item label="Ellipsis">
<Switch checked={!!state.ellipsis} onChange={this.handleEllipsisChange} />
</Form.Item>
<Form.Item label="Size">
<Radio.Group value={state.size} onChange={this.handleSizeChange}>
<Radio.Button value="default">Default</Radio.Button>
<Radio.Button value="middle">Middle</Radio.Button>
<Radio.Button value="small">Small</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item label="Table Layout">
<Radio.Group value={state.tableLayout} onChange={this.handleTableLayoutChange}>
<Radio.Button value={undefined}>Unset</Radio.Button>
<Radio.Button value="fixed">Fixed</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item label="Pagination">
<Radio.Group
value={state.pagination ? state.pagination.position : 'none'}
onChange={this.handlePaginationChange}
>
<Radio.Button value="top">Top</Radio.Button>
<Radio.Button value="bottom">Bottom</Radio.Button>
<Radio.Button value="both">Both</Radio.Button>
<Radio.Button value="none">None</Radio.Button>
</Radio.Group>
</Form.Item>
</Form>
<Table
{...this.state}
columns={columns.map(item => ({ ...item, ellipsis: state. ellipsis }))}
dataSource={state.hasData ? data : null}
/>
</div>
);
}
@ -194,9 +213,6 @@ ReactDOM.render(<Demo />, mountNode);
```
<style>
.components-table-demo-control-bar {
margin-bottom: 10px;
}
.components-table-demo-control-bar .ant-form-item {
margin-right: 16px;
margin-bottom: 8px;

View File

@ -0,0 +1,88 @@
---
order: 27
title:
en-US: ellipsis column
zh-CN: 单元格自动省略
---
## zh-CN
设置 `column.ellipsis` 可以让单元格内容根据宽度自动省略。
> 列头缩略暂不支持和排序筛选一起使用。
## en-US
Ellipsize cell content via setting `column.ellipsis`.
> Cannot ellipsize table header with sorters and filters for now.
```jsx
import { Table } 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: true,
},
{
title: 'Long Column Long Column Long Column',
dataIndex: 'address',
key: 'address 2',
ellipsis: true,
},
{
title: 'Long Column Long Column',
dataIndex: 'address',
key: 'address 3',
ellipsis: true,
},
{
title: 'Long Column',
dataIndex: 'address',
key: 'address 4',
ellipsis: true,
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park, New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park, London No. 2 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park, Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
ReactDOM.render(<Table columns={columns} dataSource={data} />, mountNode);
```

View File

@ -99,6 +99,7 @@ class App extends React.Component {
onFilter: (value, record) => record.name.includes(value),
sorter: (a, b) => a.name.length - b.name.length,
sortOrder: sortedInfo.columnKey === 'name' && sortedInfo.order,
ellipsis: true,
},
{
title: 'Age',
@ -106,6 +107,7 @@ class App extends React.Component {
key: 'age',
sorter: (a, b) => a.age - b.age,
sortOrder: sortedInfo.columnKey === 'age' && sortedInfo.order,
ellipsis: true,
},
{
title: 'Address',
@ -116,6 +118,7 @@ class App extends React.Component {
onFilter: (value, record) => record.address.includes(value),
sorter: (a, b) => a.address.length - b.address.length,
sortOrder: sortedInfo.columnKey === 'address' && sortedInfo.order,
ellipsis: true,
},
];
return (

View File

@ -7,11 +7,11 @@ title:
## zh-CN
集成 react-resizable 来实现可伸缩列。
集成 [react-resizable](https://github.com/STRML/react-resizable) 来实现可伸缩列。
## en-US
Implement resizable column by integrate with react-resizable.
Implement resizable column by integrate with [react-resizable](https://github.com/STRML/react-resizable).
```jsx
import { Table } from 'antd';

View File

@ -59,6 +59,7 @@ const columns = [
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| tableLayout | [table-layout](https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout) attribute of table element | - \| 'auto' \| 'fixed' | -<hr />`fixed` when header/columns are fixed, or using `column.ellipsis` | 3.24.0 |
| bordered | Whether to show all table borders | boolean | `false` | |
| childrenColumnName | The column contains children to display | string\[] | children | 3.4.2 |
| columns | Columns of table | [ColumnProps](https://git.io/vMMXC)\[] | - | |
@ -119,6 +120,7 @@ One of the Table `columns` prop for describing the table's columns, Column has t
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| align | specify which way that column is aligned | 'left' \| 'right' \| 'center' | 'left' | 3.3.2 |
| ellipsis | ellipsize cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is true. | boolean | false | 3.24.0 |
| className | className of this column | string | - | |
| colSpan | Span of this column's title | number | | |
| dataIndex | Display field of the data record, could be set like `a.b.c`, `a[0].b.c[1]` | string | - | |

View File

@ -64,6 +64,7 @@ const columns = [
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| tableLayout | 表格元素的 [table-layout](https://developer.mozilla.org/zh-CN/docs/Web/CSS/table-layout) 属性,设为 `fixed` 表示内容不会影响列的布局 | - \| 'auto' \| 'fixed' | 无<hr />固定表头/列或使用了 `column.ellipsis` 时,默认值为 `fixed` | 3.24.0 |
| bordered | 是否展示外边框和列边框 | boolean | false | |
| childrenColumnName | 指定树形结构的列名 | string\[] | children | 3.4.2 |
| columns | 表格列的配置描述,具体项见下表 | [ColumnProps](https://git.io/vMMXC)\[] | - | |
@ -124,6 +125,7 @@ const columns = [
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| align | 设置列的对齐方式 | 'left' \| 'right' \| 'center' | 'left' | 3.3.2 |
| ellipsis | 超过宽度将自动省略,暂不支持和排序筛选一起使用。<br />设置为 `true` 时,表格布局将变成 `tableLayout="fixed"`。 | boolean | false | 3.24.0 |
| className | 列样式类名 | string | - | |
| colSpan | 表头列合并,设置为 0 时,不渲染 | number | | |
| dataIndex | 列数据在数据项中对应的 key支持 `a.b.c`、`a[0].b.c[1]` 的嵌套写法 | string | - | |

View File

@ -33,6 +33,7 @@ export interface ColumnProps<T> {
dataIndex?: string; // Note: We can not use generic type here, since we need to support nested key, see #9393
render?: (text: any, record: T, index: number) => React.ReactNode;
align?: 'left' | 'right' | 'center';
ellipsis?: boolean;
filters?: ColumnFilterItem[];
onFilter?: (value: any, record: T) => boolean;
filterMultiple?: boolean;
@ -190,6 +191,7 @@ export interface TableProps<T> {
bodyStyle?: React.CSSProperties;
className?: string;
style?: React.CSSProperties;
tableLayout?: React.CSSProperties['tableLayout'];
children?: React.ReactNode;
sortDirections?: SortOrder[];
getPopupContainer?: (triggerNode: HTMLElement) => HTMLElement;

View File

@ -37,6 +37,10 @@
border-spacing: 0;
}
&-layout-fixed table {
table-layout: fixed;
}
&-thead > tr > th {
color: @table-header-color;
font-weight: 500;
@ -169,6 +173,7 @@
.@{table-prefix-cls}-header-column {
display: inline-block;
max-width: 100%;
vertical-align: top;
.@{table-prefix-cls}-column-sorters {
@ -328,6 +333,7 @@
&-thead > tr > th,
&-tbody > tr > td {
padding: @table-padding-vertical @table-padding-horizontal;
overflow-wrap: break-word;
}
&-expand-icon-th,
@ -579,6 +585,22 @@
content: '.';
}
}
&-cell-ellipsis,
&-cell-ellipsis .@{table-prefix-cls}-column-title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
&-cell-ellipsis .@{table-prefix-cls}-column-title {
display: block;
}
&-cell-break-word {
word-wrap: break-word;
word-break: break-word;
}
}
tr&-expanded-row {
@ -600,7 +622,6 @@
overflow: auto;
overflow-x: hidden;
table {
width: auto;
min-width: 100%;
// https://github.com/ant-design/ant-design/issues/14545

View File

@ -239,7 +239,7 @@ exports[`renders ./components/typography/demo/ellipsis-debug.md correctly 1`] =
/>
<div
class="ant-slider-track"
style="left:0%;width:0%"
style="left:0%;right:auto;width:0%"
/>
<div
class="ant-slider-step"
@ -251,7 +251,7 @@ exports[`renders ./components/typography/demo/ellipsis-debug.md correctly 1`] =
aria-valuenow="1"
class="ant-slider-handle"
role="slider"
style="left:0%"
style="left:0%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div

View File

@ -253,11 +253,12 @@ class Upload extends React.Component<UploadProps, UploadState> {
showUploadList,
listType,
onPreview,
onDownload,
previewFile,
disabled,
locale: propLocale,
} = this.props;
const { showRemoveIcon, showPreviewIcon } = showUploadList as any;
const { showRemoveIcon, showPreviewIcon, showDownloadIcon } = showUploadList as any;
const { fileList } = this.state;
return (
<UploadList
@ -265,9 +266,11 @@ class Upload extends React.Component<UploadProps, UploadState> {
items={fileList}
previewFile={previewFile}
onPreview={onPreview}
onDownload={onDownload}
onRemove={this.handleManualRemove}
showRemoveIcon={!disabled && showRemoveIcon}
showPreviewIcon={showPreviewIcon}
showDownloadIcon={showDownloadIcon}
locale={{ ...locale, ...propLocale }}
/>
);

View File

@ -16,6 +16,7 @@ export default class UploadList extends React.Component<UploadListProps, any> {
showInfo: false,
},
showRemoveIcon: true,
showDownloadIcon: true,
showPreviewIcon: true,
previewFile: previewImage,
};
@ -56,6 +57,15 @@ export default class UploadList extends React.Component<UploadListProps, any> {
return onPreview(file);
};
handleDownload = (file: UploadFile) => {
const { onDownload } = this.props;
if (typeof onDownload === 'function') {
onDownload(file);
} else if (file.url) {
window.open(file.url);
}
};
handleClose = (file: UploadFile) => {
const { onRemove } = this.props;
if (onRemove) {
@ -70,6 +80,7 @@ export default class UploadList extends React.Component<UploadListProps, any> {
listType,
showPreviewIcon,
showRemoveIcon,
showDownloadIcon,
locale,
progressAttr,
} = this.props;
@ -125,28 +136,63 @@ export default class UploadList extends React.Component<UploadListProps, any> {
const infoUploadingClass = classNames({
[`${prefixCls}-list-item`]: true,
[`${prefixCls}-list-item-${file.status}`]: true,
[`${prefixCls}-list-item-list-type-${listType}`]: true,
});
const linkProps =
typeof file.linkProps === 'string' ? JSON.parse(file.linkProps) : file.linkProps;
const preview = file.url ? (
<a
target="_blank"
rel="noopener noreferrer"
className={`${prefixCls}-list-item-name`}
title={file.name}
{...linkProps}
href={file.url}
onClick={e => this.handlePreview(file, e)}
const removeIcon = showRemoveIcon ? (
<Icon type="delete" title={locale.removeFile} onClick={() => this.handleClose(file)} />
) : null;
const downloadIcon =
showDownloadIcon && file.status === 'done' ? (
<Icon
type="download"
title={locale.downloadFile}
onClick={() => this.handleDownload(file)}
/>
) : null;
const downloadOrDelete = listType !== 'picture-card' && (
<span
key="download-delete"
className={`${prefixCls}-list-item-card-actions ${
listType === 'picture' ? 'picture' : ''
}`}
>
{file.name}
</a>
{downloadIcon && <a title={locale.downloadFile}>{downloadIcon}</a>}
{removeIcon && <a title={locale.removeFile}>{removeIcon}</a>}
</span>
);
const listItemNameClass = classNames({
[`${prefixCls}-list-item-name`]: true,
[`${prefixCls}-list-item-name-icon-count-${
[downloadIcon, removeIcon].filter(x => x).length
}`]: true,
});
const preview = file.url ? (
[
<a
key="view"
target="_blank"
rel="noopener noreferrer"
className={listItemNameClass}
title={file.name}
{...linkProps}
href={file.url}
onClick={e => this.handlePreview(file, e)}
>
{file.name}
</a>,
downloadOrDelete,
]
) : (
<span
className={`${prefixCls}-list-item-name`}
className={listItemNameClass}
onClick={e => this.handlePreview(file, e)}
title={file.name}
>
{file.name}
{downloadOrDelete}
</span>
);
const style: React.CSSProperties = {
@ -165,21 +211,14 @@ export default class UploadList extends React.Component<UploadListProps, any> {
<Icon type="eye-o" />
</a>
) : null;
const removeIcon = showRemoveIcon ? (
<Icon type="delete" title={locale.removeFile} onClick={() => this.handleClose(file)} />
) : null;
const removeIconClose = showRemoveIcon ? (
<Icon type="close" title={locale.removeFile} onClick={() => this.handleClose(file)} />
) : null;
const actions =
listType === 'picture-card' && file.status !== 'uploading' ? (
<span className={`${prefixCls}-list-item-actions`}>
{previewIcon}
{removeIcon}
</span>
) : (
removeIconClose
);
const actions = listType === 'picture-card' && file.status !== 'uploading' && (
<span className={`${prefixCls}-list-item-actions`}>
{previewIcon}
{file.status === 'done' && downloadIcon}
{removeIcon}
</span>
);
let message;
if (file.response && typeof file.response === 'string') {
message = file.response;

View File

@ -34,7 +34,7 @@ exports[`renders ./components/upload/demo/defaultFileList.md correctly 1`] = `
class="ant-upload-list ant-upload-list-text"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-text"
>
<div
class="ant-upload-list-item-info"
@ -60,7 +60,7 @@ exports[`renders ./components/upload/demo/defaultFileList.md correctly 1`] = `
</svg>
</i>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="http://www.baidu.com/xxx.png"
rel="noopener noreferrer"
target="_blank"
@ -68,32 +68,65 @@ exports[`renders ./components/upload/demo/defaultFileList.md correctly 1`] = `
>
xxx.png
</a>
<span
class="ant-upload-list-item-card-actions "
>
<a
title="Download file"
>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</a>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-text"
>
<div
class="ant-upload-list-item-info"
@ -119,7 +152,7 @@ exports[`renders ./components/upload/demo/defaultFileList.md correctly 1`] = `
</svg>
</i>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="http://www.baidu.com/yyy.png"
rel="noopener noreferrer"
target="_blank"
@ -127,32 +160,65 @@ exports[`renders ./components/upload/demo/defaultFileList.md correctly 1`] = `
>
yyy.png
</a>
<span
class="ant-upload-list-item-card-actions "
>
<a
title="Download file"
>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</a>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
<div
class="ant-upload-list-item ant-upload-list-item-error"
class="ant-upload-list-item ant-upload-list-item-error ant-upload-list-item-list-type-text"
>
<div
class="ant-upload-list-item-info"
@ -178,7 +244,7 @@ exports[`renders ./components/upload/demo/defaultFileList.md correctly 1`] = `
</svg>
</i>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1"
href="http://www.baidu.com/zzz.png"
rel="noopener noreferrer"
target="_blank"
@ -186,29 +252,37 @@ exports[`renders ./components/upload/demo/defaultFileList.md correctly 1`] = `
>
zzz.png
</a>
<span
class="ant-upload-list-item-card-actions "
>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
</div>
</span>
@ -251,7 +325,7 @@ exports[`renders ./components/upload/demo/fileList.md correctly 1`] = `
class="ant-upload-list ant-upload-list-text"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-text"
>
<div
class="ant-upload-list-item-info"
@ -277,7 +351,7 @@ exports[`renders ./components/upload/demo/fileList.md correctly 1`] = `
</svg>
</i>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="http://www.baidu.com/xxx.png"
rel="noopener noreferrer"
target="_blank"
@ -285,29 +359,62 @@ exports[`renders ./components/upload/demo/fileList.md correctly 1`] = `
>
xxx.png
</a>
<span
class="ant-upload-list-item-card-actions "
>
<a
title="Download file"
>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</a>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
</div>
</span>
@ -324,7 +431,7 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
class="ant-upload-list ant-upload-list-picture-card"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"
>
<div
class="ant-upload-list-item-info"
@ -343,7 +450,7 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
/>
</a>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
rel="noopener noreferrer"
target="_blank"
@ -382,6 +489,27 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
</svg>
</i>
</a>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
@ -406,7 +534,7 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
</span>
</div>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"
>
<div
class="ant-upload-list-item-info"
@ -425,7 +553,7 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
/>
</a>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
rel="noopener noreferrer"
target="_blank"
@ -464,6 +592,27 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
</svg>
</i>
</a>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
@ -488,7 +637,7 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
</span>
</div>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"
>
<div
class="ant-upload-list-item-info"
@ -507,7 +656,7 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
/>
</a>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
rel="noopener noreferrer"
target="_blank"
@ -546,6 +695,27 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
</svg>
</i>
</a>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
@ -570,7 +740,7 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
</span>
</div>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"
>
<div
class="ant-upload-list-item-info"
@ -589,7 +759,7 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
/>
</a>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
rel="noopener noreferrer"
target="_blank"
@ -628,6 +798,27 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
</svg>
</i>
</a>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
@ -652,7 +843,7 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
</span>
</div>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"
>
<div
class="ant-upload-list-item-info"
@ -671,7 +862,7 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
/>
</a>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
rel="noopener noreferrer"
target="_blank"
@ -710,6 +901,27 @@ exports[`renders ./components/upload/demo/picture-card.md correctly 1`] = `
</svg>
</i>
</a>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
@ -753,7 +965,7 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
class="ant-upload-list ant-upload-list-picture"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"
>
<div
class="ant-upload-list-item-info"
@ -772,7 +984,7 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
/>
</a>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
rel="noopener noreferrer"
target="_blank"
@ -780,32 +992,65 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
>
xxx.png
</a>
<span
class="ant-upload-list-item-card-actions picture"
>
<a
title="Download file"
>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</a>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"
>
<div
class="ant-upload-list-item-info"
@ -824,7 +1069,7 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
/>
</a>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
rel="noopener noreferrer"
target="_blank"
@ -832,29 +1077,62 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
>
yyy.png
</a>
<span
class="ant-upload-list-item-card-actions picture"
>
<a
title="Download file"
>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</a>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
</div>
</span>
@ -870,7 +1148,7 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
class="ant-upload-list ant-upload-list-picture"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"
>
<div
class="ant-upload-list-item-info"
@ -889,7 +1167,7 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
/>
</a>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
rel="noopener noreferrer"
target="_blank"
@ -897,32 +1175,65 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
>
xxx.png
</a>
<span
class="ant-upload-list-item-card-actions picture"
>
<a
title="Download file"
>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</a>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
<div
class="ant-upload-list-item ant-upload-list-item-done"
class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"
>
<div
class="ant-upload-list-item-info"
@ -941,7 +1252,7 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
/>
</a>
<a
class="ant-upload-list-item-name"
class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-2"
href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
rel="noopener noreferrer"
target="_blank"
@ -949,29 +1260,62 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
>
yyy.png
</a>
<span
class="ant-upload-list-item-card-actions picture"
>
<a
title="Download file"
>
<i
aria-label="icon: download"
class="anticon anticon-download"
tabindex="-1"
title="Download file"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</a>
<a
title="Remove file"
>
<i
aria-label="icon: delete"
class="anticon anticon-delete"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/>
</svg>
</i>
</a>
</span>
</span>
</div>
<i
aria-label="icon: close"
class="anticon anticon-close"
tabindex="-1"
title="Remove file"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
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 0 0 203 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"
/>
</svg>
</i>
</div>
</div>
</span>

View File

@ -417,7 +417,7 @@ describe('Upload', () => {
const wrapper = mount(<Upload {...props} />);
wrapper.find('div.ant-upload-list-item i.anticon-close').simulate('click');
wrapper.find('div.ant-upload-list-item i.anticon-delete').simulate('click');
setImmediate(() => {
wrapper.update();
@ -429,6 +429,33 @@ describe('Upload', () => {
});
});
it('should not stop download when return use onDownload', done => {
const mockRemove = jest.fn(() => false);
const props = {
onRemove: mockRemove,
fileList: [
{
uid: '-1',
name: 'foo.png',
status: 'done',
url: 'http://www.baidu.com/xxx.png',
},
],
};
const wrapper = mount(<Upload {...props} onDownload={() => {}} />);
wrapper.find('div.ant-upload-list-item i.anticon-download').simulate('click');
setImmediate(() => {
wrapper.update();
expect(props.fileList).toHaveLength(1);
expect(props.fileList[0].status).toBe('done');
done();
});
});
// https://github.com/ant-design/ant-design/issues/14439
it('should allow call abort function through upload instance', () => {
const wrapper = mount(

View File

@ -121,7 +121,7 @@ describe('Upload List', () => {
wrapper
.find('.ant-upload-list-item')
.at(0)
.find('.anticon-close')
.find('.anticon-delete')
.simulate('click');
await sleep(400);
wrapper.update();
@ -202,6 +202,36 @@ describe('Upload List', () => {
expect(handleChange.mock.calls[0][0].fileList).toHaveLength(3);
});
it('In the case of listType=picture, the error status does not show the download.', () => {
const file = { status: 'error', uid: 'file' };
const wrapper = mount(
<Upload listType="picture" fileList={[file]}>
<button type="button">upload</button>
</Upload>,
);
expect(wrapper.find('div.ant-upload-list-item i.anticon-download').length).toBe(0);
});
it('In the case of listType=picture-card, the error status does not show the download.', () => {
const file = { status: 'error', uid: 'file' };
const wrapper = mount(
<Upload listType="picture-card" fileList={[file]}>
<button type="button">upload</button>
</Upload>,
);
expect(wrapper.find('div.ant-upload-list-item i.anticon-download').length).toBe(0);
});
it('In the case of listType=text, the error status does not show the download.', () => {
const file = { status: 'error', uid: 'file' };
const wrapper = mount(
<Upload listType="text" fileList={[file]}>
<button type="button">upload</button>
</Upload>,
);
expect(wrapper.find('div.ant-upload-list-item i.anticon-download').length).toBe(0);
});
it('should support onPreview', () => {
const handlePreview = jest.fn();
const wrapper = mount(
@ -248,6 +278,52 @@ describe('Upload List', () => {
expect(handleChange.mock.calls.length).toBe(2);
});
it('should support onDownload', async () => {
const handleDownload = jest.fn();
const wrapper = mount(
<Upload
listType="picture-card"
defaultFileList={[
{
uid: '0',
name: 'xxx.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
]}
onDownload={handleDownload}
>
<button type="button">upload</button>
</Upload>,
);
wrapper
.find('.anticon-download')
.at(0)
.simulate('click');
});
it('should support no onDownload', async () => {
const wrapper = mount(
<Upload
listType="picture-card"
defaultFileList={[
{
uid: '0',
name: 'xxx.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
]}
>
<button type="button">upload</button>
</Upload>,
);
wrapper
.find('.anticon-download')
.at(0)
.simulate('click');
});
describe('should generate thumbUrl from file', () => {
[
{ width: 100, height: 200, name: 'height large than width' },
@ -431,6 +507,13 @@ describe('Upload List', () => {
expect(wrapper.handlePreview()).toBe(undefined);
});
it('return when prop onDownload not exists', () => {
const file = new File([''], 'test.txt', { type: 'text/plain' });
const items = [{ uid: 'upload-list-item', url: '' }];
const wrapper = mount(<UploadList items={items} locale={{ downloadFile: '' }} />).instance();
expect(wrapper.handleDownload(file)).toBe(undefined);
});
it('previewFile should work correctly', async () => {
const file = new File([''], 'test.txt', { type: 'text/plain' });
const items = [{ uid: 'upload-list-item', url: '' }];
@ -440,6 +523,20 @@ describe('Upload List', () => {
return wrapper.props.previewFile(file);
});
it('downloadFile should work correctly', async () => {
const file = new File([''], 'test.txt', { type: 'text/plain' });
const items = [{ uid: 'upload-list-item', url: '' }];
const wrapper = mount(
<UploadList
listType="picture-card"
items={items}
onDownload={() => {}}
locale={{ downloadFile: '' }}
/>,
).instance();
return wrapper.props.onDownload(file);
});
it('extname should work correctly when url not exists', () => {
const items = [{ uid: 'upload-list-item', url: '' }];
const wrapper = mount(
@ -448,6 +545,21 @@ describe('Upload List', () => {
expect(wrapper.find('.ant-upload-list-item-thumbnail').length).toBe(2);
});
it('extname should work correctly when url exists', () => {
const items = [{ status: 'done', uid: 'upload-list-item', url: '/example' }];
const wrapper = mount(
<UploadList
listType="picture"
onDownload={file => {
expect(file.url).toBe('/example');
}}
items={items}
locale={{ downloadFile: '' }}
/>,
);
wrapper.find('div.ant-upload-list-item i.anticon-download').simulate('click');
});
it('when picture-card is loading, icon should render correctly', () => {
const items = [{ status: 'uploading', uid: 'upload-list-item' }];
const wrapper = mount(

View File

@ -32,13 +32,14 @@ Uploading is the process of publishing information (web pages, text, pictures, v
| multiple | Whether to support selected multiple file. `IE10+` supported. You can select multiple files with CTRL holding down while multiple is set to be true | boolean | false | |
| name | The name of uploading file | string | 'file' | |
| previewFile | Customize preview file logic | (file: File \| Blob) => Promise<dataURL: string> | - | 3.17.0 |
| showUploadList | Whether to show default upload list, could be an object to specify `showPreviewIcon` and `showRemoveIcon` individually | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean } | true | |
| showUploadList | Whether to show default upload list, could be an object to specify `showPreviewIcon`, `showRemoveIcon` and `showDownloadIcon` individually | Boolean or { showPreviewIcon?: boolean, showDownloadIcon?: boolean, showRemoveIcon?: boolean } | true | |
| supportServerRender | Need to be turned on while the server side is rendering | boolean | false | |
| withCredentials | ajax upload with cookie sent | boolean | false | |
| openFileDialogOnClick | click open file dialog | boolean | true | 3.10.0 |
| onChange | A callback function, can be executed when uploading state is changing, see [onChange](#onChange) | Function | - | |
| onPreview | A callback function, will be executed when file link or preview icon is clicked | Function(file) | - | |
| onRemove | A callback function, will be executed when removing file button is clicked, remove event will be prevented when return value is `false` or a Promise which resolve(false) or reject | Function(file): `boolean | Promise` | - | |
| onDownload | Click the method to download the file, pass the method to perform the method logic, do not pass the default jump to the new TAB. | Function(file): void | Jump to new TAB | |
| transformFile   | Customize transform file before request | Function(file): `string | Blob | File | Promise<string | Blob | File>` | - | 3.21.0 |
### onChange

View File

@ -33,13 +33,14 @@ title: Upload
| multiple | 是否支持多选文件,`ie10+` 支持。开启后按住 ctrl 可选择多个文件 | boolean | false | |
| name | 发到后台的文件参数名 | string | 'file' | |
| previewFile | 自定义文件预览逻辑 | (file: File \| Blob) => Promise<dataURL: string> | 无 | 3.17.0 |
| showUploadList | 是否展示文件列表, 可设为一个对象,用于单独设定 `showPreviewIcon``showRemoveIcon` | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean } | true | |
| showUploadList | 是否展示文件列表, 可设为一个对象,用于单独设定 `showPreviewIcon`, `showRemoveIcon``showDownloadIcon` | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean, showDownloadIcon?: boolean } | true | |
| supportServerRender | 服务端渲染时需要打开这个 | boolean | false | |
| withCredentials | 上传请求时是否携带 cookie | boolean | false | |
| openFileDialogOnClick | 点击打开文件对话框 | boolean | true | 3.10.0 |
| onChange | 上传文件改变时的状态,详见 [onChange](#onChange) | Function | 无 | |
| onPreview | 点击文件链接或预览图标时的回调 | Function(file) | 无 | |
| onRemove   | 点击移除文件时的回调,返回值为 false 时不移除。支持返回一个 Promise 对象Promise 对象 resolve(false) 或 reject 时不移除。               | Function(file): `boolean | Promise` | 无   | |
| onDownload | 点击下载文件时的回调,如果没有指定,则默认跳转到文件 url 对应的标签页。 | Function(file): void | 跳转新标签页 | |
| transformFile   | 在上传之前转换文件。支持返回一个 Promise 对象   | Function(file): `string | Blob | File | Promise<string | Blob | File>` | 无   | 3.21.0 |
### onChange

View File

@ -40,11 +40,13 @@ export interface UploadChangeParam<T extends object = UploadFile> {
export interface ShowUploadListInterface {
showRemoveIcon?: boolean;
showPreviewIcon?: boolean;
showDownloadIcon?: boolean;
}
export interface UploadLocale {
uploading?: string;
removeFile?: string;
downloadFile?: string;
uploadError?: string;
previewFile?: string;
}
@ -74,6 +76,7 @@ export interface UploadProps {
listType?: UploadListType;
className?: string;
onPreview?: (file: UploadFile) => void;
onDownload?: (file: UploadFile) => void;
onRemove?: (file: UploadFile) => void | boolean | Promise<void | boolean>;
supportServerRender?: boolean;
style?: React.CSSProperties;
@ -96,11 +99,13 @@ export interface UploadState {
export interface UploadListProps {
listType?: UploadListType;
onPreview?: (file: UploadFile) => void;
onDownload?: (file: UploadFile) => void;
onRemove?: (file: UploadFile) => void | boolean;
items?: Array<UploadFile>;
progressAttr?: Object;
prefixCls?: string;
showRemoveIcon?: boolean;
showDownloadIcon?: boolean;
showPreviewIcon?: boolean;
locale: UploadLocale;
previewFile?: PreviewFileHandler;

View File

@ -140,6 +140,16 @@
.@{upload-prefix-cls}-list {
.reset-component;
.clearfix;
&-item-list-type-text {
&:hover {
.@{upload-prefix-cls}-list-item-name-icon-count-1 {
padding-right: 14px;
}
.@{upload-prefix-cls}-list-item-name-icon-count-2 {
padding-right: 28px;
}
}
}
&-item {
position: relative;
height: 22px;
@ -154,6 +164,25 @@
text-overflow: ellipsis;
}
&-name-icon-count-1 {
padding-right: 14px;
}
&-card-actions {
position: absolute;
right: 0;
opacity: 0;
&.picture {
top: 25px;
line-height: 1;
opacity: 1;
}
.anticon {
padding-right: 5px;
color: rgba(0, 0, 0, 0.45);
}
}
&-info {
height: 100%;
padding: 0 12px 0 4px;
@ -196,14 +225,21 @@
opacity: 1;
}
&:hover &-card-actions {
opacity: 1;
}
&-error,
&-error .@{iconfont-css-prefix}-paper-clip,
&-error &-name {
color: @error-color;
}
&-error .@{iconfont-css-prefix}-close {
color: @error-color !important;
&-error &-card-actions {
.anticon {
padding-right: 5px;
color: @error-color;
}
opacity: 1;
}
@ -290,6 +326,14 @@
transition: all 0.3s;
}
.@{upload-item}-name-icon-count-1 {
padding-right: 18px;
}
.@{upload-item}-name-icon-count-2 {
padding-right: 36px;
}
.@{upload-item}-uploading .@{upload-item}-name {
line-height: 28px;
}
@ -353,6 +397,7 @@
transition: all 0.3s;
.@{iconfont-css-prefix}-eye-o,
.@{iconfont-css-prefix}-download,
.@{iconfont-css-prefix}-delete {
z-index: 10;
width: 16px;

View File

@ -121,10 +121,10 @@
"rc-progress": "~2.5.0",
"rc-rate": "~2.5.0",
"rc-select": "~9.2.0",
"rc-slider": "~8.6.11",
"rc-slider": "~8.7.1",
"rc-steps": "~3.5.0",
"rc-switch": "~1.9.0",
"rc-table": "~6.7.0",
"rc-table": "~6.8.6",
"rc-tabs": "~9.6.4",
"rc-time-picker": "~3.7.1",
"rc-tooltip": "~3.7.3",

View File

@ -252,11 +252,15 @@
word-break: break-all;
}
&:nth-child(4) {
width: 13%;
width: 16%;
font-size: @font-size-base - 1px;
}
}
}
hr {
margin: 12px 0;
}
}
.grid-demo,