Merge branch 'master' into feature-3.6.0

This commit is contained in:
afc163 2018-05-28 14:41:02 +08:00
commit 21ca50fca1
24 changed files with 1227 additions and 95 deletions

View File

@ -14,6 +14,17 @@ timeline: true
* Major version release is not included in this schedule for breaking change and new features.
---
## 3.5.4
`2018-05-26`
- 🐞 Fix `showSearch` on `Cascader` not working.[968488a2](https://github.com/ant-design/ant-design/commit/968488a2fac9bcb16bee9f0c248f49bca00dbec6)
- 🐞 `Badge[status]` support `Tooltip`.[#10626](https://github.com/ant-design/ant-design/issues/10626)
- 🐞 Fix `text-align` on parent element affects `Spin`.[#10643](https://github.com/ant-design/ant-design/pull/10643) [@wmzhong](https://github.com/wmzhong)
- 💄 `Table` break line style change from `break-all` to `break-word`.[#10655](https://github.com/ant-design/ant-design/pull/10655) [@clinyong](https://github.com/clinyong)
- 🌟 When `Search` not define `enterButton`, click search icon will trigger `onSearch`. [36ffe7e1](https://github.com/ant-design/ant-design/commit/36ffe7e1dc9d9473c8c68168ab79b7a03a604702)
## 3.5.3
`2018-05-20`
@ -456,7 +467,7 @@ Happy 2018 !~ 2018 2018 2018 coming!~~~
`2017-12-04`
Learn more in the [Ant Design 3.0 announcement post](https://medium.com/ant-design/announcing-ant-design-3-0-70e3e65eca0c)!
Learn more in the [Ant Design 3.0 announcement post](https://medium.com/ant-design/announcing-ant-design-3-0-70e3e65eca0c).
### Major Changes

View File

@ -14,11 +14,21 @@ timeline: true
* 主版本号:含有破坏性更新和新特性,不在发布周期内。
---
## 3.5.4
`2018-05-26`
- 🐞 修复 `Cascader``showSearch` 无效问题。[968488a2](https://github.com/ant-design/ant-design/commit/968488a2fac9bcb16bee9f0c248f49bca00dbec6)
- 🐞 使 `Badge[status]` 支持 `Tooltip`。[#10626](https://github.com/ant-design/ant-design/issues/10626)
- 🐞 修复父元素使用 `text-align` 会影响 `Spin` 的问题。[#10643](https://github.com/ant-design/ant-design/pull/10643) [@wmzhong](https://github.com/wmzhong)
- 💄 `Table` 换行从 `break-all` 改为 `break-word`。[#10655](https://github.com/ant-design/ant-design/pull/10655) [@clinyong](https://github.com/clinyong)
- 🌟 `Search` 在未定义 `enterButton` 时,点击搜索图标将触发 `onSearch`。 [36ffe7e1](https://github.com/ant-design/ant-design/commit/36ffe7e1dc9d9473c8c68168ab79b7a03a604702)
## 3.5.3
`2018-05-20`
- 🐞 修复了 `Affix``offsetTop === 0`, 值将变为 `undefined` 的问题 [#10566](https://github.com/ant-design/ant-design/pull/10566)
- 🐞 修复了 `Menu` item 中的高亮链接颜色问题 [09d5e36](https://github.com/ant-design/ant-design/commit/09d5e36cfa27e371a7b4d4e68276a279698ea901)
- 🐞 修复了 `Input.Group` 组件阴影被遮盖的问题 [#10230](https://github.com/ant-design/ant-design/issues/10230)
@ -459,11 +469,11 @@ timeline: true
`2017-12-04`
更多内容见 [Ant Design 3.0 发布公告](https://medium.com/ant-design/announcing-ant-design-3-0-70e3e65eca0c)
更多内容见 [Ant Design 3.0 发布公告](https://medium.com/ant-design/announcing-ant-design-3-0-70e3e65eca0c)
### 主要变化
- 全新的[色彩系统](https://ant.design/docs/spec/colors-cn#Color-Palettes),组件主色由 『`#108EE9`』 改为 『`#1890FF`』,新主色我们称之为『拂晓蓝』。
- 全新的[色彩系统](https://ant.design/docs/spec/colors-cn#Color-Palettes),组件主色由『`#108EE9`』改为『`#1890FF`』,新主色我们称之为『拂晓蓝』。
- 全新的视觉样式和组件尺寸,更现代更美观。
- 基础字体大小由 `12px` 增大到 `14px`
- 默认语言由中文改为英文。
@ -475,6 +485,8 @@ timeline: true
### 不兼容改动
> 如果你从 2.x 升级到 3.x建议直接升级到 3.x 的最新版本。
此版本有部分不兼容的改动,升级时确保修改相应的使用代码。另外由于人肉查找代码中的废弃用法过于低效,所以我们提供了 [antd-migration-helper](https://github.com/ant-design/antd-migration-helper) 用于扫描代码中的废弃用法。
- Card 的 `noHovering` 属性重命名为 `hoverable`,且默认值改为 `true`
@ -521,7 +533,7 @@ timeline: true
- Form 下的表单控件不再默认为 `size="large"`
- `Input.Search` 默认的 🔍 图标只作为装饰,不再响应用户交互。需要添加可交互按钮请使用 `enterButton`
- UMD 版本的 `dist/antd.js` 不再包含 moment使用的时候需要自己引入 moment。
- UMD 版本的 `dist/antd.js` 不再包含 moment使用的时候需要自己引入 moment。
```diff
<html>
<head>

View File

@ -0,0 +1,31 @@
import React from 'react';
import { mount } from 'enzyme';
import Alert from '..';
describe('Alert', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it('could be closed', () => {
const onClose = jest.fn();
const afterClose = jest.fn();
const wrapper = mount(
<Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
closable
onClose={onClose}
afterClose={afterClose}
/>
);
wrapper.find('.ant-alert-close-icon').simulate('click');
expect(onClose).toBeCalled();
jest.runAllTimers();
expect(afterClose).toBeCalled();
});
});

View File

@ -62,10 +62,10 @@ describe('Anchor Render', () => {
wrapper.instance().handleScrollTo('##API');
expect(wrapper.instance().state.activeLink).toBe('##API');
expect(scrollToSpy).not.toHaveBeenCalled();
await new Promise(resolve => setTimeout(resolve, 100));
await new Promise(resolve => setTimeout(resolve, 50));
expect(scrollToSpy).toHaveBeenCalled();
expect(wrapper.instance().animating).toBe(true);
await new Promise(resolve => setTimeout(resolve, 400));
await new Promise(resolve => setTimeout(resolve, 500));
expect(wrapper.instance().animating).toBe(false);
});

View File

@ -196,7 +196,7 @@ exports[`Badge should render when count is changed 1`] = `
>
<AnimateChild
animation={Object {}}
key="rc_animate_1527238793036"
key="scrollNumber"
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
@ -206,7 +206,7 @@ exports[`Badge should render when count is changed 1`] = `
className="ant-badge-count ant-badge-multiple-words"
count={10}
data-show={true}
key="rc_animate_1527238793036"
key="scrollNumber"
onAnimated={[Function]}
prefixCls="ant-scroll-number"
title={10}
@ -445,7 +445,7 @@ exports[`Badge should render when count is changed 2`] = `
>
<AnimateChild
animation={Object {}}
key="rc_animate_1527238793036"
key="scrollNumber"
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
@ -455,7 +455,7 @@ exports[`Badge should render when count is changed 2`] = `
className="ant-badge-count ant-badge-multiple-words"
count={11}
data-show={true}
key="rc_animate_1527238793036"
key="scrollNumber"
onAnimated={[Function]}
prefixCls="ant-scroll-number"
title={11}
@ -887,7 +887,7 @@ exports[`Badge should render when count is changed 3`] = `
>
<AnimateChild
animation={Object {}}
key="rc_animate_1527238793036"
key="scrollNumber"
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
@ -897,7 +897,7 @@ exports[`Badge should render when count is changed 3`] = `
className="ant-badge-count ant-badge-multiple-words"
count={11}
data-show={true}
key="rc_animate_1527238793036"
key="scrollNumber"
onAnimated={[Function]}
prefixCls="ant-scroll-number"
title={11}
@ -1329,7 +1329,7 @@ exports[`Badge should render when count is changed 4`] = `
>
<AnimateChild
animation={Object {}}
key="rc_animate_1527238793036"
key="scrollNumber"
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
@ -1339,7 +1339,7 @@ exports[`Badge should render when count is changed 4`] = `
className="ant-badge-count ant-badge-multiple-words"
count={10}
data-show={true}
key="rc_animate_1527238793036"
key="scrollNumber"
onAnimated={[Function]}
prefixCls="ant-scroll-number"
title={10}
@ -1771,7 +1771,7 @@ exports[`Badge should render when count is changed 5`] = `
>
<AnimateChild
animation={Object {}}
key="rc_animate_1527238793036"
key="scrollNumber"
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
@ -1781,7 +1781,7 @@ exports[`Badge should render when count is changed 5`] = `
className="ant-badge-count"
count={9}
data-show={true}
key="rc_animate_1527238793036"
key="scrollNumber"
onAnimated={[Function]}
prefixCls="ant-scroll-number"
title={9}

View File

@ -107,6 +107,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
count={displayCount}
title={title || count}
style={styleWithOffset}
key="scrollNumber"
/>
);

View File

@ -0,0 +1,170 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`react router react router 3 1`] = `
<Breadcrumb
params={
Object {
"id": 1,
}
}
prefixCls="ant-breadcrumb"
routes={
Array [
Object {
"breadcrumbName": "Home",
"childRoutes": Array [
Object {
"breadcrumbName": "Application List",
"childRoutes": Array [
Object {
"breadcrumbName": "Application:id",
"childRoutes": Array [
Object {
"breadcrumbName": "Detail",
"name": "detail",
"path": "detail",
},
],
"name": "app",
"path": ":id",
},
],
"name": "apps",
"path": "apps",
},
],
"name": "home",
"path": "/",
},
Object {
"breadcrumbName": "Application List",
"childRoutes": Array [
Object {
"breadcrumbName": "Application:id",
"childRoutes": Array [
Object {
"breadcrumbName": "Detail",
"name": "detail",
"path": "detail",
},
],
"name": "app",
"path": ":id",
},
],
"name": "apps",
"path": "apps",
},
Object {
"breadcrumbName": "Application:id",
"childRoutes": Array [
Object {
"breadcrumbName": "Detail",
"name": "detail",
"path": "detail",
},
],
"name": "app",
"path": ":id",
},
Object {
"breadcrumbName": "Detail",
"name": "detail",
"path": "detail",
},
]
}
separator="/"
>
<div
className="ant-breadcrumb"
>
<BreadcrumbItem
key="Home"
prefixCls="ant-breadcrumb"
separator="/"
>
<span>
<span
className="ant-breadcrumb-link"
>
<a
href="#/"
>
Home
</a>
</span>
<span
className="ant-breadcrumb-separator"
>
/
</span>
</span>
</BreadcrumbItem>
<BreadcrumbItem
key="Application List"
prefixCls="ant-breadcrumb"
separator="/"
>
<span>
<span
className="ant-breadcrumb-link"
>
<a
href="#/apps"
>
Application List
</a>
</span>
<span
className="ant-breadcrumb-separator"
>
/
</span>
</span>
</BreadcrumbItem>
<BreadcrumbItem
key="Application:id"
prefixCls="ant-breadcrumb"
separator="/"
>
<span>
<span
className="ant-breadcrumb-link"
>
<a
href="#/apps/1"
>
Application1
</a>
</span>
<span
className="ant-breadcrumb-separator"
>
/
</span>
</span>
</BreadcrumbItem>
<BreadcrumbItem
key="Detail"
prefixCls="ant-breadcrumb"
separator="/"
>
<span>
<span
className="ant-breadcrumb-link"
>
<span>
Detail
</span>
</span>
<span
className="ant-breadcrumb-separator"
>
/
</span>
</span>
</BreadcrumbItem>
</div>
</Breadcrumb>
`;

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Route, Switch, Link, withRouter, MemoryRouter } from 'react-router-dom';
import { Breadcrumb } from 'antd';
import { mount } from 'enzyme';
import Breadcrumb from '../index';
const Apps = () => (
<ul className="app-list">
@ -78,4 +78,73 @@ describe('react router', () => {
expect(wrapper.find('BreadcrumbItem').length).toBe(2);
expect(wrapper.find('BreadcrumbItem .ant-breadcrumb-link').at(1).text()).toBe('Application List');
});
it('react router 3', () => {
const routes = [{
name: 'home',
breadcrumbName: 'Home',
path: '/',
childRoutes: [
{
name: 'apps',
breadcrumbName: 'Application List',
path: 'apps',
childRoutes: [
{
name: 'app',
breadcrumbName: 'Application:id',
path: ':id',
childRoutes: [
{
name: 'detail',
breadcrumbName: 'Detail',
path: 'detail',
},
],
},
],
},
],
},
{
name: 'apps',
breadcrumbName: 'Application List',
path: 'apps',
childRoutes: [
{
name: 'app',
breadcrumbName: 'Application:id',
path: ':id',
childRoutes: [
{
name: 'detail',
breadcrumbName: 'Detail',
path: 'detail',
},
],
},
],
},
{
name: 'app',
breadcrumbName: 'Application:id',
path: ':id',
childRoutes: [
{
name: 'detail',
breadcrumbName: 'Detail',
path: 'detail',
},
],
},
{
name: 'detail',
breadcrumbName: 'Detail',
path: 'detail',
}];
const wrapper = mount(
<Breadcrumb routes={routes} params={{ id: 1 }} />
);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -215,6 +215,632 @@ exports[`Cascader popup correctly with defaultValue 1`] = `
</div>
`;
exports[`Cascader should highlight keyword and filter when search in Cascader 1`] = `
<Popup
action={
Array [
"click",
]
}
align={
Object {
"offset": Array [
0,
4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"tl",
"bl",
],
}
}
className=""
destroyPopupOnHide={false}
getClassNameFromAlign={[Function]}
getRootDomNode={[Function]}
mask={false}
onAlign={[Function]}
prefixCls="ant-cascader-menus"
style={Object {}}
transitionName="slide-up"
visible={true}
>
<div>
<Animate
animation={Object {}}
component=""
componentProps={Object {}}
exclusive={true}
onAppear={[Function]}
onEnd={[Function]}
onEnter={[Function]}
onLeave={[Function]}
showProp="xVisible"
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
transitionName="slide-up"
>
<AnimateChild
animation={Object {}}
key="popup"
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
transitionName="slide-up"
>
<Align
align={
Object {
"offset": Array [
0,
4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"tl",
"bl",
],
}
}
childrenProps={
Object {
"visible": "xVisible",
}
}
disabled={false}
key="popup"
monitorBufferTime={50}
monitorWindowResize={true}
onAlign={[Function]}
target={[Function]}
xVisible={true}
>
<PopupInner
className="ant-cascader-menus ant-cascader-menus-placement-bottomLeft "
hiddenClassName="ant-cascader-menus-hidden"
prefixCls="ant-cascader-menus"
style={Object {}}
visible={true}
>
<div
className="ant-cascader-menus ant-cascader-menus-placement-bottomLeft "
style={Object {}}
>
<LazyRenderBox
className="ant-cascader-menus-content"
visible={true}
>
<Menus
activeValue={Array []}
allowClear={true}
builtinPlacements={
Object {
"bottomLeft": Object {
"offset": Array [
0,
4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"tl",
"bl",
],
},
"bottomRight": Object {
"offset": Array [
0,
4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"tr",
"br",
],
},
"topLeft": Object {
"offset": Array [
0,
-4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"bl",
"tl",
],
},
"topRight": Object {
"offset": Array [
0,
-4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"br",
"tr",
],
},
}
}
defaultFiledNames={
Object {
"children": "children",
"label": "label",
"value": "value",
}
}
disabled={false}
dropdownMenuColumnStyle={
Object {
"width": 0,
}
}
expandTrigger="click"
filedNames={
Object {
"children": "children",
"label": "label",
"value": "value",
}
}
inputPrefixCls="ant-input"
notFoundContent="Not Found"
onChange={[Function]}
onPopupVisibleChange={[Function]}
onSelect={[Function]}
options={
Array [
Object {
"__IS_FILTERED_OPTION": true,
"disabled": false,
"label": Array [
"Jiangsu",
Array [
" / ",
"Nanjing",
],
Array [
" / ",
"Zhong Hua Men",
],
],
"path": Array [
Object {
"children": Array [
Object {
"children": Array [
Object {
"label": "Zhong Hua Men",
"value": "zhonghuamen",
},
],
"label": "Nanjing",
"value": "nanjing",
},
],
"label": "Jiangsu",
"value": "jiangsu",
},
Object {
"children": Array [
Object {
"label": "Zhong Hua Men",
"value": "zhonghuamen",
},
],
"label": "Nanjing",
"value": "nanjing",
},
Object {
"label": "Zhong Hua Men",
"value": "zhonghuamen",
},
],
"value": Array [
"jiangsu",
"nanjing",
"zhonghuamen",
],
},
Object {
"__IS_FILTERED_OPTION": true,
"disabled": false,
"label": Array [
"Zhejiang",
Array [
" / ",
Array [
"Hang",
Array [
<span
className="ant-cascader-menu-item-keyword"
>
z
</span>,
"hou",
],
],
],
Array [
" / ",
"West Lake",
],
],
"path": Array [
Object {
"children": Array [
Object {
"children": Array [
Object {
"label": "West Lake",
"value": "xihu",
},
],
"label": "Hangzhou",
"value": "hangzhou",
},
],
"label": "Zhejiang",
"value": "zhejiang",
},
Object {
"children": Array [
Object {
"label": "West Lake",
"value": "xihu",
},
],
"label": "Hangzhou",
"value": "hangzhou",
},
Object {
"label": "West Lake",
"value": "xihu",
},
],
"value": Array [
"zhejiang",
"hangzhou",
"xihu",
],
},
]
}
placeholder="Please select"
popupClassName=""
popupPlacement="bottomLeft"
popupVisible={true}
prefixCls="ant-cascader"
showSearch={
Object {
"filter": [Function],
}
}
transitionName="slide-up"
value={Array []}
visible={true}
>
<div>
<ul
className="ant-cascader-menu"
key="0"
style={
Object {
"width": 0,
}
}
>
<li
className="ant-cascader-menu-item"
key="jiangsu,nanjing,zhonghuamen"
onClick={[Function]}
title=""
>
Jiangsu
/
Nanjing
/
Zhong Hua Men
</li>
<li
className="ant-cascader-menu-item"
key="zhejiang,hangzhou,xihu"
onClick={[Function]}
title=""
>
Zhejiang
/
Hang
<span
className="ant-cascader-menu-item-keyword"
key="seperator"
>
z
</span>
hou
/
West Lake
</li>
</ul>
</div>
</Menus>
</LazyRenderBox>
</div>
</PopupInner>
</Align>
</AnimateChild>
</Animate>
</div>
</Popup>
`;
exports[`Cascader should render not found content 1`] = `
<Popup
action={
Array [
"click",
]
}
align={
Object {
"offset": Array [
0,
4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"tl",
"bl",
],
}
}
className=""
destroyPopupOnHide={false}
getClassNameFromAlign={[Function]}
getRootDomNode={[Function]}
mask={false}
onAlign={[Function]}
prefixCls="ant-cascader-menus"
style={Object {}}
transitionName="slide-up"
visible={true}
>
<div>
<Animate
animation={Object {}}
component=""
componentProps={Object {}}
exclusive={true}
onAppear={[Function]}
onEnd={[Function]}
onEnter={[Function]}
onLeave={[Function]}
showProp="xVisible"
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
transitionName="slide-up"
>
<AnimateChild
animation={Object {}}
key="popup"
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
transitionName="slide-up"
>
<Align
align={
Object {
"offset": Array [
0,
4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"tl",
"bl",
],
}
}
childrenProps={
Object {
"visible": "xVisible",
}
}
disabled={false}
key="popup"
monitorBufferTime={50}
monitorWindowResize={true}
onAlign={[Function]}
target={[Function]}
xVisible={true}
>
<PopupInner
className="ant-cascader-menus ant-cascader-menus-placement-bottomLeft "
hiddenClassName="ant-cascader-menus-hidden"
prefixCls="ant-cascader-menus"
style={Object {}}
visible={true}
>
<div
className="ant-cascader-menus ant-cascader-menus-placement-bottomLeft "
style={Object {}}
>
<LazyRenderBox
className="ant-cascader-menus-content"
visible={true}
>
<Menus
activeValue={Array []}
allowClear={true}
builtinPlacements={
Object {
"bottomLeft": Object {
"offset": Array [
0,
4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"tl",
"bl",
],
},
"bottomRight": Object {
"offset": Array [
0,
4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"tr",
"br",
],
},
"topLeft": Object {
"offset": Array [
0,
-4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"bl",
"tl",
],
},
"topRight": Object {
"offset": Array [
0,
-4,
],
"overflow": Object {
"adjustX": 1,
"adjustY": 1,
},
"points": Array [
"br",
"tr",
],
},
}
}
defaultFiledNames={
Object {
"children": "children",
"label": "label",
"value": "value",
}
}
disabled={false}
dropdownMenuColumnStyle={
Object {
"height": "auto",
"width": 0,
}
}
expandTrigger="click"
filedNames={
Object {
"children": "children",
"label": "label",
"value": "value",
}
}
inputPrefixCls="ant-input"
notFoundContent="Not Found"
onChange={[Function]}
onPopupVisibleChange={[Function]}
onSelect={[Function]}
options={
Array [
Object {
"disabled": true,
"label": "Not Found",
"value": "ANT_CASCADER_NOT_FOUND",
},
]
}
placeholder="Please select"
popupClassName=""
popupPlacement="bottomLeft"
popupVisible={true}
prefixCls="ant-cascader"
showSearch={
Object {
"filter": [Function],
}
}
transitionName="slide-up"
value={Array []}
visible={true}
>
<div>
<ul
className="ant-cascader-menu"
key="0"
style={
Object {
"height": "auto",
"width": 0,
}
}
>
<li
className="ant-cascader-menu-item ant-cascader-menu-item-disabled"
key="ANT_CASCADER_NOT_FOUND"
onClick={[Function]}
title="Not Found"
>
Not Found
</li>
</ul>
</div>
</Menus>
</LazyRenderBox>
</div>
</PopupInner>
</Align>
</AnimateChild>
</Animate>
</div>
</Popup>
`;
exports[`Cascader support controlled mode 1`] = `
<span
class="ant-cascader-picker"

View File

@ -28,6 +28,10 @@ const options = [{
}],
}];
function filter(inputValue, path) {
return path.some(option => (option.label).toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
}
describe('Cascader', () => {
focusTest(Cascader);
@ -39,11 +43,13 @@ describe('Cascader', () => {
});
it('popup correctly when panel is open', () => {
const onPopupVisibleChange = jest.fn();
const wrapper = mount(
<Cascader options={options} />
<Cascader options={options} onPopupVisibleChange={onPopupVisibleChange} />
);
wrapper.find('input').simulate('click');
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
expect(onPopupVisibleChange).toHaveBeenCalledWith(true);
});
it('support controlled mode', () => {
@ -64,8 +70,18 @@ describe('Cascader', () => {
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
});
it('should support popupVisible', () => {
const wrapper = mount(
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />
);
expect(wrapper.find('Trigger').instance().getComponent().props.visible).toBe(false);
wrapper.setProps({ popupVisible: true });
expect(wrapper.find('Trigger').instance().getComponent().props.visible).toBe(true);
});
it('can be selected', () => {
const wrapper = mount(<Cascader options={options} />);
const onChange = jest.fn();
const wrapper = mount(<Cascader options={options} onChange={onChange} />);
wrapper.find('input').simulate('click');
let popupWrapper = mount(wrapper.find('Trigger').instance().getComponent());
popupWrapper.find('.ant-cascader-menu').at(0).find('.ant-cascader-menu-item').at(0)
@ -79,6 +95,7 @@ describe('Cascader', () => {
popupWrapper.find('.ant-cascader-menu').at(2).find('.ant-cascader-menu-item').at(0)
.simulate('click');
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
expect(onChange).toHaveBeenCalledWith(['zhejiang', 'hangzhou', 'xihu'], expect.anything());
});
it('backspace should work with `Cascader[showSearch]`', () => {
@ -89,4 +106,88 @@ describe('Cascader', () => {
// Simulate onKeyDown will not trigger onChange by default, so the value is still '123'
expect(wrapper.state('inputValue')).toBe('123');
});
it('should highlight keyword and filter when search in Cascader', () => {
const wrapper = mount(<Cascader options={options} showSearch={{ filter }} />);
wrapper.find('input').simulate('click');
wrapper.find('input').simulate('change', { target: { value: 'z' } });
expect(wrapper.state('inputValue')).toBe('z');
const popupWrapper = mount(wrapper.find('Trigger').instance().getComponent());
expect(popupWrapper).toMatchSnapshot();
});
it('should render not found content', () => {
const wrapper = mount(<Cascader options={options} showSearch={{ filter }} />);
wrapper.find('input').simulate('click');
wrapper.find('input').simulate('change', { target: { value: '__notfoundkeyword__' } });
expect(wrapper.state('inputValue')).toBe('__notfoundkeyword__');
const popupWrapper = mount(wrapper.find('Trigger').instance().getComponent());
expect(popupWrapper).toMatchSnapshot();
});
it('should support to clear selection', () => {
const wrapper = mount(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />);
expect(wrapper.find('.ant-cascader-picker-label').text()).toBe('Zhejiang / Hangzhou');
wrapper.find('.ant-cascader-picker-clear').at(0).simulate('click');
expect(wrapper.find('.ant-cascader-picker-label').text()).toBe('');
});
it('should close popup when clear selection', () => {
const onPopupVisibleChange = jest.fn();
const wrapper = mount(
<Cascader
options={options}
popupVisible
defaultValue={['zhejiang', 'hangzhou']}
onPopupVisibleChange={onPopupVisibleChange}
/>
);
wrapper.find('.ant-cascader-picker-clear').at(0).simulate('click');
expect(onPopupVisibleChange).toHaveBeenCalledWith(false);
});
it('should clear search input when clear selection', () => {
const wrapper = mount(
<Cascader
options={options}
defaultValue={['zhejiang', 'hangzhou']}
showSearch
/>
);
wrapper.find('input').simulate('click');
wrapper.find('input').simulate('change', { target: { value: 'xxx' } });
expect(wrapper.state('inputValue')).toBe('xxx');
wrapper.find('.ant-cascader-picker-clear').at(0).simulate('click');
expect(wrapper.state('inputValue')).toBe('');
});
it('should not trigger visible change when click search input', () => {
const onPopupVisibleChange = jest.fn();
const wrapper = mount(
<Cascader
options={options}
showSearch
onPopupVisibleChange={onPopupVisibleChange}
/>
);
wrapper.find('input').simulate('focus');
expect(onPopupVisibleChange).toHaveBeenCalledTimes(0);
wrapper.find('input').simulate('click');
expect(onPopupVisibleChange).toHaveBeenCalledTimes(1);
wrapper.find('input').simulate('click');
expect(onPopupVisibleChange).toHaveBeenCalledTimes(1);
wrapper.find('input').simulate('blur');
wrapper.setState({ popupVisible: false });
wrapper.find('input').simulate('click');
expect(onPopupVisibleChange).toHaveBeenCalledTimes(2);
});
it('should change filtered item when options are changed', () => {
const wrapper = mount(<Cascader options={options} showSearch={{ filter }} />);
wrapper.find('input').simulate('click');
wrapper.find('input').simulate('change', { target: { value: 'a' } });
expect(wrapper.find('.ant-cascader-menu-item').length).toBe(2);
wrapper.setProps({ options: [options[0]] });
expect(wrapper.find('.ant-cascader-menu-item').length).toBe(1);
});
});

View File

@ -219,7 +219,9 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
// Prevent `Trigger` behaviour.
if (inputFocused || popupVisible) {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
if (e.nativeEvent.stopImmediatePropagation) {
e.nativeEvent.stopImmediatePropagation();
}
}
}

View File

@ -41,31 +41,31 @@ export default class Search extends React.Component<SearchProps, any> {
getButtonOrIcon() {
const { enterButton, prefixCls, size, disabled } = this.props;
if (!enterButton) {
return <Icon className={`${prefixCls}-icon`} type="search" key="searchIcon" />;
}
const enterButtonAsElement = enterButton as React.ReactElement<any>;
if (enterButtonAsElement.type === Button || enterButtonAsElement.type === 'button') {
return React.cloneElement(enterButtonAsElement, enterButtonAsElement.type === Button ? {
let node;
if (!enterButton) {
node = <Icon className={`${prefixCls}-icon`} type="search" key="searchIcon" />;
} else if (enterButtonAsElement.type === Button || enterButtonAsElement.type === 'button') {
node = React.cloneElement(enterButtonAsElement, enterButtonAsElement.type === Button ? {
className: `${prefixCls}-button`,
size,
onClick: this.onSearch,
} : {
onClick: this.onSearch,
});
} : {});
} else {
node = (
<Button
className={`${prefixCls}-button`}
type="primary"
size={size}
disabled={disabled}
key="enterButton"
>
{enterButton === true ? <Icon type="search" /> : enterButton}
</Button>
);
}
return (
<Button
className={`${prefixCls}-button`}
type="primary"
size={size}
disabled={disabled}
onClick={this.onSearch}
key="enterButton"
>
{enterButton === true ? <Icon type="search" /> : enterButton}
</Button>
);
return React.cloneElement(node, {
onClick: this.onSearch,
});
}
render() {

View File

@ -27,4 +27,64 @@ describe('Input.Search', () => {
);
expect(wrapper.find('.ant-btn-primary[disabled]')).toHaveLength(1);
});
it('should trigger onSearch when click search icon', () => {
const onSearch = jest.fn();
const wrapper = mount(
<Search defaultValue="search text" onSearch={onSearch} />
);
wrapper.find('Icon').simulate('click');
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toBeCalledWith('search text');
});
it('should trigger onSearch when click search button', () => {
const onSearch = jest.fn();
const wrapper = mount(
<Search defaultValue="search text" enterButton onSearch={onSearch} />
);
wrapper.find('Button').simulate('click');
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toBeCalledWith('search text');
});
it('should trigger onSearch when click search button with text', () => {
const onSearch = jest.fn();
const wrapper = mount(
<Search defaultValue="search text" enterButton="button text" onSearch={onSearch} />
);
wrapper.find('Button').simulate('click');
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toBeCalledWith('search text');
});
it('should trigger onSearch when click search button with customize button', () => {
const onSearch = jest.fn();
const wrapper = mount(
<Search defaultValue="search text" enterButton={<Button>antd button</Button>} onSearch={onSearch} />
);
wrapper.find('Button').simulate('click');
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toBeCalledWith('search text');
});
it('should trigger onSearch when click search button of native', () => {
const onSearch = jest.fn();
const wrapper = mount(
<Search defaultValue="search text" enterButton={<button>antd button</button>} onSearch={onSearch} />
);
wrapper.find('button').simulate('click');
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toBeCalledWith('search text');
});
it('should trigger onSearch when press enter', () => {
const onSearch = jest.fn();
const wrapper = mount(
<Search defaultValue="search text" onSearch={onSearch} />
);
wrapper.find('input').simulate('keydown', { key: 'Enter', keyCode: 13 });
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toBeCalledWith('search text');
});
});

View File

@ -34,6 +34,7 @@ exports[`Input.Search should support suffix 1`] = `
"suffix",
<Icon
className="ant-input-search-icon"
onClick={[Function]}
type="search"
/>,
]
@ -57,10 +58,12 @@ exports[`Input.Search should support suffix 1`] = `
<Icon
className="ant-input-search-icon"
key="searchIcon"
onClick={[Function]}
type="search"
>
<i
className="anticon anticon-search ant-input-search-icon"
onClick={[Function]}
/>
</Icon>
</span>

View File

@ -31,7 +31,12 @@ ReactDOM.render(
enterButton
/>
<br /><br />
<Search placeholder="input search text" enterButton="Search" size="large" />
<Search
placeholder="input search text"
enterButton="Search"
size="large"
onSearch={value => console.log(value)}
/>
</div>
, mountNode);
````

View File

@ -7,8 +7,12 @@
.@{search-prefix} {
&-icon {
pointer-events: none;
color: @text-color-secondary;
cursor: pointer;
transition: all .3s;
&:hover {
color: #333;
}
}
&:not(&-small) > .@{ant-prefix}-input-suffix {

View File

@ -83,4 +83,19 @@ describe('Modal.confirm triggers callbacks correctly', () => {
expect($$('.ant-btn')).toHaveLength(1);
expect($$('.ant-btn')[0].innerHTML).toContain('OK');
});
it('trigger onCancel once when click on cancel button', () => {
jest.useFakeTimers();
['info', 'success', 'warning', 'error'].forEach((type) => {
Modal[type]({
title: 'title',
content: 'content',
});
expect($$(`.ant-confirm-${type}`)).toHaveLength(1);
$$('.ant-btn')[0].click();
jest.runAllTimers();
expect($$(`.ant-confirm-${type}`)).toHaveLength(0);
});
jest.useRealTimers();
});
});

View File

@ -45,4 +45,41 @@ describe('Popconfirm', () => {
expect(popup.innerHTML).toMatchSnapshot();
expect(popup.innerHTML).toMatchSnapshot();
});
it('should be controlled by visible', () => {
jest.useFakeTimers();
const popconfirm = mount(
<Popconfirm title="code">
<span>show me your code</span>
</Popconfirm>
);
expect(popconfirm.instance().getPopupDomNode()).toBeFalsy();
popconfirm.setProps({ visible: true });
expect(popconfirm.instance().getPopupDomNode()).toBeTruthy();
expect(popconfirm.instance().getPopupDomNode().className).not.toContain('ant-popover-hidden');
popconfirm.setProps({ visible: false });
jest.runAllTimers();
expect(popconfirm.instance().getPopupDomNode().className).toContain('ant-popover-hidden');
jest.useRealTimers();
});
it('should trigger onConfirm and onCancel', () => {
const confirm = jest.fn();
const cancel = jest.fn();
const onVisibleChange = jest.fn();
const popconfirm = mount(
<Popconfirm title="code" onConfirm={confirm} onCancel={cancel} onVisibleChange={onVisibleChange}>
<span>show me your code</span>
</Popconfirm>
);
const triggerNode = popconfirm.find('span').at(0);
triggerNode.simulate('click');
popconfirm.find('.ant-btn-primary').simulate('click');
expect(confirm).toHaveBeenCalled();
expect(onVisibleChange).toHaveBeenLastCalledWith(false);
triggerNode.simulate('click');
popconfirm.find('.ant-btn').at(0).simulate('click');
expect(cancel).toHaveBeenCalled();
expect(onVisibleChange).toHaveBeenLastCalledWith(false);
});
});

View File

@ -2671,11 +2671,12 @@ exports[`renders ./components/table/demo/dynamic-settings.md correctly 1`] = `
exports[`renders ./components/table/demo/edit-cell.md correctly 1`] = `
<div>
<button
class="ant-btn editable-add-btn"
class="ant-btn ant-btn-primary"
style="margin-bottom:16px"
type="button"
>
<span>
Add
Add a row
</span>
</button>
<div
@ -2758,7 +2759,7 @@ exports[`renders ./components/table/demo/edit-cell.md correctly 1`] = `
class="editable-cell"
>
<div
class="editable-cell-text-wrapper"
style="padding-right:24px"
>
Edward King 0
<i
@ -2801,7 +2802,7 @@ exports[`renders ./components/table/demo/edit-cell.md correctly 1`] = `
class="editable-cell"
>
<div
class="editable-cell-text-wrapper"
style="padding-right:24px"
>
Edward King 1
<i

View File

@ -39,21 +39,21 @@ class EditableCell extends React.Component {
return (
<div className="editable-cell">
{
editable ?
<div className="editable-cell-input-wrapper">
<Input
value={value}
onChange={this.handleChange}
onPressEnter={this.check}
/>
<Icon
type="check"
className="editable-cell-icon-check"
onClick={this.check}
/>
</div>
:
<div className="editable-cell-text-wrapper">
editable ? (
<Input
value={value}
onChange={this.handleChange}
onPressEnter={this.check}
suffix={
<Icon
type="check"
className="editable-cell-icon-check"
onClick={this.check}
/>
}
/>
) : (
<div style={{ paddingRight: 24 }}>
{value || ' '}
<Icon
type="edit"
@ -61,6 +61,7 @@ class EditableCell extends React.Component {
onClick={this.edit}
/>
</div>
)
}
</div>
);
@ -148,7 +149,9 @@ class EditableTable extends React.Component {
const columns = this.columns;
return (
<div>
<Button className="editable-add-btn" onClick={this.handleAdd}>Add</Button>
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
Add a row
</Button>
<Table bordered dataSource={dataSource} columns={columns} />
</div>
);
@ -163,33 +166,21 @@ ReactDOM.render(<EditableTable />, mountNode);
position: relative;
}
.editable-cell-input-wrapper,
.editable-cell-text-wrapper {
padding-right: 24px;
}
.editable-cell-text-wrapper {
padding: 5px 24px 5px 5px;
}
.editable-cell-icon,
.editable-cell-icon-check {
position: absolute;
right: 0;
width: 20px;
cursor: pointer;
}
.editable-cell-icon {
line-height: 18px;
line-height: 14px;
position: absolute;
right: 0;
top: 50%;
margin-top: -7px;
display: none;
}
.editable-cell-icon-check {
line-height: 28px;
}
.editable-cell:hover .editable-cell-icon {
td:hover .editable-cell-icon {
display: inline-block;
}
@ -197,8 +188,4 @@ ReactDOM.render(<EditableTable />, mountNode);
.editable-cell-icon-check:hover {
color: #108ee9;
}
.editable-add-btn {
margin-bottom: 8px;
}
````

View File

@ -168,7 +168,7 @@
&-thead > tr > th,
&-tbody > tr > td {
padding: @table-padding-vertical @table-padding-horizontal;
word-break: break-all;
word-break: break-word;
}
&-thead > tr > th.@{table-prefix-cls}-selection-column-custom {

View File

@ -11,7 +11,7 @@ title:
## en-US
Tab can be slide to left or right(up or down), which is used for a lot of tabs.
In order to fit in more tabs, they can slide left and right (or up and down).
````jsx
import { Tabs, Radio } from 'antd';

View File

@ -1,9 +1,6 @@
import * as React from 'react';
import Button from '../button';
function noop() {
}
export interface TransferOperationProps {
className?: string;
leftArrowText?: string;
@ -17,8 +14,8 @@ export interface TransferOperationProps {
export default class Operation extends React.Component<TransferOperationProps, any> {
render() {
const {
moveToLeft = noop,
moveToRight = noop,
moveToLeft,
moveToRight,
leftArrowText = '',
rightArrowText = '',
leftActive,

View File

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "3.5.3",
"version": "3.5.5",
"title": "Ant Design",
"description": "An enterprise-class UI design language and React-based implementation",
"homepage": "http://ant.design/",
@ -82,7 +82,7 @@
"react-lazy-load": "^3.0.12",
"react-slick": "~0.23.1",
"shallowequal": "^1.0.1",
"warning": "~4.0.0"
"warning": "~3.0.0"
},
"devDependencies": {
"@babel/types": "7.0.0-beta.44",