Merge pull request #18327 from ant-design/feature

Merge feature into master
This commit is contained in:
偏右 2019-08-17 22:40:44 +08:00 committed by GitHub
commit cfa73249db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 8083 additions and 1177 deletions

View File

@ -0,0 +1,13 @@
import { easeInOutCubic } from '../easings';
describe('Test easings', () => {
it('easeInOutCubic return value', () => {
const nums = [];
// eslint-disable-next-line no-plusplus
for (let index = 0; index < 5; index++) {
nums.push(easeInOutCubic(index, 1, 5, 4));
}
expect(nums).toEqual([1, 1.25, 3, 4.75, 5]);
});
});

View File

@ -0,0 +1,56 @@
import scrollTo from '../scrollTo';
describe('Test ScrollTo function', () => {
let dateNowMock;
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
beforeEach(() => {
dateNowMock = jest
.spyOn(Date, 'now')
.mockImplementationOnce(() => 0)
.mockImplementationOnce(() => 1000);
});
afterEach(() => {
dateNowMock.mockRestore();
});
it('test scrollTo', async () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
window.scrollY = y;
window.pageYOffset = y;
});
scrollTo(1000);
jest.runAllTimers();
expect(window.pageYOffset).toBe(1000);
scrollToSpy.mockRestore();
});
it('test callback - option', async () => {
const cbMock = jest.fn();
scrollTo(1000, {
callback: cbMock,
});
jest.runAllTimers();
expect(cbMock).toHaveBeenCalledTimes(1);
});
it('test getContainer - option', async () => {
const div = document.createElement('div');
scrollTo(1000, {
getContainer: () => div,
});
jest.runAllTimers();
expect(div.scrollTop).toBe(1000);
});
});

View File

@ -0,0 +1,9 @@
// eslint-disable-next-line import/prefer-default-export
export function easeInOutCubic(t: number, b: number, c: number, d: number) {
const cc = c - b;
t /= d / 2;
if (t < 1) {
return (cc / 2) * t * t * t + b;
}
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
}

View File

@ -0,0 +1,37 @@
import raf from 'raf';
import getScroll from './getScroll';
import { easeInOutCubic } from './easings';
interface ScrollToOptions {
/** Scroll container, default as window */
getContainer?: () => HTMLElement | Window;
/** Scroll end callback */
callback?: () => any;
/** Animation duration, default as 450 */
duration?: number;
}
export default function scrollTo(y: number, options: ScrollToOptions = {}) {
const { getContainer = () => window, callback, duration = 450 } = options;
const container = getContainer();
const scrollTop = getScroll(container, true);
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now();
const time = timestamp - startTime;
const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration);
if (container === window) {
window.scrollTo(window.pageXOffset, nextScrollTop);
} else {
(container as HTMLElement).scrollTop = nextScrollTop;
}
if (time < duration) {
raf(frameFunc);
} else if (typeof callback === 'function') {
callback();
}
};
raf(frameFunc);
}

View File

@ -3,10 +3,10 @@ import * as ReactDOM from 'react-dom';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import raf from 'raf';
import Affix from '../affix';
import AnchorLink from './AnchorLink';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import scrollTo from '../_util/scrollTo';
import getScroll from '../_util/getScroll';
function getDefaultContainer() {
@ -35,52 +35,7 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number
return rect.top;
}
function easeInOutCubic(t: number, b: number, c: number, d: number) {
const cc = c - b;
t /= d / 2;
if (t < 1) {
return (cc / 2) * t * t * t + b;
}
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
}
const sharpMatcherRegx = /#([^#]+)$/;
function scrollTo(
href: string,
offsetTop = 0,
getContainer: () => AnchorContainer,
callback = () => {},
) {
const container = getContainer();
const scrollTop = getScroll(container, true);
const sharpLinkMatch = sharpMatcherRegx.exec(href);
if (!sharpLinkMatch) {
return;
}
const targetElement = document.getElementById(sharpLinkMatch[1]);
if (!targetElement) {
return;
}
const eleOffsetTop = getOffsetTop(targetElement, container);
const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now();
const time = timestamp - startTime;
const nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450);
if (container === window) {
window.scrollTo(window.pageXOffset, nextScrollTop);
} else {
(container as HTMLElement).scrollTop = nextScrollTop;
}
if (time < 450) {
raf(frameFunc);
} else {
callback();
}
};
raf(frameFunc);
}
type Section = {
link: string;
@ -99,10 +54,14 @@ export interface AnchorProps {
affix?: boolean;
showInkInFixed?: boolean;
getContainer?: () => AnchorContainer;
/** Return customize highlight anchor */
getCurrentAnchor?: () => string;
onClick?: (
e: React.MouseEvent<HTMLElement>,
link: { title: React.ReactNode; href: string },
) => void;
/** Scroll to target offset value, if none, it's offsetTop prop value or 0. */
targetOffset?: number;
}
export interface AnchorState {
@ -205,6 +164,12 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
}
getCurrentAnchor(offsetTop = 0, bounds = 5): string {
const { getCurrentAnchor } = this.props;
if (typeof getCurrentAnchor === 'function') {
return getCurrentAnchor();
}
const activeLink = '';
if (typeof document === 'undefined') {
return activeLink;
@ -237,6 +202,34 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
return '';
}
handleScrollTo = (link: string) => {
const { offsetTop, getContainer, targetOffset } = this.props as AnchorDefaultProps;
this.setState({ activeLink: link });
const container = getContainer();
const scrollTop = getScroll(container, true);
const sharpLinkMatch = sharpMatcherRegx.exec(link);
if (!sharpLinkMatch) {
return;
}
const targetElement = document.getElementById(sharpLinkMatch[1]);
if (!targetElement) {
return;
}
const eleOffsetTop = getOffsetTop(targetElement, container);
let y = scrollTop + eleOffsetTop;
y -= targetOffset !== undefined ? targetOffset : offsetTop || 0;
this.animating = true;
scrollTo(y, {
callback: () => {
this.animating = false;
},
getContainer,
});
};
saveInkNode = (node: HTMLSpanElement) => {
this.inkNode = node;
};
@ -246,8 +239,11 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
return;
}
const { activeLink } = this.state;
const { offsetTop, bounds } = this.props;
const currentActiveLink = this.getCurrentAnchor(offsetTop, bounds);
const { offsetTop, bounds, targetOffset } = this.props;
const currentActiveLink = this.getCurrentAnchor(
targetOffset !== undefined ? targetOffset : offsetTop || 0,
bounds,
);
if (activeLink !== currentActiveLink) {
this.setState({
activeLink: currentActiveLink,
@ -255,15 +251,6 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
}
};
handleScrollTo = (link: string) => {
const { offsetTop, getContainer } = this.props as AnchorDefaultProps;
this.animating = true;
this.setState({ activeLink: link });
scrollTo(link, offsetTop, getContainer, () => {
this.animating = false;
});
};
updateInk = () => {
if (typeof document === 'undefined') {
return;

View File

@ -1,12 +1,29 @@
import React from 'react';
import { mount } from 'enzyme';
import Anchor from '..';
import { spyElementPrototypes } from '../../__tests__/util/domHook';
import { sleep } from '../../../tests/utils';
const { Link } = Anchor;
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
describe('Anchor Render', () => {
const getBoundingClientRectMock = jest.fn(() => ({
width: 100,
height: 100,
top: 1000,
}));
const getClientRectsMock = jest.fn(() => ({
length: 1,
}));
const headingSpy = spyElementPrototypes(HTMLHeadingElement, {
getBoundingClientRect: getBoundingClientRectMock,
getClientRects: getClientRectsMock,
});
afterAll(() => {
headingSpy.mockRestore();
});
it('Anchor render perfectly', () => {
const wrapper = mount(
<Anchor>
@ -64,7 +81,7 @@ describe('Anchor Render', () => {
wrapper.instance().handleScrollTo('##API');
expect(wrapper.instance().state.activeLink).toBe('##API');
expect(scrollToSpy).not.toHaveBeenCalled();
await delay(1000);
await sleep(1000);
expect(scrollToSpy).toHaveBeenCalled();
});
@ -154,7 +171,7 @@ describe('Anchor Render', () => {
</Anchor>,
);
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
await delay(1000);
await sleep(1000);
wrapper.setProps({ getContainer: getContainerB });
expect(removeListenerSpy).not.toHaveBeenCalled();
});
@ -187,7 +204,7 @@ describe('Anchor Render', () => {
);
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
expect(removeListenerSpy).not.toHaveBeenCalled();
await delay(1000);
await sleep(1000);
wrapper.setProps({ getContainer: getContainerB });
expect(removeListenerSpy).toHaveBeenCalled();
});
@ -239,9 +256,67 @@ describe('Anchor Render', () => {
);
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
expect(removeListenerSpy).not.toHaveBeenCalled();
await delay(1000);
await sleep(1000);
holdContainer.container = document.getElementById('API2');
wrapper.setProps({ 'data-only-trigger-re-render': true });
expect(removeListenerSpy).toHaveBeenCalled();
});
it('Anchor getCurrentAnchor prop', () => {
const getCurrentAnchor = () => '#API2';
const wrapper = mount(
<Anchor getCurrentAnchor={getCurrentAnchor}>
<Link href="#API1" title="API1" />
<Link href="#API2" title="API2" />
</Anchor>,
);
expect(wrapper.instance().state.activeLink).toBe('#API2');
});
it('Anchor targetOffset prop', async () => {
jest.useFakeTimers();
let dateNowMock;
function dataNowMockFn() {
return jest
.spyOn(Date, 'now')
.mockImplementationOnce(() => 0)
.mockImplementationOnce(() => 1000);
}
dateNowMock = dataNowMockFn();
const scrollToSpy = jest.spyOn(window, 'scrollTo');
let root = document.getElementById('root');
if (!root) {
root = document.createElement('div', { id: 'root' });
root.id = 'root';
document.body.appendChild(root);
}
mount(<h1 id="API">Hello</h1>, { attachTo: root });
const wrapper = mount(
<Anchor>
<Link href="#API" title="API" />
</Anchor>,
);
wrapper.instance().handleScrollTo('#API');
jest.runAllTimers();
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 });
wrapper.instance().handleScrollTo('#API');
jest.runAllTimers();
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 });
wrapper.instance().handleScrollTo('#API');
jest.runAllTimers();
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
dateNowMock.mockRestore();
jest.useRealTimers();
});
});

View File

@ -80,6 +80,80 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
</div>
`;
exports[`renders ./components/anchor/demo/customizeHighlight.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"
@ -227,3 +301,83 @@ exports[`renders ./components/anchor/demo/static.md correctly 1`] = `
</div>
</div>
`;
exports[`renders ./components/anchor/demo/targetOffset.md correctly 1`] = `
<div>
<div
class=""
>
<div
class="ant-anchor-wrapper"
style="max-height:100vh"
>
<div
class="ant-anchor"
>
<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>
</div>
</div>
`;

View File

@ -0,0 +1,36 @@
---
order: 4
title:
zh-CN: 自定义锚点高亮
en-US: Customize the anchor highlight
---
## zh-CN
自定义锚点高亮。
## en-US
Customize the anchor highlight.
```jsx
import { Anchor } from 'antd';
const { Link } = Anchor;
const getCurrentAnchor = () => {
return '#components-anchor-demo-static';
};
ReactDOM.render(
<Anchor affix={false} getCurrentAnchor={getCurrentAnchor}>
<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

@ -0,0 +1,47 @@
---
order: 4
title:
zh-CN: 设置锚点滚动偏移量
en-US: Set Anchor scroll offset
---
## zh-CN
锚点目标滚动到屏幕正中间。
## en-US
Anchor target scroll to screen center.
```jsx
import { Anchor } from 'antd';
const { Link } = Anchor;
class AnchorExample extends React.Component {
state = {
targetOffset: undefined,
};
componentDidMount() {
this.setState({
targetOffset: window.innerHeight / 2,
});
}
render() {
return (
<Anchor targetOffset={this.state.targetOffset}>
<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>
);
}
}
ReactDOM.render(<AnchorExample />, mountNode);
```

View File

@ -24,6 +24,8 @@ For displaying anchor hyperlinks on page and jumping between them.
| offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 | |
| showInkInFixed | Whether show ink-balls in Fixed mode | boolean | false | |
| onClick | set the handler to handle `click` event | Function(e: Event, link: Object) | - | 3.9.0 |
| getCurrentAnchor | Customize the anchor highlight | () => string | - | 3.21.0 |
| targetOffset | Anchor scroll offset, default as `offsetTop`, [example](#components-anchor-demo-targetOffset) | number | `offsetTop` | 3.22.0 |
### Link Props

View File

@ -25,6 +25,8 @@ title: Anchor
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | | |
| showInkInFixed | 固定模式是否显示小圆点 | boolean | false | |
| onClick | `click` 事件的 handler | Function(e: Event, link: Object) | - | 3.9.0 |
| getCurrentAnchor | 自定义高亮的锚点 | () => string | - | 3.21.0 |
| targetOffset | 锚点滚动偏移量,默认与 offsetTop 相同,[例子](#components-anchor-demo-targetOffset) | number | `offsetTop` | 3.22.0 |
### Link Props

View File

@ -1,16 +1,22 @@
import React from 'react';
import { mount } from 'enzyme';
import { sleep } from '../../../tests/utils';
import BackTop from '..';
describe('BackTop', () => {
it('should scroll to top after click it', async () => {
const wrapper = mount(<BackTop visibilityHeight={-1} />);
document.documentElement.scrollTop = 400;
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
window.scrollY = y;
window.pageYOffset = y;
});
window.scrollTo(0, 400);
// trigger scroll manually
wrapper.instance().handleScroll();
await new Promise(resolve => setTimeout(resolve, 0));
await sleep();
wrapper.find('.ant-back-top').simulate('click');
await new Promise(resolve => setTimeout(resolve, 1000));
expect(Math.abs(Math.round(document.documentElement.scrollTop))).toBe(0);
await sleep(500);
expect(window.pageYOffset).toBe(0);
scrollToSpy.mockRestore();
});
});

View File

@ -3,20 +3,9 @@ import Animate from 'rc-animate';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import classNames from 'classnames';
import omit from 'omit.js';
import raf from 'raf';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getScroll from '../_util/getScroll';
const easeInOutCubic = (t: number, b: number, c: number, d: number) => {
const cc = c - b;
t /= d / 2;
if (t < 1) {
return (cc / 2) * t * t * t + b;
}
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
};
function noop() {}
import scrollTo from '../_util/scrollTo';
function getDefaultTarget() {
return window;
@ -79,21 +68,14 @@ export default class BackTop extends React.Component<BackTopProps, any> {
};
scrollToTop = (e: React.MouseEvent<HTMLDivElement>) => {
const scrollTop = this.getCurrentScrollTop();
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now();
const time = timestamp - startTime;
this.setScrollTop(easeInOutCubic(time, scrollTop, 0, 450));
if (time < 450) {
raf(frameFunc);
} else {
this.setScrollTop(0);
const { target = getDefaultTarget } = this.props;
scrollTo(0, {
getContainer: target,
});
if (typeof this.props.onClick === 'function') {
this.props.onClick(e);
}
};
raf(frameFunc);
(this.props.onClick || noop)(e);
};
handleScroll = () => {
const { visibilityHeight, target = getDefaultTarget } = this.props;

View File

@ -0,0 +1,3 @@
import ro_RO from '../../date-picker/locale/ro_RO';
export default ro_RO;

View File

@ -11357,7 +11357,10 @@ exports[`ConfigProvider components Steps configProvider 1`] = `
class="config-steps config-steps-horizontal config-steps-label-horizontal"
>
<div
class="config-steps-item config-steps-item-process"
class="config-steps-item config-steps-item-process config-steps-item-active"
>
<div
class="config-steps-item-container"
>
<div
class="config-steps-item-tail"
@ -11387,6 +11390,7 @@ exports[`ConfigProvider components Steps configProvider 1`] = `
</div>
</div>
</div>
</div>
`;
exports[`ConfigProvider components Steps normal 1`] = `
@ -11394,7 +11398,10 @@ exports[`ConfigProvider components Steps normal 1`] = `
class="ant-steps ant-steps-horizontal ant-steps-label-horizontal"
>
<div
class="ant-steps-item ant-steps-item-process"
class="ant-steps-item ant-steps-item-process ant-steps-item-active"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -11424,6 +11431,7 @@ exports[`ConfigProvider components Steps normal 1`] = `
</div>
</div>
</div>
</div>
`;
exports[`ConfigProvider components Steps prefixCls 1`] = `
@ -11431,7 +11439,10 @@ exports[`ConfigProvider components Steps prefixCls 1`] = `
class="prefix-Steps prefix-Steps-horizontal prefix-Steps-label-horizontal"
>
<div
class="prefix-Steps-item prefix-Steps-item-process"
class="prefix-Steps-item prefix-Steps-item-process prefix-Steps-item-active"
>
<div
class="prefix-Steps-item-container"
>
<div
class="prefix-Steps-item-tail"
@ -11461,6 +11472,7 @@ exports[`ConfigProvider components Steps prefixCls 1`] = `
</div>
</div>
</div>
</div>
`;
exports[`ConfigProvider components Switch configProvider 1`] = `

View File

@ -0,0 +1,19 @@
import CalendarLocale from 'rc-calendar/lib/locale/ro_RO';
import TimePickerLocale from '../../time-picker/locale/ro_RO';
// Merge into a locale object
const locale = {
lang: {
placeholder: 'Selectează data',
rangePlaceholder: ['Data start', 'Data sfârșit'],
...CalendarLocale,
},
timePickerLocale: {
...TimePickerLocale,
},
};
// All settings at:
// https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json
export default locale;

View File

@ -579,6 +579,123 @@ exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
</table>
</div>
</div>
<br />
<br />
<div
class="ant-descriptions"
>
<div
class="ant-descriptions-title"
>
Custom Size
</div>
<div
class="ant-descriptions-view"
>
<table>
<tbody>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
>
Product
</span>
<span
class="ant-descriptions-item-content"
>
Cloud Database
</span>
</td>
<td
class="ant-descriptions-item"
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
>
Billing
</span>
<span
class="ant-descriptions-item-content"
>
Prepaid
</span>
</td>
<td
class="ant-descriptions-item"
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
>
time
</span>
<span
class="ant-descriptions-item-content"
>
18:00:00
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
>
Amount
</span>
<span
class="ant-descriptions-item-content"
>
$80.00
</span>
</td>
<td
class="ant-descriptions-item"
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
>
Discount
</span>
<span
class="ant-descriptions-item-content"
>
$20.00
</span>
</td>
<td
class="ant-descriptions-item"
colspan="1"
>
<span
class="ant-descriptions-item-label ant-descriptions-item-colon"
>
Official
</span>
<span
class="ant-descriptions-item-content"
>
$60.00
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
`;

View File

@ -59,6 +59,16 @@ class Demo extends React.Component {
Region: East China 1<br />
</Descriptions.Item>
</Descriptions>
<br />
<br />
<Descriptions title="Custom Size" border size={this.state.size}>
<Descriptions.Item label="Product">Cloud Database</Descriptions.Item>
<Descriptions.Item label="Billing">Prepaid</Descriptions.Item>
<Descriptions.Item label="time">18:00:00</Descriptions.Item>
<Descriptions.Item label="Amount">$80.00</Descriptions.Item>
<Descriptions.Item label="Discount">$20.00</Descriptions.Item>
<Descriptions.Item label="Official">$60.00</Descriptions.Item>
</Descriptions>
</div>
);
}

View File

@ -30,3 +30,5 @@ Commonly displayed on the details page.
| -------- | ------------------------------ | --------- | ------- | ------- |
| label | description of the content | ReactNode | - | 3.19.0 |
| span | The number of columns included | number | 1 | 3.19.0 |
> The number of span Description.Item. Span={2} takes up the width of two DescriptionItems.

View File

@ -31,3 +31,5 @@ cols: 1
| ----- | ------------ | --------- | ------ | ------ |
| label | 内容的描述 | ReactNode | - | 3.19.0 |
| span | 包含列的数量 | number | 1 | 3.19.0 |
> span Description.Item 的数量。 span={2} 会占用两个 DescriptionItem 的宽度。

View File

@ -78,6 +78,24 @@
}
}
&-middle {
.@{descriptions-prefix-cls}-row {
> th,
> td {
padding-bottom: 12px;
}
}
}
&-small {
.@{descriptions-prefix-cls}-row {
> th,
> td {
padding-bottom: 8px;
}
}
}
&-bordered {
.@{descriptions-prefix-cls}-view {
border: 1px solid @border-color-split;

View File

@ -50,6 +50,7 @@ import nlNL from '../nl_NL';
import plPL from '../pl_PL';
import ptBR from '../pt_BR';
import ptPT from '../pt_PT';
import roRO from '../ro_RO';
import ruRU from '../ru_RU';
import skSK from '../sk_SK';
import slSI from '../sl_SI';
@ -99,6 +100,7 @@ const locales = [
plPL,
ptBR,
ptPT,
roRO,
ruRU,
skSK,
slSI,

View File

@ -0,0 +1,3 @@
import locale from '../locale/ro_RO';
export default locale;

View File

@ -0,0 +1,61 @@
import Pagination from 'rc-pagination/lib/locale/ro_RO';
import DatePicker from '../date-picker/locale/ro_RO';
import TimePicker from '../time-picker/locale/ro_RO';
import Calendar from '../calendar/locale/ro_RO';
export default {
locale: 'ro',
Pagination,
DatePicker,
TimePicker,
Calendar,
global: {
placeholder: 'Selectează',
},
Table: {
filterTitle: 'Filtrează',
filterConfirm: 'OK',
filterReset: 'Resetează',
selectAll: 'Selectează pagina curentă',
selectInvert: 'Inversează pagina curentă',
sortTitle: 'Ordonează',
expand: 'Extinde rândul',
collapse: 'Micșorează rândul',
},
Modal: {
okText: 'OK',
cancelText: 'Anulare',
justOkText: 'OK',
},
Popconfirm: {
okText: 'OK',
cancelText: 'Anulare',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Căutare',
itemUnit: 'element',
itemsUnit: 'elemente',
},
Upload: {
uploading: 'Se transferă...',
removeFile: 'Înlătură fișierul',
uploadError: 'Eroare la upload',
previewFile: 'Previzualizare fișier',
},
Empty: {
description: 'Fără date',
},
Icon: {
icon: 'icon',
},
Text: {
edit: 'editează',
copy: 'copiază',
copied: 'copiat',
expand: 'extinde',
},
PageHeader: {
back: 'înapoi',
},
};

View File

@ -38,6 +38,7 @@ When need to mention someone or something.
| onSearch | Trigger when prefix hit | (text: string, prefix: string) => void | - | 3.19.0 |
| onFocus | Trigger when mentions get focus | () => void | - | 3.19.0 |
| onBlur | Trigger when mentions lose focus | () => void | - | 3.19.0 |
| getPopupContainer | Set the mount HTML node for suggestions | () => HTMLElement | - | 3.22.0 |
### Mention methods

View File

@ -48,6 +48,7 @@ title: Mentions
| onSearch | 搜索时触发 | (text: string, prefix: string) => void | - | 3.19.0 |
| onFocus | 获得焦点时触发 | () => void | - | 3.19.0 |
| onBlur | 失去焦点时触发 | () => void | - | 3.19.0 |
| getPopupContainer | 指定建议框挂载的 HTML 节点 | () => HTMLElement | - | 3.22.0 |
### Mention 方法

View File

@ -76,6 +76,7 @@ export interface ModalProps {
keyboard?: boolean;
wrapProps?: any;
prefixCls?: string;
closeIcon?: React.ReactNode;
}
type getContainerFunc = () => HTMLElement;
@ -164,6 +165,7 @@ export default class Modal extends React.Component<ModalProps, {}> {
footer: PropTypes.node,
title: PropTypes.node,
closable: PropTypes.bool,
closeIcon: PropTypes.node,
};
handleCancel = (e: React.MouseEvent<HTMLButtonElement>) => {
@ -210,6 +212,7 @@ export default class Modal extends React.Component<ModalProps, {}> {
wrapClassName,
centered,
getContainer,
closeIcon,
...restProps
} = this.props;
@ -220,9 +223,9 @@ export default class Modal extends React.Component<ModalProps, {}> {
</LocaleReceiver>
);
const closeIcon = (
const closeIconToRender = (
<span className={`${prefixCls}-close-x`}>
<Icon className={`${prefixCls}-close-icon`} type="close" />
{closeIcon || <Icon className={`${prefixCls}-close-icon`} type="close" />}
</span>
);
@ -236,7 +239,7 @@ export default class Modal extends React.Component<ModalProps, {}> {
visible={visible}
mousePosition={mousePosition}
onClose={this.handleCancel}
closeIcon={closeIcon}
closeIcon={closeIconToRender}
/>
);
};

View File

@ -57,4 +57,9 @@ describe('Modal', () => {
wrapper.handleOk();
expect(onOk).toHaveBeenCalled();
});
it('support closeIcon', () => {
const wrapper = mount(<Modal closeIcon={<a>closeIcon</a>} visible />);
expect(wrapper.render()).toMatchSnapshot();
});
});

View File

@ -169,3 +169,75 @@ exports[`Modal render without footer 1`] = `
</div>
</div>
`;
exports[`Modal support closeIcon 1`] = `
<div>
<div
class="ant-modal-mask fade-appear"
/>
<div
class="ant-modal-wrap "
role="dialog"
tabindex="-1"
>
<div
class="ant-modal zoom-appear"
role="document"
style="width: 520px;"
>
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden;"
tabindex="0"
/>
<div
class="ant-modal-content"
>
<button
aria-label="Close"
class="ant-modal-close"
type="button"
>
<span
class="ant-modal-close-x"
>
<a>
closeIcon
</a>
</span>
</button>
<div
class="ant-modal-body"
/>
<div
class="ant-modal-footer"
>
<div>
<button
class="ant-btn"
type="button"
>
<span>
Cancel
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
OK
</span>
</button>
</div>
</div>
</div>
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden;"
tabindex="0"
/>
</div>
</div>
</div>
`;

View File

@ -19,6 +19,7 @@ When requiring users to interact with the application, but without jumping to a
| cancelText | Text of the Cancel button | string\|ReactNode | `Cancel` | |
| centered | Centered Modal | Boolean | `false` | 3.8.0 |
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | true | |
| closeIcon | custom close icon | ReactNode | - | 3.22.0 |
| confirmLoading | Whether to apply loading visual effect for OK button or not | boolean | false | |
| destroyOnClose | Whether to unmount child components on onClose | boolean | false | 3.1.0 |
| footer | Footer content, set as `footer={null}` when you don't need default buttons | string\|ReactNode | OK and Cancel buttons | |

View File

@ -22,6 +22,7 @@ title: Modal
| cancelText | 取消按钮文字 | string\|ReactNode | 取消 | |
| centered | 垂直居中展示 Modal | Boolean | `false` | 3.8.0 |
| closable | 是否显示右上角的关闭按钮 | boolean | true | |
| closeIcon | 自定义关闭图标 | ReactNode | - | 3.22.0 |
| confirmLoading | 确定按钮 loading | boolean | 无 | |
| destroyOnClose | 关闭时销毁 Modal 里的子元素 | boolean | false | 3.1.0 |
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `footer={null}` | string\|ReactNode | 确定取消按钮 | |

View File

@ -4,8 +4,7 @@ import moment from 'moment';
import { mount } from 'enzyme';
import Statistic from '..';
import { formatTimeStr } from '../utils';
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
import { sleep } from '../../../tests/utils';
describe('Statistic', () => {
beforeAll(() => {
@ -70,7 +69,7 @@ describe('Statistic', () => {
const instance = wrapper.instance();
expect(instance.countdownId).not.toBe(undefined);
await delay(10);
await sleep(10);
wrapper.unmount();
expect(instance.countdownId).toBe(undefined);

View File

@ -6,7 +6,10 @@ exports[`renders ./components/steps/demo/clickable.md correctly 1`] = `
class="ant-steps ant-steps-horizontal ant-steps-label-horizontal"
>
<div
class="ant-steps-item ant-steps-item-process"
class="ant-steps-item ant-steps-item-process ant-steps-item-active"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
@ -37,8 +40,12 @@ exports[`renders ./components/steps/demo/clickable.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
@ -69,8 +76,12 @@ exports[`renders ./components/steps/demo/clickable.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
@ -102,6 +113,7 @@ exports[`renders ./components/steps/demo/clickable.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-divider ant-divider-horizontal"
role="separator"
@ -110,7 +122,10 @@ exports[`renders ./components/steps/demo/clickable.md correctly 1`] = `
class="ant-steps ant-steps-vertical"
>
<div
class="ant-steps-item ant-steps-item-process"
class="ant-steps-item ant-steps-item-process ant-steps-item-active"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
@ -141,8 +156,12 @@ exports[`renders ./components/steps/demo/clickable.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
@ -173,8 +192,12 @@ exports[`renders ./components/steps/demo/clickable.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
@ -207,6 +230,7 @@ exports[`renders ./components/steps/demo/clickable.md correctly 1`] = `
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/steps/demo/customized-progress-dot.md correctly 1`] = `
@ -215,6 +239,9 @@ exports[`renders ./components/steps/demo/customized-progress-dot.md correctly 1`
>
<div
class="ant-steps-item ant-steps-item-finish"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -245,8 +272,12 @@ exports[`renders ./components/steps/demo/customized-progress-dot.md correctly 1`
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-process"
class="ant-steps-item ant-steps-item-process ant-steps-item-active"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -277,8 +308,12 @@ exports[`renders ./components/steps/demo/customized-progress-dot.md correctly 1`
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -309,8 +344,12 @@ exports[`renders ./components/steps/demo/customized-progress-dot.md correctly 1`
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -342,6 +381,7 @@ exports[`renders ./components/steps/demo/customized-progress-dot.md correctly 1`
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/steps/demo/error.md correctly 1`] = `
@ -350,6 +390,9 @@ exports[`renders ./components/steps/demo/error.md correctly 1`] = `
>
<div
class="ant-steps-item ant-steps-item-finish ant-steps-next-error"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -396,8 +439,12 @@ exports[`renders ./components/steps/demo/error.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-error"
class="ant-steps-item ant-steps-item-error ant-steps-item-active"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -444,8 +491,12 @@ exports[`renders ./components/steps/demo/error.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -475,6 +526,7 @@ exports[`renders ./components/steps/demo/error.md correctly 1`] = `
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/steps/demo/icon.md correctly 1`] = `
@ -482,7 +534,10 @@ exports[`renders ./components/steps/demo/icon.md correctly 1`] = `
class="ant-steps ant-steps-horizontal ant-steps-label-horizontal"
>
<div
class="ant-steps-item ant-steps-item-finish ant-steps-item-custom"
class="ant-steps-item ant-steps-item-finish ant-steps-item-custom ant-steps-item-active"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -524,8 +579,12 @@ exports[`renders ./components/steps/demo/icon.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-finish ant-steps-item-custom"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -567,8 +626,12 @@ exports[`renders ./components/steps/demo/icon.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-process ant-steps-item-custom"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -610,8 +673,12 @@ exports[`renders ./components/steps/demo/icon.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait ant-steps-item-custom"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -654,6 +721,480 @@ exports[`renders ./components/steps/demo/icon.md correctly 1`] = `
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/steps/demo/nav.md correctly 1`] = `
<div>
<div
style="border:1px solid rgb(235, 237, 240);margin-bottom:24px"
>
<div
class="ant-steps ant-steps-horizontal ant-steps-small ant-steps-label-horizontal ant-steps-navigation"
>
<div
class="ant-steps-item ant-steps-item-finish ant-steps-item-active"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
<i
aria-label="icon: check"
class="anticon anticon-check ant-steps-finish-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="check"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"
/>
</svg>
</i>
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
Step 1
<div
class="ant-steps-item-subtitle"
title="00:00:05"
>
00:00:05
</div>
</div>
<div
class="ant-steps-item-description"
>
This is a description.
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-process"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
2
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
Step 2
<div
class="ant-steps-item-subtitle"
title="00:01:02"
>
00:01:02
</div>
</div>
<div
class="ant-steps-item-description"
>
This is a description.
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
3
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
Step 3
<div
class="ant-steps-item-subtitle"
title="waiting for longlong time"
>
waiting for longlong time
</div>
</div>
<div
class="ant-steps-item-description"
>
This is a description.
</div>
</div>
</div>
</div>
</div>
</div>
<div
style="border:1px solid rgb(235, 237, 240);margin-bottom:24px"
>
<div
class="ant-steps ant-steps-horizontal ant-steps-label-horizontal ant-steps-navigation"
>
<div
class="ant-steps-item ant-steps-item-finish ant-steps-item-active"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
<i
aria-label="icon: check"
class="anticon anticon-check ant-steps-finish-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="check"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"
/>
</svg>
</i>
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
Step 1
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-process"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
2
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
Step 2
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
3
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
Step 3
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
4
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
Step 4
</div>
</div>
</div>
</div>
</div>
</div>
<div
style="border:1px solid rgb(235, 237, 240);margin-bottom:24px"
>
<div
class="ant-steps ant-steps-horizontal ant-steps-small ant-steps-label-horizontal ant-steps-navigation"
>
<div
class="ant-steps-item ant-steps-item-finish ant-steps-item-active"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
<i
aria-label="icon: check"
class="anticon anticon-check ant-steps-finish-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="check"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"
/>
</svg>
</i>
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
finish 1
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-finish"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
<i
aria-label="icon: check"
class="anticon anticon-check ant-steps-finish-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="check"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"
/>
</svg>
</i>
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
finish 2
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-process"
>
<div
class="ant-steps-item-container"
role="button"
tabindex="0"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
3
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
current process
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait ant-steps-item-disabled"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
/>
<div
class="ant-steps-item-icon"
>
<span
class="ant-steps-icon"
>
4
</span>
</div>
<div
class="ant-steps-item-content"
>
<div
class="ant-steps-item-title"
>
wait
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/steps/demo/progress-dot.md correctly 1`] = `
@ -662,6 +1203,9 @@ exports[`renders ./components/steps/demo/progress-dot.md correctly 1`] = `
>
<div
class="ant-steps-item ant-steps-item-finish"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -692,8 +1236,12 @@ exports[`renders ./components/steps/demo/progress-dot.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-process"
class="ant-steps-item ant-steps-item-process ant-steps-item-active"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -724,8 +1272,12 @@ exports[`renders ./components/steps/demo/progress-dot.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -757,6 +1309,7 @@ exports[`renders ./components/steps/demo/progress-dot.md correctly 1`] = `
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/steps/demo/simple.md correctly 1`] = `
@ -765,6 +1318,9 @@ exports[`renders ./components/steps/demo/simple.md correctly 1`] = `
>
<div
class="ant-steps-item ant-steps-item-finish"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -811,8 +1367,12 @@ exports[`renders ./components/steps/demo/simple.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-process"
class="ant-steps-item ant-steps-item-process ant-steps-item-active"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -833,6 +1393,12 @@ exports[`renders ./components/steps/demo/simple.md correctly 1`] = `
class="ant-steps-item-title"
>
In Progress
<div
class="ant-steps-item-subtitle"
title="Left 00:00:08"
>
Left 00:00:08
</div>
</div>
<div
class="ant-steps-item-description"
@ -841,8 +1407,12 @@ exports[`renders ./components/steps/demo/simple.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -872,6 +1442,7 @@ exports[`renders ./components/steps/demo/simple.md correctly 1`] = `
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/steps/demo/small-size.md correctly 1`] = `
@ -880,6 +1451,9 @@ exports[`renders ./components/steps/demo/small-size.md correctly 1`] = `
>
<div
class="ant-steps-item ant-steps-item-finish"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -921,8 +1495,12 @@ exports[`renders ./components/steps/demo/small-size.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-process"
class="ant-steps-item ant-steps-item-process ant-steps-item-active"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -946,8 +1524,12 @@ exports[`renders ./components/steps/demo/small-size.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -972,6 +1554,7 @@ exports[`renders ./components/steps/demo/small-size.md correctly 1`] = `
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/steps/demo/step-next.md correctly 1`] = `
@ -980,7 +1563,10 @@ exports[`renders ./components/steps/demo/step-next.md correctly 1`] = `
class="ant-steps ant-steps-horizontal ant-steps-label-horizontal"
>
<div
class="ant-steps-item ant-steps-item-process"
class="ant-steps-item ant-steps-item-process ant-steps-item-active"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -1004,8 +1590,12 @@ exports[`renders ./components/steps/demo/step-next.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -1029,8 +1619,12 @@ exports[`renders ./components/steps/demo/step-next.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -1055,6 +1649,7 @@ exports[`renders ./components/steps/demo/step-next.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="steps-content"
>
@ -1081,6 +1676,9 @@ exports[`renders ./components/steps/demo/vertical.md correctly 1`] = `
>
<div
class="ant-steps-item ant-steps-item-finish"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -1127,8 +1725,12 @@ exports[`renders ./components/steps/demo/vertical.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-process"
class="ant-steps-item ant-steps-item-process ant-steps-item-active"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -1157,8 +1759,12 @@ exports[`renders ./components/steps/demo/vertical.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -1188,6 +1794,7 @@ exports[`renders ./components/steps/demo/vertical.md correctly 1`] = `
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/steps/demo/vertical-small.md correctly 1`] = `
@ -1196,6 +1803,9 @@ exports[`renders ./components/steps/demo/vertical-small.md correctly 1`] = `
>
<div
class="ant-steps-item ant-steps-item-finish"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -1242,8 +1852,12 @@ exports[`renders ./components/steps/demo/vertical-small.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-process"
class="ant-steps-item ant-steps-item-process ant-steps-item-active"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -1272,8 +1886,12 @@ exports[`renders ./components/steps/demo/vertical-small.md correctly 1`] = `
</div>
</div>
</div>
</div>
<div
class="ant-steps-item ant-steps-item-wait"
>
<div
class="ant-steps-item-container"
>
<div
class="ant-steps-item-tail"
@ -1303,4 +1921,5 @@ exports[`renders ./components/steps/demo/vertical-small.md correctly 1`] = `
</div>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,84 @@
---
order: 11
title:
zh-CN: 导航步骤
en-US: Navgation Steps
---
## zh-CN
导航类型的步骤条。
## en-US
Navgation steps.
```jsx
import { Steps } from 'antd';
const { Step } = Steps;
class Demo extends React.Component {
state = {
current: 0,
};
onChange = current => {
console.log('onChange:', current);
this.setState({ current });
};
render() {
const { current } = this.state;
const containerStyle = {
border: '1px solid rgb(235, 237, 240)',
marginBottom: 24,
};
return (
<div>
<div style={containerStyle}>
<Steps type="navigation" size="small" current={current} onChange={this.onChange}>
<Step
title="Step 1"
subTitle="00:00:05"
status="finish"
description="This is a description."
/>
<Step
title="Step 2"
subTitle="00:01:02"
status="process"
description="This is a description."
/>
<Step
title="Step 3"
subTitle="waiting for longlong time"
status="wait"
description="This is a description."
/>
</Steps>
</div>
<div style={containerStyle}>
<Steps type="navigation" current={current} onChange={this.onChange}>
<Step status="finish" title="Step 1" />
<Step status="process" title="Step 2" />
<Step status="wait" title="Step 3" />
<Step status="wait" title="Step 4" />
</Steps>
</div>
<div style={containerStyle}>
<Steps type="navigation" size="small" current={current} onChange={this.onChange}>
<Step status="finish" title="finish 1" />
<Step status="finish" title="finish 2" />
<Step status="process" title="current process" />
<Step status="wait" title="wait" disabled />
</Steps>
</div>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -21,7 +21,7 @@ const { Step } = Steps;
ReactDOM.render(
<Steps current={1}>
<Step title="Finished" description="This is a description." />
<Step title="In Progress" description="This is a description." />
<Step title="In Progress" subTitle="Left 00:00:08" description="This is a description." />
<Step title="Waiting" description="This is a description." />
</Steps>,
mountNode,

View File

@ -28,6 +28,7 @@ The whole of the step bar.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| className | additional class to Steps | string | - | 3.11.3 |
| type | type of steps, can be set to one of the following values: `default`, `navigation` | string | `default` | 3.22.0 |
| current | to set the current step, counting from 0. You can overwrite this state by using `status` of `Step` | number | 0 | |
| direction | to specify the direction of the step bar, `horizontal` or `vertical` | string | `horizontal` | |
| labelPlacement | place title and description with `horizontal` or `vertical` direction | string | `horizontal` | 3.7.3 |
@ -47,3 +48,5 @@ A single step in the step bar.
| icon | icon of the step, optional property | string\|ReactNode | - | |
| status | to specify the status. It will be automatically set by `current` of `Steps` if not configured. Optional values are: `wait` `process` `finish` `error` | string | `wait` | |
| title | title of the step | string\|ReactNode | - | |
| subTitle | subTitle of the step | string\|ReactNode | - | 3.22.0 |
| disabled | disable click | boolean | false | 3.22.0 |

View File

@ -5,6 +5,7 @@ import Icon from '../icon';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
export interface StepsProps {
type?: 'default' | 'navigation';
className?: string;
current?: number;
direction?: 'horizontal' | 'vertical';
@ -25,6 +26,7 @@ export interface StepProps {
icon?: React.ReactNode;
onClick?: React.MouseEventHandler<HTMLElement>;
status?: 'wait' | 'process' | 'finish' | 'error';
disabled?: boolean;
title?: React.ReactNode;
style?: React.CSSProperties;
}

View File

@ -29,6 +29,7 @@ title: Steps
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| className | 步骤条类名 | string | - | 3.11.3 |
| type | 步骤条类型,有 `default``navigation` 两种 | string | `default` | 3.22.0 |
| current | 指定当前步骤,从 0 开始记数。在子 Step 元素中,可以通过 `status` 属性覆盖状态 | number | 0 | |
| direction | 指定步骤条方向。目前支持水平(`horizontal`)和竖直(`vertical`)两种方向 | string | horizontal | |
| labelPlacement | 指定标签放置位置,默认水平放图标右侧,可选 `vertical` 放图标下方 | string | `horizontal` | 3.7.3 |
@ -48,3 +49,5 @@ title: Steps
| icon | 步骤图标的类型,可选 | string\|ReactNode | - | |
| status | 指定状态。当不配置该属性时,会使用 Steps 的 `current` 来自动指定状态。可选:`wait` `process` `finish` `error` | string | wait | |
| title | 标题 | string\|ReactNode | - | |
| subTitle | 子标题 | string\|ReactNode | - | 3.22.0 |
| disabled | 禁用点击 | boolean | false | 3.22.0 |

View File

@ -20,12 +20,15 @@
@error-description-color: @error-color;
@error-tail-color: @wait-tail-color;
@steps-background: @component-background;
@steps-nav-arrow-color: fade(@black, 25%);
@steps-nav-active-color: @primary-color;
@steps-icon-size: 32px;
@steps-small-icon-size: 24px;
@steps-dot-size: 8px;
@steps-current-dot-size: 10px;
@steps-desciption-max-width: 140px;
@steps-nav-content-max-width: @steps-desciption-max-width;
.@{steps-prefix-cls} {
.reset-component;
@ -42,12 +45,16 @@
overflow: hidden;
vertical-align: top;
&-container {
outline: none;
}
&:last-child {
flex: none;
}
&:last-child > &-tail,
&:last-child > &-content > &-title::after {
&:last-child > &-container > &-tail,
&:last-child > &-container > &-content > &-title::after {
display: none;
}
@ -110,6 +117,13 @@
content: '';
}
}
&-subtitle {
display: inline;
color: @text-color-secondary;
font-size: @font-size-base;
margin-left: 8px;
font-weight: normal;
}
&-description {
color: @text-color-secondary;
font-size: @font-size-base;
@ -131,12 +145,12 @@
&.@{steps-prefix-cls}-next-error .@{steps-prefix-cls}-item-title::after {
background: @error-icon-color;
}
}
// ===================== Clickable =====================
&[role='button'] {
outline: none;
&:not(.@{steps-prefix-cls}-item-process) {
.@{steps-prefix-cls}:not(.@{steps-prefix-cls}-navigation) .@{steps-prefix-cls}-item {
&:not(.@{steps-prefix-cls}-item-active) {
& > .@{steps-prefix-cls}-item-container[role='button'] {
cursor: pointer;
.@{steps-prefix-cls}-item {
@ -150,10 +164,17 @@
&:hover {
.@{steps-prefix-cls}-item {
&-title,
&-subtitle,
&-description {
color: @primary-color;
}
}
}
}
&:not(.@{steps-prefix-cls}-item-process) {
& > .@{steps-prefix-cls}-item-container[role='button']:hover {
.@{steps-prefix-cls}-item {
&-icon {
border-color: @primary-color;
@ -202,16 +223,16 @@
}
}
}
&-@{status} > &-content > &-title {
&-@{status} > &-container > &-content > &-title {
color: @@title-color;
&::after {
background-color: @@tail-color;
}
}
&-@{status} > &-content > &-description {
&-@{status} > &-container > &-content > &-description {
color: @@description-color;
}
&-@{status} > &-tail::after {
&-@{status} > &-container > &-tail::after {
background-color: @@tail-color;
}
}
@ -221,4 +242,5 @@
@import 'vertical';
@import 'label-placement';
@import 'progress-dot';
@import 'nav';
@import 'compatibility';

View File

@ -0,0 +1,91 @@
.@{steps-prefix-cls}-navigation {
padding-top: 12px;
&.@{steps-prefix-cls}-small {
.@{steps-prefix-cls}-item {
&-container {
margin-left: -12px;
}
}
}
.@{steps-prefix-cls}-item {
text-align: center;
overflow: visible;
&-container {
display: inline-block;
text-align: left;
height: 100%;
padding-bottom: 12px;
margin-left: -16px;
transition: opacity 0.3s;
.@{steps-prefix-cls}-item-content {
max-width: @steps-nav-content-max-width;
}
.@{steps-prefix-cls}-item-title {
padding-right: 0;
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&::after {
display: none;
}
}
}
&:not(.@{steps-prefix-cls}-item-active) {
.@{steps-prefix-cls}-item-container[role='button'] {
cursor: pointer;
&:hover {
opacity: 0.85;
}
}
}
&:last-child {
flex: 1;
&::after {
display: none;
}
}
&::after {
content: '';
display: inline-block;
width: 12px;
height: 12px;
border: 1px solid @steps-nav-arrow-color;
border-bottom: none;
border-left: none;
transform: rotate(45deg);
position: absolute;
top: 50%;
left: 100%;
margin-top: -14px;
margin-left: -2px;
}
&::before {
content: '';
display: inline-block;
position: absolute;
left: 50%;
bottom: 0;
height: 3px;
width: 0;
background-color: @steps-nav-active-color;
transition: width 0.3s, left 0.3s;
transition-timing-function: ease-out;
}
}
.@{steps-prefix-cls}-item.@{steps-prefix-cls}-item-active::before {
left: 0;
width: 100%;
}
}

View File

@ -1,4 +1,4 @@
.@{steps-prefix-cls}-small {
.@{steps-prefix-cls}-small .@{steps-prefix-cls}-item-container {
&.@{steps-prefix-cls}-horizontal:not(.@{steps-prefix-cls}-label-vertical)
.@{steps-prefix-cls}-item {
margin-right: 12px;

View File

@ -20,7 +20,9 @@
}
}
> .@{steps-prefix-cls}-item > .@{steps-prefix-cls}-item-tail {
> .@{steps-prefix-cls}-item
> .@{steps-prefix-cls}-item-container
> .@{steps-prefix-cls}-item-tail {
position: absolute;
top: 0;
left: 16px;
@ -33,11 +35,14 @@
}
}
> .@{steps-prefix-cls}-item:not(:last-child) > .@{steps-prefix-cls}-item-tail {
> .@{steps-prefix-cls}-item:not(:last-child)
> .@{steps-prefix-cls}-item-container
> .@{steps-prefix-cls}-item-tail {
display: block;
}
> .@{steps-prefix-cls}-item
> .@{steps-prefix-cls}-item-container
> .@{steps-prefix-cls}-item-content
> .@{steps-prefix-cls}-item-title {
&::after {
@ -45,7 +50,7 @@
}
}
&.@{steps-prefix-cls}-small {
&.@{steps-prefix-cls}-small .@{steps-prefix-cls}-item-container {
.@{steps-prefix-cls}-item-tail {
position: absolute;
top: 0;

View File

@ -0,0 +1,5 @@
const locale = {
placeholder: 'Selectează ora',
};
export default locale;

View File

@ -7,8 +7,7 @@ import Checkbox from '../../checkbox';
import DatePicker from '../../date-picker';
import Input from '../../input';
import Group from '../../input/Group';
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
import { sleep } from '../../../tests/utils';
describe('Tooltip', () => {
it('check `onVisibleChange` arguments', () => {
@ -205,12 +204,12 @@ describe('Tooltip', () => {
expect(wrapper.find('span.ant-calendar-picker')).toHaveLength(1);
const picker = wrapper.find('span.ant-calendar-picker').at(0);
picker.simulate('mouseenter');
await delay(100);
await sleep(100);
expect(onVisibleChange).toHaveBeenCalledWith(true);
expect(wrapper.instance().tooltip.props.visible).toBe(true);
picker.simulate('mouseleave');
await delay(100);
await sleep(100);
expect(onVisibleChange).toHaveBeenCalledWith(false);
expect(wrapper.instance().tooltip.props.visible).toBe(false);
});
@ -230,12 +229,12 @@ describe('Tooltip', () => {
expect(wrapper.find('Group')).toHaveLength(1);
const picker = wrapper.find('Group').at(0);
picker.simulate('mouseenter');
await delay(100);
await sleep(100);
expect(onVisibleChange).toHaveBeenCalledWith(true);
expect(wrapper.instance().tooltip.props.visible).toBe(true);
picker.simulate('mouseleave');
await delay(100);
await sleep(100);
expect(onVisibleChange).toHaveBeenCalledWith(false);
expect(wrapper.instance().tooltip.props.visible).toBe(false);
});

View File

@ -6,8 +6,7 @@ import Form from '../../form';
import { spyElementPrototypes } from '../../__tests__/util/domHook';
import { errorRequest, successRequest } from './requests';
import { setup, teardown } from './mock';
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
import { sleep } from '../../../tests/utils';
const fileList = [
{
@ -124,7 +123,7 @@ describe('Upload List', () => {
.at(0)
.find('.anticon-close')
.simulate('click');
await delay(400);
await sleep(400);
wrapper.update();
expect(wrapper.find('.ant-upload-list-item').hostNodes().length).toBe(1);
});
@ -245,7 +244,7 @@ describe('Upload List', () => {
.at(1)
.simulate('click');
expect(handleRemove).toHaveBeenCalledWith(fileList[1]);
await delay(0);
await sleep();
expect(handleChange.mock.calls.length).toBe(2);
});
@ -274,7 +273,7 @@ describe('Upload List', () => {
</Upload>,
);
wrapper.setState({});
await delay(0);
await sleep();
expect(wrapper.state().fileList[2].thumbUrl).not.toBe(undefined);
expect(onDrawImage).toHaveBeenCalled();
@ -525,7 +524,7 @@ describe('Upload List', () => {
</Upload>,
);
wrapper.setState({});
await delay(0);
await sleep();
expect(previewFile).toHaveBeenCalledWith(file.originFileObj);
wrapper.update();

View File

@ -66,6 +66,7 @@ Supported languages:
| Tamil | ta_IN |
| Thai | th_TH |
| Turkish | tr_TR |
| Romanian | ro_RO |
| Russian | ru_RU |
| Ukrainian | uk_UA |
| Vietnamese | vi_VN |

View File

@ -65,6 +65,7 @@ return (
| 泰米尔语 | ta_IN |
| 泰语 | th_TH |
| 土耳其语 | tr_TR |
| 罗马尼亚语 | ro_RO |
| 俄罗斯语 | ru_RU |
| 乌克兰语 | uk_UA |
| 越南语 | vi_VN |

View File

@ -69,7 +69,7 @@
"rc-editor-mention": "^1.1.13",
"rc-form": "^2.4.5",
"rc-input-number": "~4.4.5",
"rc-mentions": "~0.3.1",
"rc-mentions": "~0.4.0",
"rc-menu": "~7.4.23",
"rc-notification": "~3.3.1",
"rc-pagination": "~1.20.5",
@ -77,7 +77,7 @@
"rc-rate": "~2.5.0",
"rc-select": "~9.2.0",
"rc-slider": "~8.6.11",
"rc-steps": "~3.4.1",
"rc-steps": "~3.5.0",
"rc-switch": "~1.9.0",
"rc-table": "~6.7.0",
"rc-tabs": "~9.6.4",

View File

@ -8,3 +8,5 @@ export function setMockDate(dateString = '2017-09-18T03:30:07.795') {
export function resetMockDate() {
MockDate.reset();
}
export const sleep = (timeout = 0) => new Promise(resolve => setTimeout(resolve, timeout));