mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-15 13:59:19 +08:00
Compare commits
182 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3769e3b96 | ||
|
|
f597c32aa6 | ||
|
|
4d6f8e96b9 | ||
|
|
ae6de361e2 | ||
|
|
1a9456f03a | ||
|
|
05c5115991 | ||
|
|
8e26b6823a | ||
|
|
5174801b95 | ||
|
|
ac0b4bacc5 | ||
|
|
e7cd474894 | ||
|
|
789b54905c | ||
|
|
71d2a991ed | ||
|
|
8610eb054f | ||
|
|
71d3af310a | ||
|
|
89228118af | ||
|
|
0ca1a990be | ||
|
|
74755fef99 | ||
|
|
396f81dbab | ||
|
|
a4a3b5e12d | ||
|
|
78adc05973 | ||
|
|
b524e1fe7e | ||
|
|
8444ad209d | ||
|
|
ad52250256 | ||
|
|
e1ba74e5fc | ||
|
|
6602df16e6 | ||
|
|
17b158c091 | ||
|
|
c2d94ad84d | ||
|
|
57a01e1b9f | ||
|
|
cdc571b17e | ||
|
|
d69610a17f | ||
|
|
e13c1a678d | ||
|
|
248dbc2429 | ||
|
|
37d5f895d2 | ||
|
|
70ca0281ed | ||
|
|
bbf5cb75c3 | ||
|
|
30eacdfb1b | ||
|
|
f8314a6b1e | ||
|
|
0ea890a0a4 | ||
|
|
0fdd13e5d6 | ||
|
|
15637c885b | ||
|
|
163ec46cbb | ||
|
|
2b5abd6dec | ||
|
|
e25323882a | ||
|
|
e124cabc5b | ||
|
|
7149d1fdff | ||
|
|
5be1b9c33c | ||
|
|
7c273912fd | ||
|
|
87163dfcd6 | ||
|
|
05251682f1 | ||
|
|
31be3717ad | ||
|
|
ca7ed4c07d | ||
|
|
64a29dfd1a | ||
|
|
b5c5f0b5d4 | ||
|
|
4f91fe61bf | ||
|
|
7f16ded760 | ||
|
|
9bc98b6471 | ||
|
|
e8351b8104 | ||
|
|
6d610de157 | ||
|
|
be7f540fba | ||
|
|
814ee7365a | ||
|
|
5fd4139661 | ||
|
|
e14928b4f9 | ||
|
|
19a04e52f4 | ||
|
|
21ca50fca1 | ||
|
|
38f94990f8 | ||
|
|
d18f100721 | ||
|
|
b10e7c4e82 | ||
|
|
9552f80cde | ||
|
|
ad0108dfd5 | ||
|
|
a6e2037c06 | ||
|
|
ac4a9086a0 | ||
|
|
12d0aedeca | ||
|
|
36ffe7e1dc | ||
|
|
b86f4eeaad | ||
|
|
3ceb619b92 | ||
|
|
baa78bfa5f | ||
|
|
8c2f99fd3e | ||
|
|
50057739bf | ||
|
|
3a445f3e75 | ||
|
|
be13326a1b | ||
|
|
93b14da4a6 | ||
|
|
21db29bd53 | ||
|
|
d58c0780f9 | ||
|
|
042c014a38 | ||
|
|
aff9d8fc62 | ||
|
|
b7b12b657c | ||
|
|
0feaf95629 | ||
|
|
076540c83e | ||
|
|
3db0610d52 | ||
|
|
7222944347 | ||
|
|
322ebf0039 | ||
|
|
c2519e776b | ||
|
|
33b604f7a8 | ||
|
|
4c101275a5 | ||
|
|
4c1b2369d7 | ||
|
|
cbff10db15 | ||
|
|
b7cf33d1eb | ||
|
|
b828a599fb | ||
|
|
f3869270ae | ||
|
|
2989e8dba0 | ||
|
|
6d8df2ac25 | ||
|
|
fd96b4a784 | ||
|
|
af8f4dc5c3 | ||
|
|
8da79f4b34 | ||
|
|
247d78990e | ||
|
|
acb56281f8 | ||
|
|
fafd83cadc | ||
|
|
7e96ae1a6a | ||
|
|
3c381c6dd1 | ||
|
|
292a91a777 | ||
|
|
89ed0dcb35 | ||
|
|
77301c378f | ||
|
|
22d2f9eb01 | ||
|
|
3078819535 | ||
|
|
6471e5d4f0 | ||
|
|
1e585135aa | ||
|
|
6057821f39 | ||
|
|
33c58a8ef6 | ||
|
|
b09e03d82a | ||
|
|
a2e0e40caa | ||
|
|
f409df8d84 | ||
|
|
a148bc7b79 | ||
|
|
4077ffb58c | ||
|
|
bb4bc61556 | ||
|
|
0fc5a0ece5 | ||
|
|
6458280cb3 | ||
|
|
0f73b5d3f7 | ||
|
|
14beee1fe1 | ||
|
|
e0312f65ee | ||
|
|
389c7c4f10 | ||
|
|
59fe98a194 | ||
|
|
2955600642 | ||
|
|
d4ed303bcd | ||
|
|
ff458a2c3f | ||
|
|
06ffd9ca04 | ||
|
|
251d882101 | ||
|
|
129baddc44 | ||
|
|
968488a2fa | ||
|
|
00d710cef0 | ||
|
|
ddc242a9cb | ||
|
|
d6a554823f | ||
|
|
fd1d9661c0 | ||
|
|
07b24f4edd | ||
|
|
dbd2bd1866 | ||
|
|
8b420c41bb | ||
|
|
96d53aefb8 | ||
|
|
8e2f72ffe0 | ||
|
|
29229ebee7 | ||
|
|
e1ad91119a | ||
|
|
76bf36a79b | ||
|
|
aa87fcb01e | ||
|
|
1ed9fed2cf | ||
|
|
7c4f6e8b2a | ||
|
|
df7ef9a833 | ||
|
|
9b17a943f5 | ||
|
|
7404b7f36c | ||
|
|
963120f702 | ||
|
|
b7d508e166 | ||
|
|
cafeb8e3eb | ||
|
|
99dca86904 | ||
|
|
09d5e36cfa | ||
|
|
6e0865f691 | ||
|
|
eb17aae25c | ||
|
|
593435ce65 | ||
|
|
c4f58e7027 | ||
|
|
b76910f63f | ||
|
|
12d3046687 | ||
|
|
d5d0501ac7 | ||
|
|
c7c85e3d5d | ||
|
|
78af67a057 | ||
|
|
604584931e | ||
|
|
77e21d33ea | ||
|
|
bdb4483ef5 | ||
|
|
97c2d58d15 | ||
|
|
f09c799bba | ||
|
|
bc8353729c | ||
|
|
bf03d954d4 | ||
|
|
413479a5b9 | ||
|
|
5e0b8df412 | ||
|
|
3413774ae5 | ||
|
|
f3545ccdc7 | ||
|
|
6c0d6d302a |
@@ -15,13 +15,71 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 3.6.2
|
||||
|
||||
`2018-06-09`
|
||||
|
||||
- 🐞 Fix the wrong status of check all checkbox in Table when data change. [#10629](https://github.com/ant-design/ant-design/issues/10629)
|
||||
- 🐞 Fix border style of Button.Group.
|
||||
- 🐞 Fix file list being reversed when `beforeUpload` returns `false` in Upload component. [#10681](https://github.com/ant-design/ant-design/issues/10681)
|
||||
- 🐞 Fix overflow of cell content in Calendar. [#10808](https://github.com/ant-design/ant-design/pull/10808) [@Yangzhedi](https://github.com/ant-design/Yangzhedi)
|
||||
- 🐞 Fix the processing color of Badge not follows the `primary-color`.
|
||||
- Spin
|
||||
- 🐞 Fix custom icon does not follow the value of `size`. [#10786](https://github.com/ant-design/ant-design/issues/10786)
|
||||
- 🐞 Fix no `delay` issue when sets `spinning` to `true` as default.[#10727](https://github.com/ant-design/ant-design/pull/10727) [@dreamerblue](https://github.com/dreamerblue)
|
||||
- TypeScript
|
||||
- 🐞 Fix Menu type definition. [#10773](https://github.com/ant-design/ant-design/issues/10773)
|
||||
- 🐞 Fix AutoComplete type definition. [#10745](https://github.com/ant-design/ant-design/issues/10745) [#10619](https://github.com/ant-design/ant-design/issues/10619)
|
||||
- 🐞 Fix Tree type definition. [#10841](https://github.com/ant-design/ant-design/pull/10841) [@Voronar](https://github.com/Voronar)
|
||||
- 🐞 Fix Checkbox.Group definition. [#10677](https://github.com/ant-design/ant-design/pull/10677)
|
||||
|
||||
## 3.6.0
|
||||
|
||||
`2018-06-02`
|
||||
|
||||
* 🌟 `Form`'s error message now can be customized with ReactNode. [#10136](https://github.com/ant-design/ant-design/issues/10136) [@lovekonakona](https://github.com/lovekonakona)
|
||||
* 🌟 `List` support customized configuration for position of pagination. [#10581](https://github.com/ant-design/ant-design/pull/10581) [@zheeeng](https://github.com/zheeeng)
|
||||
* 🌟 `Layout.Sider` now can be configured with `light` or `dark` theme. [#10142](https://github.com/ant-design/ant-design/issues/10142) [@pd4d10](https://github.com/pd4d10)
|
||||
* 🌟 Ant Design official site now supports offline mode. [#10625](https://github.com/ant-design/ant-design/issues/10625)
|
||||
* 🌟 `Transfer` adding `style` and `operationStyle` props for further customization. [@eduludi](https://github.com/eduludi)
|
||||
* 🌟 `Message` adds a promisfied interface to handle callback. [#10421](https://github.com/ant-design/ant-design/issues/10421) [@zhujinxuan](https://github.com/zhujinxuan)
|
||||
* 🐞 Fix compilation issue with typescript@v2.9.1 . [#10729](https://github.com/ant-design/ant-design/issues/10729) [@karol-majewski](https://github.com/karol-majewski)
|
||||
* 🐞 Fix a bug in `Menu` where outer menu item not highlighting while inner item is selected. [#8666](https://github.com/ant-design/ant-design/issues/8666) [@stonehank](https://github.com/stonehank)
|
||||
* 🐞 Affix `offsetBottom` not working. [#10674](https://github.com/ant-design/ant-design/issues/10674)
|
||||
|
||||
## 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`
|
||||
|
||||
- 🐞 Fix `Affix` with offsetTop === 0, value becomes `undefined` problem [#10566](https://github.com/ant-design/ant-design/pull/10566)
|
||||
- 🐞 Fixed issue with shaded `Input.Group` component shadows[#10230](https://github.com/ant-design/ant-design/issues/10230)
|
||||
- 🐞 Fixed issue where the `Transfer` component checkbox event fired twice [#10480](https://github.com/ant-design/ant-design/issues/10480)
|
||||
- 💄 Unifying the name of the variable [12d3046](https://github.com/ant-design/ant-design/commit/12d3046687a0dcdb51fece08dd2bea64f185cc40)
|
||||
- 💄 Fine-tuned the style of `Dropdown` [8e2f72f](https://github.com/ant-design/ant-design/commit/8e2f72ffe0eb300f5997296726b02246bf990c8f)
|
||||
- 💄 The components of the Chinese document will now be demonstrated using Chinese language packages. [9b17a94](https://github.com/ant-design/ant-design/commit/9b17a943f5d57d40d65041b7b0c247add09d2851)
|
||||
- 💄 The main theme switch was changed to `antd-theme-generato`, thanks to the work of [@mzohaibqc](https://github.com/mzohaibqc).
|
||||
- TypeScript
|
||||
- 🐞 Fixed an issue with the type of gutter attribute in `Row` [b7d508e](https://github.com/ant-design/ant-design/commit/b7d508e1662bf20a0cacbe6440a2ce31a65a8a59)
|
||||
- 💄 Improved the `Form` component type [#10564](https://github.com/ant-design/ant-design/pull/10564)
|
||||
- 💄 Improved the `Button` and `Tag` types[1ed9fed](https://github.com/ant-design/ant-design/commit/1ed9fed2cf1c99b947359fafb101b2e58213cb48)
|
||||
|
||||
## 3.5.2
|
||||
|
||||
`2018-05-13`
|
||||
|
||||
- 🐞 Fixed `Table` filter doesn't work when click checkbox. [#10452](https://github.com/ant-design/ant-design/issues/10452)
|
||||
- 🐞 Fixed `Cascader` displayRender z-index issue. [#10433](https://github.com/ant-design/ant-design/issues/10433)
|
||||
- 🐞 Fixed `Button` Types of property 'ref' are incompatible. [#10405](https://github.com/ant-design/ant-design/issues/10405)
|
||||
- 🐞 Fixed `Table` filter doesn't work on checkbox click. [#10452](https://github.com/ant-design/ant-design/issues/10452)
|
||||
- 🐞 Fixed the height of `Form` item when checks position is dithered. [#10445](https://github.com/ant-design/ant-design/issues/10445)
|
||||
- 🌟 Allow to use any CSS units for `Layout` width. [#10479](https://github.com/ant-design/ant-design/pull/10479)
|
||||
|
||||
@@ -441,7 +499,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
|
||||
|
||||
|
||||
@@ -15,13 +15,72 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 3.6.2
|
||||
|
||||
`2018-06-09`
|
||||
|
||||
- 🐞 修复 Table 数据变化时全选勾选框状态显示不正确的问题。[#10629](https://github.com/ant-design/ant-design/issues/10629)
|
||||
- 🐞 修复 Button.Group 中使用 disabled 按钮时缺失边框。
|
||||
- 🐞 修复 Upload 中 `beforeUpload` 返回 `false` 时,文件列表排序会被反转的问题。[#10681](https://github.com/ant-design/ant-design/issues/10681)
|
||||
- 🐞 修复 Calendar 表格中内容溢出的问题。[#10808](https://github.com/ant-design/ant-design/pull/10808) [@Yangzhedi](https://github.com/ant-design/Yangzhedi)
|
||||
- Spin
|
||||
- 🐞 修复使用图标时不能按照 `size` 正确显示大小的问题。[#10786](https://github.com/ant-design/ant-design/issues/10786)
|
||||
- 🐞 修复默认旋转时 `delay` 不生效的问题。[#10727](https://github.com/ant-design/ant-design/pull/10727) [@dreamerblue](https://github.com/dreamerblue)
|
||||
- 修复 Badge 的状态色不跟主题色变化的问题。
|
||||
- TypeScript
|
||||
- 🐞 修复 Menu 类型定义。[#10773](https://github.com/ant-design/ant-design/issues/10773)
|
||||
- 🐞 修复 AutoComplete 类型定义。[#10745](https://github.com/ant-design/ant-design/issues/10745) [#10619](https://github.com/ant-design/ant-design/issues/10619)
|
||||
- 🐞 修复 Tree 类型定义。[#10841](https://github.com/ant-design/ant-design/pull/10841) [@Voronar](https://github.com/Voronar)
|
||||
- 🐞 修复 Checkbox.Group 的类型定义。[#10677](https://github.com/ant-design/ant-design/pull/10677)
|
||||
|
||||
## 3.6.0
|
||||
|
||||
`2018-06-02`
|
||||
|
||||
* 🌟 `Form` 表单错误信息展示支持传入 ReactNode。 [#10136](https://github.com/ant-design/ant-design/issues/10136) [@lovekonakona](https://github.com/lovekonakona)
|
||||
* 🌟 `List` 组件支持指定分页器的位置。 [#10581](https://github.com/ant-design/ant-design/pull/10581) [@zheeeng](https://github.com/zheeeng)
|
||||
* 🌟 `Layout.Sider` 支持 dark/light 主题定制。 [#10142](https://github.com/ant-design/ant-design/issues/10142) [@pd4d10](https://github.com/pd4d10)
|
||||
* 🌟 支持 Ant Design 站点的离线模式。 [#10625](https://github.com/ant-design/ant-design/issues/10625)
|
||||
* 🌟 `Transfer` 新增 `style` 以及 `operationStyle` 属性配置样式。 [@eduludi](https://github.com/eduludi)
|
||||
* 🌟 `Message` 增加 promise 化的回调接口。 [#10421](https://github.com/ant-design/ant-design/issues/10421) [@zhujinxuan](https://github.com/zhujinxuan)
|
||||
* 🐞 修复编译时 typescript v2.9.1兼容性问题。 [#10729](https://github.com/ant-design/ant-design/issues/10729) [@karol-majewski](https://github.com/karol-majewski)
|
||||
* 🐞 修复 `Menu` 嵌套超过两层时选中最里层后对应最外层没有亮起问题。 [#8666](https://github.com/ant-design/ant-design/issues/8666) [@stonehank](https://github.com/stonehank)
|
||||
* 🐞 修复 `Affix` 组件 offsetBottom 无效问题。 [#10674](https://github.com/ant-design/ant-design/issues/10674)
|
||||
|
||||
## 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)
|
||||
- 🐞 修复了 `Transfer` 组件 checkbox 事件触发两次的问题 [`#10480`](https://github.com/ant-design/ant-design/issues/10480)
|
||||
- 💄 统一 less 的变量命名 [12d3046](https://github.com/ant-design/ant-design/commit/12d3046687a0dcdb51fece08dd2bea64f185cc40)
|
||||
- 💄 微调了 `Dropdown` 的样式 [8e2f72f](https://github.com/ant-design/ant-design/commit/8e2f72ffe0eb300f5997296726b02246bf990c8f)
|
||||
- 💄 现在中文文档的组件会用中文语言包进行演示。[9b17a94](https://github.com/ant-design/ant-design/commit/9b17a943f5d57d40d65041b7b0c247add09d2851)
|
||||
- 💄 主站主题切换 修改为 `antd-theme-generato` ,感谢 [@mzohaibqc](https://github.com/mzohaibqc) 的工作.
|
||||
- TypeScript
|
||||
- 🐞 修复了 `Row` 中 gutter 属性的类型问题 [b7d508e](https://github.com/ant-design/ant-design/commit/b7d508e1662bf20a0cacbe6440a2ce31a65a8a59)
|
||||
- 💄 改进了 `Form` 组件类型 [#10564](https://github.com/ant-design/ant-design/pull/10564)
|
||||
- 💄 改进了 `Button` 和 `Tag` 类型 [1ed9fed](https://github.com/ant-design/ant-design/commit/1ed9fed2cf1c99b947359fafb101b2e58213cb48)
|
||||
|
||||
## 3.5.2
|
||||
|
||||
`2018-05-13`
|
||||
|
||||
- 🐞 修复 `Cascader` 的 displayRender 问题。[#10433](https://github.com/ant-design/ant-design/issues/10433)
|
||||
- 🐞 修复 `Table` 过滤器和 `Transfer` 复选框无法点击的问题。[#10452](https://github.com/ant-design/ant-design/issues/10452)
|
||||
- 🐞 修复 `Cascader` 的 `displayRender` 里的链接无法点击的问题。[#10433](https://github.com/ant-design/ant-design/issues/10433)
|
||||
- 🐞 修复 `Button` ref 不兼容问题。[#10405](https://github.com/ant-design/ant-design/issues/10405)
|
||||
- 🐞 修复 `Table` 过滤器在复选框上不起作用。[#10452](https://github.com/ant-design/ant-design/issues/10452)
|
||||
- 🐞 修复 `Form` 表单项校验位置高度时出现抖动问题。[#10445](https://github.com/ant-design/ant-design/issues/10445)
|
||||
- 🌟 设置 `Layout` 宽度时,允许使用任何的 CSS 单位。 [#10479](https://github.com/ant-design/ant-design/pull/10479)
|
||||
|
||||
@@ -442,11 +501,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`。
|
||||
- 默认语言由中文改为英文。
|
||||
@@ -458,6 +517,8 @@ timeline: true
|
||||
|
||||
### 不兼容改动
|
||||
|
||||
> 如果你从 2.x 升级到 3.x,建议直接升级到 3.x 的最新版本。
|
||||
|
||||
此版本有部分不兼容的改动,升级时确保修改相应的使用代码。另外由于人肉查找代码中的废弃用法过于低效,所以我们提供了 [antd-migration-helper](https://github.com/ant-design/antd-migration-helper) 用于扫描代码中的废弃用法。
|
||||
|
||||
- Card 的 `noHovering` 属性重命名为 `hoverable`,且默认值改为 `true`。
|
||||
@@ -504,7 +565,7 @@ timeline: true
|
||||
|
||||
- Form 下的表单控件不再默认为 `size="large"`。
|
||||
- `Input.Search` 默认的 🔍 图标只作为装饰,不再响应用户交互。需要添加可交互按钮请使用 `enterButton`。
|
||||
- UMD 版本的 `dist/antd.js` 不再包含 moment,使用的时候需要自己引入 moment。
|
||||
- UMD 版本的 `dist/antd.js` 不再包含 moment,使用的时候需要自己引入 moment。
|
||||
```diff
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
|
||||
[](https://travis-ci.org/ant-design/ant-design)
|
||||
[](https://codecov.io/gh/ant-design/ant-design/branch/master)
|
||||
[](https://gemnasium.com/ant-design/ant-design)
|
||||
[](https://david-dm.org/ant-design/ant-design)
|
||||
[](https://david-dm.org/ant-design/ant-design#info=devDependencies&view=list)
|
||||
|
||||
[](https://www.npmjs.org/package/antd)
|
||||
[](http://www.npmtrends.com/antd)
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
|
||||
[](https://travis-ci.org/ant-design/ant-design)
|
||||
[](https://codecov.io/gh/ant-design/ant-design/branch/master)
|
||||
[](https://gemnasium.com/ant-design/ant-design)
|
||||
[](https://david-dm.org/ant-design/ant-design)
|
||||
[](https://david-dm.org/ant-design/ant-design#info=devDependencies&view=list)
|
||||
|
||||
[](https://www.npmjs.org/package/antd)
|
||||
[](http://www.npmtrends.com/antd)
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
const OLD_NODE_ENV = process.env.NODE_ENV;
|
||||
process.env.NODE_ENV = 'development';
|
||||
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const antd = require('..');
|
||||
|
||||
describe('antd', () => {
|
||||
afterAll(() => {
|
||||
process.env.NODE_ENV = OLD_NODE_ENV;
|
||||
});
|
||||
|
||||
it('exports modules correctly', () => {
|
||||
expect(Object.keys(antd)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should hint when import all components', () => {
|
||||
it('should hint when import all components in dev mode', () => {
|
||||
expect(warnSpy).toBeCalledWith(
|
||||
'You are using a whole package of antd, please use https://www.npmjs.com/package/babel-plugin-import to reduce app bundle size.'
|
||||
);
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
const availablePrefixs = ['moz', 'ms', 'webkit'];
|
||||
|
||||
function requestAnimationFramePolyfill() {
|
||||
let lastTime = 0;
|
||||
return function(callback: (n: number) => void) {
|
||||
const currTime = new Date().getTime();
|
||||
const timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
||||
const id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
|
||||
lastTime = currTime + timeToCall;
|
||||
return id;
|
||||
};
|
||||
}
|
||||
|
||||
export default function getRequestAnimationFrame() {
|
||||
if (typeof window === 'undefined') {
|
||||
return () => {};
|
||||
}
|
||||
if (window.requestAnimationFrame) {
|
||||
// https://github.com/vuejs/vue/issues/4465
|
||||
return window.requestAnimationFrame.bind(window);
|
||||
}
|
||||
|
||||
const prefix = availablePrefixs.filter(key => `${key}RequestAnimationFrame` in window)[0];
|
||||
|
||||
return prefix
|
||||
? (window as any)[`${prefix}RequestAnimationFrame`]
|
||||
: requestAnimationFramePolyfill();
|
||||
}
|
||||
|
||||
export function cancelRequestAnimationFrame(id: number) {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
if (window.cancelAnimationFrame) {
|
||||
return window.cancelAnimationFrame(id);
|
||||
}
|
||||
const prefix = availablePrefixs.filter(key =>
|
||||
`${key}CancelAnimationFrame` in window || `${key}CancelRequestAnimationFrame` in window,
|
||||
)[0];
|
||||
|
||||
return prefix ?
|
||||
(
|
||||
(window as any)[`${prefix}CancelAnimationFrame`] ||
|
||||
(window as any)[`${prefix}CancelRequestAnimationFrame`]
|
||||
).call(this, id) : clearTimeout(id);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
let animation: boolean;
|
||||
|
||||
function isCssAnimationSupported() {
|
||||
if (animation !== undefined) {
|
||||
return animation;
|
||||
}
|
||||
const domPrefixes = 'Webkit Moz O ms Khtml'.split(' ');
|
||||
const elm = document.createElement('div');
|
||||
if (elm.style.animationName !== undefined) {
|
||||
animation = true;
|
||||
}
|
||||
if (animation !== undefined) {
|
||||
for (let i = 0; i < domPrefixes.length; i++) {
|
||||
if ((elm.style as any)[`${domPrefixes[i]}AnimationName`] !== undefined) {
|
||||
animation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
animation = animation || false;
|
||||
return animation;
|
||||
}
|
||||
|
||||
export default isCssAnimationSupported;
|
||||
@@ -1,7 +1,5 @@
|
||||
import cssAnimation from 'css-animation';
|
||||
import getRequestAnimationFrame, { cancelRequestAnimationFrame } from './getRequestAnimationFrame';
|
||||
|
||||
const reqAnimFrame = getRequestAnimationFrame();
|
||||
import raf from 'raf';
|
||||
|
||||
function animate(node: HTMLElement, show: boolean, done: () => void) {
|
||||
let height: number;
|
||||
@@ -19,16 +17,16 @@ function animate(node: HTMLElement, show: boolean, done: () => void) {
|
||||
},
|
||||
active() {
|
||||
if (requestAnimationFrameId) {
|
||||
cancelRequestAnimationFrame(requestAnimationFrameId);
|
||||
raf.cancel(requestAnimationFrameId);
|
||||
}
|
||||
requestAnimationFrameId = reqAnimFrame(() => {
|
||||
requestAnimationFrameId = raf(() => {
|
||||
node.style.height = `${show ? height : 0}px`;
|
||||
node.style.opacity = show ? '1' : '0';
|
||||
});
|
||||
},
|
||||
end() {
|
||||
if (requestAnimationFrameId) {
|
||||
cancelRequestAnimationFrame(requestAnimationFrameId);
|
||||
raf.cancel(requestAnimationFrameId);
|
||||
}
|
||||
node.style.height = '';
|
||||
node.style.opacity = '';
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import getRequestAnimationFrame, { cancelRequestAnimationFrame } from '../_util/getRequestAnimationFrame';
|
||||
|
||||
const reqAnimFrame = getRequestAnimationFrame();
|
||||
import raf from 'raf';
|
||||
|
||||
export default function throttleByAnimationFrame(fn: (...args: any[]) => void) {
|
||||
let requestId: number | null;
|
||||
@@ -12,11 +10,11 @@ export default function throttleByAnimationFrame(fn: (...args: any[]) => void) {
|
||||
|
||||
const throttled = (...args: any[]) => {
|
||||
if (requestId == null) {
|
||||
requestId = reqAnimFrame(later(args));
|
||||
requestId = raf(later(args));
|
||||
}
|
||||
};
|
||||
|
||||
(throttled as any).cancel = () => cancelRequestAnimationFrame(requestId!);
|
||||
(throttled as any).cancel = () => raf.cancel(requestId!);
|
||||
|
||||
return throttled;
|
||||
}
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
export type Diff<T extends string, U extends string> = ({ [P in T]: P } &
|
||||
{ [P in U]: never } & { [x: string]: never })[T];
|
||||
export type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
|
||||
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
||||
|
||||
@@ -7,7 +7,6 @@ const events = {};
|
||||
|
||||
class AffixMounter extends React.Component {
|
||||
componentDidMount() {
|
||||
this.container.scrollTop = 100;
|
||||
this.container.addEventListener = jest.fn().mockImplementation((event, cb) => {
|
||||
events[event] = cb;
|
||||
});
|
||||
@@ -34,6 +33,7 @@ class AffixMounter extends React.Component {
|
||||
<Affix
|
||||
target={() => this.container}
|
||||
ref={ele => this.affix = ele}
|
||||
{...this.props}
|
||||
>
|
||||
<Button type="primary" >
|
||||
Fixed at the top of container
|
||||
@@ -46,6 +46,8 @@ class AffixMounter extends React.Component {
|
||||
}
|
||||
|
||||
describe('Affix Render', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
@@ -54,23 +56,48 @@ describe('Affix Render', () => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('Anchor render perfectly', () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
const wrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
|
||||
jest.runAllTimers();
|
||||
|
||||
const scrollTo = (top) => {
|
||||
wrapper.instance().affix.fixedNode.parentNode.getBoundingClientRect = jest.fn(() => {
|
||||
return {
|
||||
bottom: 100, height: 28, left: 0, right: 0, top: -50, width: 195,
|
||||
bottom: 100, height: 28, left: 0, right: 0, top: 50 - top, width: 195,
|
||||
};
|
||||
});
|
||||
|
||||
wrapper.instance().container.scrollTop = top;
|
||||
events.scroll({
|
||||
type: 'scroll',
|
||||
});
|
||||
|
||||
jest.runAllTimers();
|
||||
};
|
||||
|
||||
it('Anchor render perfectly', () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
wrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
|
||||
jest.runAllTimers();
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBe(null);
|
||||
|
||||
scrollTo(100);
|
||||
expect(wrapper.instance().affix.state.affixStyle).not.toBe(null);
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBe(null);
|
||||
});
|
||||
|
||||
it('support offsetBottom', () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
wrapper = mount(<AffixMounter offsetBottom={0} />, { attachTo: document.getElementById('mounter') });
|
||||
jest.runAllTimers();
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).not.toBe(null);
|
||||
|
||||
scrollTo(100);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBe(null);
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).not.toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,8 +38,7 @@ function getOffset(element: HTMLElement, target: HTMLElement | Window | null) {
|
||||
function noop() {}
|
||||
|
||||
function getDefaultTarget() {
|
||||
return typeof window !== 'undefined' ?
|
||||
window : null;
|
||||
return typeof window !== 'undefined' ? window : null;
|
||||
}
|
||||
|
||||
// Affix
|
||||
@@ -145,7 +144,9 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
|
||||
const targetNode = target();
|
||||
|
||||
// Backwards support
|
||||
offsetTop = offsetTop || offset;
|
||||
// Fix: if offsetTop === 0, it will get undefined,
|
||||
// if offsetBottom is type of number, offsetMode will be { top: false, ... }
|
||||
offsetTop = typeof offsetTop === 'undefined' ? offset : offsetTop;
|
||||
const scrollTop = getScroll(targetNode, true);
|
||||
const affixNode = ReactDOM.findDOMNode(this) as HTMLElement;
|
||||
const elemOffset = getOffset(affixNode, targetNode);
|
||||
|
||||
31
components/alert/__tests__/index.test.js
Normal file
31
components/alert/__tests__/index.test.js
Normal 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();
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,7 @@ title: Alert
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| afterClose | 关闭动画结束后的回掉 | () => void | - |
|
||||
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - |
|
||||
| banner | 是否用作顶部公告 | boolean | false |
|
||||
| closable | 默认不显示关闭按钮 | boolean | 无 |
|
||||
| closeText | 自定义关闭按钮 | string\|ReactNode | 无 |
|
||||
|
||||
@@ -6,7 +6,7 @@ import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import Affix from '../affix';
|
||||
import AnchorLink from './AnchorLink';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import getRequestAnimationFrame from '../_util/getRequestAnimationFrame';
|
||||
import raf from 'raf';
|
||||
|
||||
function getDefaultContainer() {
|
||||
return window;
|
||||
@@ -43,7 +43,6 @@ function easeInOutCubic(t: number, b: number, c: number, d: number) {
|
||||
return cc / 2 * ((t -= 2) * t * t + 2) + b;
|
||||
}
|
||||
|
||||
const reqAnimFrame = getRequestAnimationFrame();
|
||||
const sharpMatcherRegx = /#([^#]+)$/;
|
||||
function scrollTo(href: string, offsetTop = 0, getContainer: () => AnchorContainer, callback = () => { }) {
|
||||
const container = getContainer();
|
||||
@@ -67,12 +66,12 @@ function scrollTo(href: string, offsetTop = 0, getContainer: () => AnchorContain
|
||||
(container as HTMLElement).scrollTop = nextScrollTop;
|
||||
}
|
||||
if (time < 450) {
|
||||
reqAnimFrame(frameFunc);
|
||||
raf(frameFunc);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
reqAnimFrame(frameFunc);
|
||||
raf(frameFunc);
|
||||
history.pushState(null, '', href);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('Anchor Render', () => {
|
||||
expect(wrapper.instance().state.activeLink).toBe('http://www.example.com/#API');
|
||||
});
|
||||
|
||||
it('Anchor render perfectly for complete href - scoll', () => {
|
||||
it('Anchor render perfectly for complete href - scroll', () => {
|
||||
let root = document.getElementById('root');
|
||||
if (!root) {
|
||||
root = document.createElement('div', { id: 'root' });
|
||||
@@ -45,7 +45,8 @@ describe('Anchor Render', () => {
|
||||
expect(wrapper.instance().state.activeLink).toBe('http://www.example.com/#API');
|
||||
});
|
||||
|
||||
it('Anchor render perfectly for complete href - scollTo', () => {
|
||||
it('Anchor render perfectly for complete href - scrollTo', async () => {
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
let root = document.getElementById('root');
|
||||
if (!root) {
|
||||
root = document.createElement('div', { id: 'root' });
|
||||
@@ -60,5 +61,33 @@ describe('Anchor Render', () => {
|
||||
);
|
||||
wrapper.instance().handleScrollTo('##API');
|
||||
expect(wrapper.instance().state.activeLink).toBe('##API');
|
||||
expect(scrollToSpy).not.toHaveBeenCalled();
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
expect(scrollToSpy).toHaveBeenCalled();
|
||||
expect(wrapper.instance().animating).toBe(true);
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
expect(wrapper.instance().animating).toBe(false);
|
||||
});
|
||||
|
||||
it('should remove listener when unmount', async () => {
|
||||
const wrapper = mount(
|
||||
<Anchor>
|
||||
<Link href="#API" title="API" />
|
||||
</Anchor>
|
||||
);
|
||||
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
|
||||
wrapper.unmount();
|
||||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should unregister link when unmount children', async () => {
|
||||
const wrapper = mount(
|
||||
<Anchor>
|
||||
<Link href="#API" title="API" />
|
||||
</Anchor>
|
||||
);
|
||||
expect(wrapper.instance().links).toEqual(['#API']);
|
||||
wrapper.setProps({ children: null });
|
||||
expect(wrapper.instance().links).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,11 @@ import Input from '../input';
|
||||
import InputElement from './InputElement';
|
||||
|
||||
export interface DataSourceItemObject { value: string; text: string; }
|
||||
export type DataSourceItemType = string | DataSourceItemObject;
|
||||
export type DataSourceItemType =
|
||||
string |
|
||||
DataSourceItemObject |
|
||||
React.ReactElement<OptionProps> |
|
||||
React.ReactElement<OptGroupProps>;
|
||||
|
||||
export interface AutoCompleteInputProps {
|
||||
onChange?: React.FormEventHandler<any>;
|
||||
|
||||
@@ -75,7 +75,7 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls, shape, size, src, icon, className, ...others,
|
||||
prefixCls, shape, size, src, icon, className, ...others
|
||||
} = this.props;
|
||||
|
||||
const sizeCls = classNames({
|
||||
|
||||
@@ -4,9 +4,7 @@ import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import getRequestAnimationFrame from '../_util/getRequestAnimationFrame';
|
||||
|
||||
const reqAnimFrame = getRequestAnimationFrame();
|
||||
import raf from 'raf';
|
||||
|
||||
const easeInOutCubic = (t: number, b: number, c: number, d: number) => {
|
||||
const cc = c - b;
|
||||
@@ -64,10 +62,10 @@ export default class BackTop extends React.Component<BackTopProps, any> {
|
||||
const time = timestamp - startTime;
|
||||
this.setScrollTop(easeInOutCubic(time, scrollTop, 0, 450));
|
||||
if (time < 450) {
|
||||
reqAnimFrame(frameFunc);
|
||||
raf(frameFunc);
|
||||
}
|
||||
};
|
||||
reqAnimFrame(frameFunc);
|
||||
raf(frameFunc);
|
||||
(this.props.onClick || noop)(e);
|
||||
}
|
||||
|
||||
|
||||
2187
components/badge/__tests__/__snapshots__/index.test.js.snap
Normal file
2187
components/badge/__tests__/__snapshots__/index.test.js.snap
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,23 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { mount, render } from 'enzyme';
|
||||
import Badge from '../index';
|
||||
import Tooltip from '../../tooltip';
|
||||
|
||||
describe('Badge', () => {
|
||||
test('badge dot not scaling count > 9', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('badge dot not scaling count > 9', () => {
|
||||
const badge = mount(<Badge count={10} dot />);
|
||||
expect(badge.find('.ant-card-multiple-words').length).toBe(0);
|
||||
});
|
||||
|
||||
test('badge dot not showing count == 0', () => {
|
||||
it('badge dot not showing count == 0', () => {
|
||||
const badge = mount(<Badge count={0} dot />);
|
||||
expect(badge.find('.ant-badge-dot').length).toBe(0);
|
||||
});
|
||||
@@ -17,4 +26,40 @@ describe('Badge', () => {
|
||||
const badge = mount(<Badge count={10} title="Custom title" />);
|
||||
expect(badge.find('.ant-scroll-number').getDOMNode().attributes.getNamedItem('title').value).toEqual('Custom title');
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/10626
|
||||
it('should be composable with Tooltip', () => {
|
||||
const wrapper = mount(
|
||||
<Tooltip title="Fix the error">
|
||||
<Badge status="error" />
|
||||
</Tooltip>
|
||||
);
|
||||
wrapper.find('Badge').simulate('mouseenter');
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.instance().tooltip.props.visible).toBe(true);
|
||||
});
|
||||
|
||||
it('should render when count is changed', () => {
|
||||
const wrapper = mount(<Badge count={9} />);
|
||||
wrapper.setProps({ count: 10 });
|
||||
jest.runAllTimers();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
wrapper.setProps({ count: 11 });
|
||||
jest.runAllTimers();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
wrapper.setProps({ count: 11 });
|
||||
jest.runAllTimers();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
wrapper.setProps({ count: 10 });
|
||||
jest.runAllTimers();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
jest.runAllTimers();
|
||||
wrapper.setProps({ count: 9 });
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should be compatible with borderColor style', () => {
|
||||
const wrapper = render(<Badge count={4} style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -59,7 +59,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
text,
|
||||
offset,
|
||||
title,
|
||||
...restProps,
|
||||
...restProps
|
||||
} = this.props;
|
||||
let displayCount = (count as number) > (overflowCount as number) ? `${overflowCount}+` : count;
|
||||
const isZero = displayCount === '0' || displayCount === 0;
|
||||
@@ -92,7 +92,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
// <Badge status="success" />
|
||||
if (!children && status) {
|
||||
return (
|
||||
<span className={badgeCls} style={styleWithOffset}>
|
||||
<span {...restProps} className={badgeCls} style={styleWithOffset}>
|
||||
<span className={statusCls} />
|
||||
<span className={`${prefixCls}-status-text`}>{text}</span>
|
||||
</span>
|
||||
@@ -107,6 +107,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
count={displayCount}
|
||||
title={title || count}
|
||||
style={styleWithOffset}
|
||||
key="scrollNumber"
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -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>
|
||||
`;
|
||||
150
components/breadcrumb/__tests__/router.test.js
Normal file
150
components/breadcrumb/__tests__/router.test.js
Normal file
@@ -0,0 +1,150 @@
|
||||
import React from 'react';
|
||||
import { Route, Switch, Link, withRouter, MemoryRouter } from 'react-router-dom';
|
||||
import { mount } from 'enzyme';
|
||||
import Breadcrumb from '../index';
|
||||
|
||||
const Apps = () => (
|
||||
<ul className="app-list">
|
||||
<li>
|
||||
<Link to="/apps/1">Application1</Link>:<Link to="/apps/1/detail">Detail</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/apps/2">Application2</Link>:<Link to="/apps/2/detail">Detail</Link>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
|
||||
const breadcrumbNameMap = {
|
||||
'/apps': 'Application List',
|
||||
'/apps/1': 'Application1',
|
||||
'/apps/2': 'Application2',
|
||||
'/apps/1/detail': 'Detail',
|
||||
'/apps/2/detail': 'Detail',
|
||||
};
|
||||
|
||||
const Home = withRouter((props) => {
|
||||
const { location, history } = props;
|
||||
const pathSnippets = location.pathname.split('/').filter(i => i);
|
||||
const extraBreadcrumbItems = pathSnippets.map((_, index) => {
|
||||
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
|
||||
return (
|
||||
<Breadcrumb.Item key={url}>
|
||||
<Link to={url}>
|
||||
{breadcrumbNameMap[url]}
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
);
|
||||
});
|
||||
const breadcrumbItems = [(
|
||||
<Breadcrumb.Item key="home">
|
||||
<Link to="/">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
)].concat(extraBreadcrumbItems);
|
||||
return (
|
||||
<div className="demo">
|
||||
<div className="demo-nav">
|
||||
<a onClick={() => history.push('/')}>Home</a>
|
||||
<a onClick={() => history.push('/apps')}>Application List</a>
|
||||
</div>
|
||||
<Switch>
|
||||
<Route path="/apps" component={Apps} />
|
||||
<Route render={() => <span>Home Page</span>} />
|
||||
</Switch>
|
||||
<Breadcrumb>
|
||||
{breadcrumbItems}
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
describe('react router', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
// https://github.com/airbnb/enzyme/issues/875
|
||||
it('react router 4', () => {
|
||||
const wrapper = mount(
|
||||
<MemoryRouter initialEntries={['/']} initialIndex={0}>
|
||||
<Home />
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(wrapper.find('BreadcrumbItem').length).toBe(1);
|
||||
expect(wrapper.find('BreadcrumbItem .ant-breadcrumb-link').at(0).text()).toBe('Home');
|
||||
wrapper.find('.demo-nav a').at(1).simulate('click');
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -211,24 +211,6 @@ exports[`renders ./components/button/demo/disabled.md correctly 1`] = `
|
||||
</span>
|
||||
</button>
|
||||
<br />
|
||||
<button
|
||||
class="ant-btn ant-btn-background-ghost"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Ghost
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="ant-btn ant-btn-background-ghost"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Ghost(disabled)
|
||||
</span>
|
||||
</button>
|
||||
<br />
|
||||
<button
|
||||
class="ant-btn ant-btn-dashed"
|
||||
type="button"
|
||||
@@ -246,6 +228,27 @@ exports[`renders ./components/button/demo/disabled.md correctly 1`] = `
|
||||
Dashed(disabled)
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
style="padding:8px 8px 0 8px;background:rgb(190, 200, 200)"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-background-ghost"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Ghost
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="ant-btn ant-btn-background-ghost"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Ghost(disabled)
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import Icon from '../icon';
|
||||
import { Omit } from '../_util/type';
|
||||
import Group from './button-group';
|
||||
|
||||
const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/;
|
||||
@@ -39,7 +38,7 @@ export type ButtonType = 'default' | 'primary' | 'ghost' | 'dashed' | 'danger';
|
||||
export type ButtonShape = 'circle' | 'circle-outline';
|
||||
export type ButtonSize = 'small' | 'default' | 'large';
|
||||
|
||||
export interface BaseButtonProps<T> extends Omit<React.HTMLProps<T>, 'ref' | 'size'> {
|
||||
export interface BaseButtonProps {
|
||||
type?: ButtonType;
|
||||
htmlType?: string;
|
||||
icon?: string;
|
||||
@@ -51,11 +50,9 @@ export interface BaseButtonProps<T> extends Omit<React.HTMLProps<T>, 'ref' | 'si
|
||||
ghost?: boolean;
|
||||
}
|
||||
|
||||
export interface AnchorButtonProps extends BaseButtonProps<HTMLAnchorElement> {
|
||||
href: string;
|
||||
}
|
||||
export type AnchorButtonProps = BaseButtonProps & React.AnchorHTMLAttributes<HTMLAnchorElement>;
|
||||
|
||||
export interface NativeButtonProps extends BaseButtonProps<HTMLButtonElement> {}
|
||||
export type NativeButtonProps = BaseButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
|
||||
export type ButtonProps = AnchorButtonProps | NativeButtonProps;
|
||||
|
||||
@@ -160,7 +157,7 @@ export default class Button extends React.Component<ButtonProps, any> {
|
||||
|
||||
render() {
|
||||
const {
|
||||
type, shape, size, className, htmlType, children, icon, prefixCls, ghost, ...others,
|
||||
type, shape, size, className, htmlType, children, icon, prefixCls, ghost, ...others
|
||||
} = this.props;
|
||||
|
||||
const { loading, clicked, hasTwoCNChar } = this.state;
|
||||
|
||||
@@ -24,11 +24,12 @@ ReactDOM.render(
|
||||
<Button>Default</Button>
|
||||
<Button disabled>Default(disabled)</Button>
|
||||
<br />
|
||||
<Button ghost>Ghost</Button>
|
||||
<Button ghost disabled>Ghost(disabled)</Button>
|
||||
<br />
|
||||
<Button type="dashed">Dashed</Button>
|
||||
<Button type="dashed" disabled>Dashed(disabled)</Button>
|
||||
<div style={{ padding: '8px 8px 0 8px', background: 'rgb(190, 200, 200)' }}>
|
||||
<Button ghost>Ghost</Button>
|
||||
<Button ghost disabled>Ghost(disabled)</Button>
|
||||
</div>
|
||||
</div>,
|
||||
mountNode
|
||||
);
|
||||
|
||||
@@ -250,11 +250,16 @@
|
||||
.@{btnClassName} + &,
|
||||
span + .@{btnClassName},
|
||||
.@{btnClassName} + span,
|
||||
> span + span,
|
||||
& + .@{btnClassName},
|
||||
& + & {
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.@{btnClassName}-primary + .@{btnClassName}:not(.@{btnClassName}-primary):not([disabled]) {
|
||||
border-left-color: transparent;
|
||||
}
|
||||
|
||||
.@{btnClassName}:not(:first-child):not(:last-child) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ exports[`renders ./components/calendar/demo/basic.md correctly 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell"
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell ant-fullcalendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="October 31, 2016"
|
||||
>
|
||||
@@ -796,7 +796,7 @@ exports[`renders ./components/calendar/demo/basic.md correctly 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-fullcalendar-cell"
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="November 30, 2016"
|
||||
>
|
||||
@@ -1245,7 +1245,7 @@ exports[`renders ./components/calendar/demo/card.md correctly 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell"
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell ant-fullcalendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="October 31, 2016"
|
||||
>
|
||||
@@ -1805,7 +1805,7 @@ exports[`renders ./components/calendar/demo/card.md correctly 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-fullcalendar-cell"
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="November 30, 2016"
|
||||
>
|
||||
@@ -2256,7 +2256,7 @@ exports[`renders ./components/calendar/demo/notice-calendar.md correctly 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell"
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell ant-fullcalendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="October 31, 2016"
|
||||
>
|
||||
@@ -3093,7 +3093,7 @@ exports[`renders ./components/calendar/demo/notice-calendar.md correctly 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-fullcalendar-cell"
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="November 30, 2016"
|
||||
>
|
||||
@@ -4211,7 +4211,7 @@ exports[`renders ./components/calendar/demo/select.md correctly 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-fullcalendar-cell"
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="January 31, 2017"
|
||||
>
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
}
|
||||
|
||||
&-fullscreen &-content {
|
||||
height: 90px;
|
||||
height: 88px;
|
||||
overflow-y: auto;
|
||||
position: static;
|
||||
width: auto;
|
||||
|
||||
@@ -134,7 +134,7 @@ export default class Card extends React.Component<CardProps, CardState> {
|
||||
render() {
|
||||
const {
|
||||
prefixCls = 'ant-card', className, extra, bodyStyle = {}, noHovering, hoverable, title, loading,
|
||||
bordered = true, type, cover, actions, tabList, children, activeTabKey, defaultActiveTabKey, ...others,
|
||||
bordered = true, type, cover, actions, tabList, children, activeTabKey, defaultActiveTabKey, ...others
|
||||
} = this.props;
|
||||
|
||||
const classString = classNames(prefixCls, className, {
|
||||
|
||||
@@ -3,6 +3,14 @@ import { mount } from 'enzyme';
|
||||
import Carousel from '..';
|
||||
|
||||
describe('Carousel', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should has innerSlider', () => {
|
||||
const wrapper = mount(<Carousel><div /></Carousel>);
|
||||
const { innerSlider } = wrapper.instance();
|
||||
@@ -10,4 +18,42 @@ describe('Carousel', () => {
|
||||
expect(innerSlider).toBe(innerSliderFromRefs);
|
||||
expect(typeof innerSlider.slickNext).toBe('function');
|
||||
});
|
||||
|
||||
it('should has prev, next and go function', () => {
|
||||
const wrapper = mount(<Carousel><div>1</div><div>2</div><div>3</div></Carousel>);
|
||||
const { prev, next, goTo } = wrapper.instance();
|
||||
expect(typeof prev).toBe('function');
|
||||
expect(typeof next).toBe('function');
|
||||
expect(typeof goTo).toBe('function');
|
||||
expect(wrapper.instance().slick.innerSlider.state.currentSlide).toBe(0);
|
||||
wrapper.instance().goTo(2);
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.instance().slick.innerSlider.state.currentSlide).toBe(2);
|
||||
wrapper.instance().prev();
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.instance().slick.innerSlider.state.currentSlide).toBe(1);
|
||||
wrapper.instance().next();
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.instance().slick.innerSlider.state.currentSlide).toBe(2);
|
||||
});
|
||||
|
||||
it('should trigger autoPlay after window resize', async () => {
|
||||
jest.useRealTimers();
|
||||
const wrapper = mount(<Carousel autoplay><div>1</div><div>2</div><div>3</div></Carousel>);
|
||||
const spy = jest.spyOn(wrapper.instance().slick.innerSlider, 'autoPlay');
|
||||
window.resizeTo(1000);
|
||||
expect(spy).not.toBeCalled();
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
expect(spy).toBeCalled();
|
||||
});
|
||||
|
||||
it('cancel resize listener when unmount', async () => {
|
||||
const wrapper = mount(<Carousel autoplay><div>1</div><div>2</div><div>3</div></Carousel>);
|
||||
const onWindowResized = wrapper.instance().onWindowResized;
|
||||
const spy = jest.spyOn(wrapper.instance().onWindowResized, 'cancel');
|
||||
const spy2 = jest.spyOn(window, 'removeEventListener');
|
||||
wrapper.unmount();
|
||||
expect(spy).toBeCalled();
|
||||
expect(spy2).toBeCalledWith('resize', onWindowResized);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -211,7 +211,7 @@ exports[`renders ./components/cascader/demo/lazy.md correctly 1`] = `
|
||||
|
||||
exports[`renders ./components/cascader/demo/search.md correctly 1`] = `
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
class="ant-cascader-picker ant-cascader-picker-show-search"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,6 @@ export interface CascaderOptionType {
|
||||
label?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
children?: Array<CascaderOptionType>;
|
||||
__IS_FILTERED_OPTION?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -90,7 +89,7 @@ export interface CascaderState {
|
||||
inputValue: string;
|
||||
value: string[];
|
||||
popupVisible: boolean | undefined;
|
||||
flattenOptions: CascaderOptionType[][];
|
||||
flattenOptions: CascaderOptionType[][] | undefined;
|
||||
}
|
||||
|
||||
function highlightKeyword(str: string, keyword: string, prefixCls: string | undefined) {
|
||||
@@ -119,7 +118,9 @@ function defaultRenderFilteredOption(
|
||||
});
|
||||
}
|
||||
|
||||
function defaultSortFilteredOption(a: any[], b: any[], inputValue: string, names: FilledFiledNamesType) {
|
||||
function defaultSortFilteredOption(
|
||||
a: CascaderOptionType[], b: CascaderOptionType[], inputValue: string, names: FilledFiledNamesType,
|
||||
) {
|
||||
function callback(elem: CascaderOptionType) {
|
||||
return (elem[names.label] as string).indexOf(inputValue) > -1;
|
||||
}
|
||||
@@ -162,7 +163,8 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
inputValue: '',
|
||||
inputFocused: false,
|
||||
popupVisible: props.popupVisible,
|
||||
flattenOptions: props.showSearch && this.flattenTree(props.options, props.changeOnSelect, props.filedNames),
|
||||
flattenOptions:
|
||||
props.showSearch ? this.flattenTree(props.options, props.changeOnSelect, props.filedNames) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -180,7 +182,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = (value: any, selectedOptions: any[]) => {
|
||||
handleChange = (value: any, selectedOptions: CascaderOptionType[]) => {
|
||||
this.setState({ inputValue: '' });
|
||||
if (selectedOptions[0].__IS_FILTERED_OPTION) {
|
||||
const unwrappedValue = value[0];
|
||||
@@ -217,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +236,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
this.setState({ inputValue });
|
||||
}
|
||||
|
||||
setValue = (value: string[], selectedOptions: any[] = []) => {
|
||||
setValue = (value: string[], selectedOptions: CascaderOptionType[] = []) => {
|
||||
if (!('value' in this.props)) {
|
||||
this.setState({ value });
|
||||
}
|
||||
@@ -271,9 +275,9 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
filedNames: FiledNamesType | undefined,
|
||||
ancestor: CascaderOptionType[] = [],
|
||||
) {
|
||||
const names: FiledNamesType = getFilledFieldNames(filedNames);
|
||||
let flattenOptions: any = [];
|
||||
let childrenName: any = names.children;
|
||||
const names: FilledFiledNamesType = getFilledFieldNames(filedNames);
|
||||
let flattenOptions = [] as CascaderOptionType[][];
|
||||
let childrenName = names.children;
|
||||
options.forEach((option) => {
|
||||
const path = ancestor.concat(option);
|
||||
if (changeOnSelect || !option[childrenName] || !option[childrenName].length) {
|
||||
@@ -301,18 +305,18 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
render = defaultRenderFilteredOption,
|
||||
sort = defaultSortFilteredOption,
|
||||
} = showSearch as ShowSearchType;
|
||||
const { flattenOptions, inputValue } = this.state;
|
||||
const { flattenOptions = [], inputValue } = this.state;
|
||||
const filtered = flattenOptions.filter((path) => filter(this.state.inputValue, path, names))
|
||||
.sort((a, b) => sort(a, b, inputValue, names));
|
||||
|
||||
if (filtered.length > 0) {
|
||||
return filtered.map((path: any) => {
|
||||
return filtered.map((path: CascaderOptionType[]) => {
|
||||
return {
|
||||
__IS_FILTERED_OPTION: true,
|
||||
path,
|
||||
[names.label]: render(inputValue, path, prefixCls, names),
|
||||
[names.value]: path.map((o: CascaderOptionType) => o[names.value]),
|
||||
disabled: path.some((o: CascaderOptionType) => o.disabled),
|
||||
disabled: path.some((o: CascaderOptionType) => !!o.disabled),
|
||||
} as CascaderOptionType;
|
||||
});
|
||||
}
|
||||
@@ -335,7 +339,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
const { props, state } = this;
|
||||
const {
|
||||
prefixCls, inputPrefixCls, children, placeholder, size, disabled,
|
||||
className, style, allowClear, showSearch = false, ...otherProps,
|
||||
className, style, allowClear, showSearch = false, ...otherProps
|
||||
} = props;
|
||||
const value = state.value;
|
||||
|
||||
@@ -359,6 +363,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
[`${prefixCls}-picker-with-value`]: state.inputValue,
|
||||
[`${prefixCls}-picker-disabled`]: disabled,
|
||||
[`${prefixCls}-picker-${size}`]: !!size,
|
||||
[`${prefixCls}-picker-show-search`]: !!showSearch,
|
||||
});
|
||||
|
||||
// Fix bug of https://github.com/facebook/react/pull/5004
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
position: static;
|
||||
}
|
||||
|
||||
&-picker-show-search &-input.@{ant-prefix}-input {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-picker {
|
||||
.reset-component;
|
||||
position: relative;
|
||||
|
||||
@@ -79,7 +79,7 @@ export default class Checkbox extends React.Component<CheckboxProps, {}> {
|
||||
style,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
...restProps,
|
||||
...restProps
|
||||
} = props;
|
||||
const { checkboxGroup } = context;
|
||||
let checkboxProps: CheckboxProps = { ...restProps };
|
||||
|
||||
@@ -4,7 +4,7 @@ import classNames from 'classnames';
|
||||
import shallowEqual from 'shallowequal';
|
||||
import Checkbox from './Checkbox';
|
||||
|
||||
export type CheckboxValueType = string | number;
|
||||
export type CheckboxValueType = string | number | boolean;
|
||||
|
||||
export interface CheckboxOptionType {
|
||||
label: string;
|
||||
@@ -121,7 +121,7 @@ export default class CheckboxGroup extends React.Component<CheckboxGroupProps, C
|
||||
children = this.getOptions().map(option => (
|
||||
<Checkbox
|
||||
prefixCls={prefixCls}
|
||||
key={option.value}
|
||||
key={option.value.toString()}
|
||||
disabled={'disabled' in option ? option.disabled : props.disabled}
|
||||
value={option.value}
|
||||
checked={state.value.indexOf(option.value) !== -1}
|
||||
|
||||
@@ -13,7 +13,9 @@ Checkbox.
|
||||
|
||||
## API
|
||||
|
||||
### Checkbox
|
||||
### Props
|
||||
|
||||
#### Checkbox
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
@@ -21,9 +23,10 @@ Checkbox.
|
||||
| checked | Specifies whether the checkbox is selected. | boolean | false |
|
||||
| defaultChecked | Specifies the initial state: whether or not the checkbox is selected. | boolean | false |
|
||||
| disabled | Disable checkbox | boolean | false |
|
||||
| indeterminate | indeterminate checked state of checkbox | boolean | false |
|
||||
| onChange | The callback function that is triggered when the state changes. | Function(e:Event) | - |
|
||||
|
||||
### Checkbox Group
|
||||
#### Checkbox Group
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
@@ -33,11 +36,11 @@ Checkbox.
|
||||
| value | Used for setting the currently selected value. | string\[] | \[] |
|
||||
| onChange | The callback function that is triggered when the state changes. | Function(checkedValue) | - |
|
||||
|
||||
## Methods
|
||||
### Methods
|
||||
|
||||
### Checkbox
|
||||
#### Checkbox
|
||||
|
||||
| Name | Description |
|
||||
| ---- | ----------- |
|
||||
| blur() | remove focus |
|
||||
| focus() | get focus |
|
||||
| blur() | remove focus |
|
||||
|
||||
@@ -14,30 +14,34 @@ title: Checkbox
|
||||
|
||||
## API
|
||||
|
||||
### Checkbox
|
||||
### 属性
|
||||
|
||||
#### Checkbox
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| autoFocus | 自动获取焦点 | boolean | false |
|
||||
| checked | 指定当前是否选中 | boolean | false |
|
||||
| defaultChecked | 初始是否选中 | boolean | false |
|
||||
| disabled | 失效状态 | boolean | false |
|
||||
| indeterminate | 设置 indeterminate 状态,只负责样式控制 | boolean | false |
|
||||
| onChange | 变化时回调函数 | Function(e:Event) | - |
|
||||
|
||||
### Checkbox Group
|
||||
#### Checkbox Group
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| defaultValue | 默认选中的选项 | string\[] | \[] |
|
||||
| disabled | 整组失效 | boolean | false |
|
||||
| options | 指定可选项 | string\[] | \[] |
|
||||
| value | 指定选中的选项 | string\[] | \[] |
|
||||
| onChange | 变化时回调函数 | Function(checkedValue) | - |
|
||||
|
||||
## 方法
|
||||
### 方法
|
||||
|
||||
### Checkbox
|
||||
#### Checkbox
|
||||
|
||||
| 名称 | 描述 |
|
||||
| --- | --- |
|
||||
| blur() | 移除焦点 |
|
||||
| focus() | 获取焦点 |
|
||||
| blur() | 移除焦点 |
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
&-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
top: 0;
|
||||
|
||||
@@ -261,7 +261,7 @@ exports[`DatePicker prop locale should works 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="December 31, 1999"
|
||||
>
|
||||
@@ -689,7 +689,7 @@ exports[`DatePicker prop locale should works 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="January 31, 2000"
|
||||
>
|
||||
|
||||
@@ -221,7 +221,7 @@ exports[`RangePicker show month panel according to value 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="1999年12月31日"
|
||||
>
|
||||
@@ -649,7 +649,7 @@ exports[`RangePicker show month panel according to value 1`] = `
|
||||
role="row"
|
||||
>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="2000年1月31日"
|
||||
>
|
||||
@@ -902,7 +902,7 @@ exports[`RangePicker show month panel according to value 1`] = `
|
||||
role="row"
|
||||
>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="2000年1月31日"
|
||||
>
|
||||
@@ -1299,7 +1299,7 @@ exports[`RangePicker show month panel according to value 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="2000年2月29日"
|
||||
>
|
||||
@@ -1489,7 +1489,7 @@ exports[`RangePicker show month panel according to value 1`] = `
|
||||
class="ant-calendar-time-picker-btn"
|
||||
role="button"
|
||||
>
|
||||
Select time
|
||||
select time
|
||||
</a>
|
||||
<a
|
||||
class="ant-calendar-ok-btn"
|
||||
@@ -1726,7 +1726,7 @@ exports[`RangePicker switch to corresponding month panel when click presetted ra
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="1999年12月31日"
|
||||
>
|
||||
@@ -2154,7 +2154,7 @@ exports[`RangePicker switch to corresponding month panel when click presetted ra
|
||||
role="row"
|
||||
>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="2000年1月31日"
|
||||
>
|
||||
@@ -2407,7 +2407,7 @@ exports[`RangePicker switch to corresponding month panel when click presetted ra
|
||||
role="row"
|
||||
>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="2000年1月31日"
|
||||
>
|
||||
@@ -2804,7 +2804,7 @@ exports[`RangePicker switch to corresponding month panel when click presetted ra
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="2000年2月29日"
|
||||
>
|
||||
@@ -3001,7 +3001,7 @@ exports[`RangePicker switch to corresponding month panel when click presetted ra
|
||||
class="ant-calendar-time-picker-btn"
|
||||
role="button"
|
||||
>
|
||||
Select time
|
||||
select time
|
||||
</a>
|
||||
<a
|
||||
class="ant-calendar-ok-btn"
|
||||
|
||||
@@ -482,7 +482,7 @@ exports[`MonthPicker and WeekPicker render WeekPicker 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="1999年12月31日"
|
||||
>
|
||||
@@ -886,7 +886,7 @@ exports[`MonthPicker and WeekPicker render WeekPicker 1`] = `
|
||||
5
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
class="ant-calendar-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="2000年1月31日"
|
||||
>
|
||||
|
||||
@@ -19,13 +19,25 @@ There are four kinds of picker:
|
||||
- RangePicker
|
||||
- WeekPicker
|
||||
|
||||
### Localization
|
||||
|
||||
The default locale is en-US, if you need to use other languages, recommend to use internationalized components provided by us at the entrance. Look at: [LocaleProvider](http://ant.design/components/locale-provider/).
|
||||
|
||||
If there are special needs (only modifying single component language), Please use the property: local. Example: [default](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json).
|
||||
|
||||
```jsx
|
||||
import locale from 'antd/lib/date-picker/locale/zh_CN';
|
||||
|
||||
<DatePicker locale={locale} />
|
||||
```
|
||||
|
||||
**Note:** Part of locale of DatePicker, MonthPicker, RangePicker, WeekPicker is read from value. So, please set the locale of moment correctly.
|
||||
|
||||
```jsx
|
||||
// The default locale is en-US, if you want to use other locale, just set locale in entry file globaly.
|
||||
// import moment from 'moment';
|
||||
// import 'moment/locale/zh-cn';
|
||||
// moment.locale('zh-cn');
|
||||
import moment from 'moment';
|
||||
import 'moment/src/locale/zh-cn';
|
||||
// import 'moment/locale/zh-cn'; if you are using webpack 1
|
||||
|
||||
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />
|
||||
```
|
||||
|
||||
@@ -20,13 +20,25 @@ subtitle: 日期选择框
|
||||
- RangePicker
|
||||
- WeekPicker
|
||||
|
||||
### 国际化配置
|
||||
|
||||
默认配置为 en-US,如果你需要设置其他语言,推荐在入口处使用我们提供的国际化组件,详见:[LocaleProvider国际化](http://ant.design/components/locale-provider-cn/)。
|
||||
|
||||
如有特殊需求(仅修改单一组件的语言),请使用 locale 参数,参考:[默认配置](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json)。
|
||||
|
||||
```jsx
|
||||
import locale from 'antd/lib/date-picker/locale/zh_CN';
|
||||
|
||||
<DatePicker locale={locale} />
|
||||
```
|
||||
|
||||
**注意:**DatePicker、MonthPicker、RangePicker、WeekPicker 部分 locale 是从 value 中读取,所以请先正确设置 moment 的 locale。
|
||||
|
||||
```jsx
|
||||
// 默认语言为 en-US,如果你需要设置其他语言,推荐在入口文件全局设置 locale
|
||||
// import moment from 'moment';
|
||||
// import 'moment/locale/zh-cn';
|
||||
// moment.locale('zh-cn');
|
||||
import moment from 'moment';
|
||||
import 'moment/src/locale/zh-cn';
|
||||
// import 'moment/locale/zh-cn'; if you are using webpack 1
|
||||
|
||||
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />
|
||||
```
|
||||
|
||||
@@ -18,6 +18,8 @@ A divider line separates different content.
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
| dashed | whether line is dashed | Boolean | false |
|
||||
| dashed | whether line is dashed | boolean | false |
|
||||
| type | direction type of divider | enum: `horizontal` `vertical` | `horizontal` |
|
||||
| orientation | position of title inside divider | enum: `left` `right` `center` | `center` |
|
||||
| className | className of container | string | - |
|
||||
| style | style object of container | object | - |
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function Divider({
|
||||
className,
|
||||
children,
|
||||
dashed,
|
||||
...restProps,
|
||||
...restProps
|
||||
}: DividerProps) {
|
||||
const orientationPrefix = (orientation.length > 0) ? '-' + orientation : orientation;
|
||||
const classString = classNames(
|
||||
|
||||
@@ -16,6 +16,8 @@ subtitle: 分割线
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| dashed | 是否虚线 | Boolean | false |
|
||||
| dashed | 是否虚线 | boolean | false |
|
||||
| type | 水平还是垂直类型 | enum: `horizontal` `vertical` | `horizontal` |
|
||||
| orientation | 分割线标题的位置 | enum: `left` `right` | `center` |
|
||||
| className | 分割线样式类 | string | - |
|
||||
| style | 分割线样式对象 | object | - |
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
margin: 24px 0;
|
||||
clear: both;
|
||||
}
|
||||
&-horizontal&-with-text {
|
||||
|
||||
&-horizontal&-with-text,
|
||||
&-horizontal&-with-text-left,
|
||||
&-horizontal&-with-text-right {
|
||||
display: table;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
@@ -33,7 +36,6 @@
|
||||
color: @heading-color;
|
||||
font-size: @font-size-lg;
|
||||
margin: 16px 0;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
@@ -45,77 +47,43 @@
|
||||
transform: translateY(50%);
|
||||
}
|
||||
}
|
||||
&-inner-text {
|
||||
display: inline-block;
|
||||
padding: 0 24px;
|
||||
}
|
||||
&-horizontal&-with-text-left {
|
||||
display: table;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
background: transparent;
|
||||
font-weight: 500;
|
||||
color: @heading-color;
|
||||
font-size: @font-size-base;
|
||||
margin: 16px 0;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: table-cell;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
width: 5%;
|
||||
border-top: 1px solid @border-color-split;
|
||||
transform: translateY(50%);
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: table-cell;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
width: 95%;
|
||||
border-top: 1px solid @border-color-split;
|
||||
transform: translateY(50%);
|
||||
}
|
||||
&-inner-text {
|
||||
&-horizontal&-with-text-left,
|
||||
&-horizontal&-with-text-right {
|
||||
font-size: @font-size-base;
|
||||
.@{divider-prefix-cls}-inner-text {
|
||||
display: inline-block;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&-horizontal&-with-text-left {
|
||||
&:before {
|
||||
top: 50%;
|
||||
width: 5%;
|
||||
}
|
||||
&:after {
|
||||
top: 50%;
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
&-horizontal&-with-text-right {
|
||||
display: table;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
background: transparent;
|
||||
font-weight: 500;
|
||||
color: @heading-color;
|
||||
font-size: @font-size-base;
|
||||
margin: 16px 0;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: table-cell;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
width: 95%;
|
||||
border-top: 1px solid @border-color-split;
|
||||
transform: translateY(50%);
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: table-cell;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
width: 5%;
|
||||
border-top: 1px solid @border-color-split;
|
||||
transform: translateY(50%);
|
||||
}
|
||||
&-inner-text {
|
||||
display: inline-block;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&-inner-text {
|
||||
display: inline-block;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
&-dashed {
|
||||
background: none;
|
||||
border-top: 1px dashed @border-color-split;
|
||||
|
||||
@@ -24,7 +24,7 @@ export default class DropdownButton extends React.Component<DropdownButtonProps,
|
||||
type, disabled, onClick, children,
|
||||
prefixCls, className, overlay, trigger, align,
|
||||
visible, onVisibleChange, placement, getPopupContainer,
|
||||
...restProps,
|
||||
...restProps
|
||||
} = this.props;
|
||||
|
||||
const dropdownProps = {
|
||||
@@ -35,9 +35,9 @@ export default class DropdownButton extends React.Component<DropdownButtonProps,
|
||||
onVisibleChange,
|
||||
placement,
|
||||
getPopupContainer,
|
||||
};
|
||||
} as DropDownProps;
|
||||
if ('visible' in this.props) {
|
||||
(dropdownProps as any).visible = visible;
|
||||
dropdownProps.visible = visible;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -41,11 +41,13 @@ export default class Dropdown extends React.Component<DropDownProps, any> {
|
||||
|
||||
componentDidMount() {
|
||||
const { overlay } = this.props;
|
||||
const overlayProps = (overlay as any).props as any;
|
||||
warning(
|
||||
!overlayProps.mode || overlayProps.mode === 'vertical',
|
||||
`mode="${overlayProps.mode}" is not supported for Dropdown\'s Menu.`,
|
||||
);
|
||||
if (overlay) {
|
||||
const overlayProps = (overlay as React.ReactElement<any>).props;
|
||||
warning(
|
||||
!overlayProps.mode || overlayProps.mode === 'vertical',
|
||||
`mode="${overlayProps.mode}" is not supported for Dropdown\'s Menu.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
outline: none;
|
||||
position: relative;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
padding: 4px 0;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
background-color: @component-background;
|
||||
@@ -102,26 +102,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child,
|
||||
&:first-child > a {
|
||||
border-radius: @border-radius-base @border-radius-base 0 0;
|
||||
}
|
||||
|
||||
&:last-child,
|
||||
&:last-child > a {
|
||||
border-radius: 0 0 @border-radius-base @border-radius-base;
|
||||
}
|
||||
|
||||
&:only-child,
|
||||
&:only-child > a {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
&-divider {
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
background-color: @border-color-split;
|
||||
line-height: 0;
|
||||
margin: 4px 0;
|
||||
}
|
||||
.@{dropdown-prefix-cls}-menu-submenu-arrow {
|
||||
position: absolute;
|
||||
@@ -138,10 +124,6 @@
|
||||
|
||||
&-submenu-title {
|
||||
padding-right: 26px;
|
||||
&:first-child,
|
||||
&:last-child {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-submenu-vertical {
|
||||
@@ -163,13 +145,6 @@
|
||||
color: @disabled-color;
|
||||
}
|
||||
}
|
||||
&-submenu:first-child &-submenu-title {
|
||||
border-radius: @border-radius-base @border-radius-base 0 0;
|
||||
}
|
||||
|
||||
&-submenu:last-child &-submenu-title {
|
||||
border-radius: 0 0 @border-radius-base @border-radius-base;
|
||||
}
|
||||
}
|
||||
|
||||
&.slide-down-enter.slide-down-enter-active&-placement-bottomLeft,
|
||||
|
||||
@@ -18,7 +18,7 @@ export interface FormCreateOption<T> {
|
||||
|
||||
export type FormLayout = 'horizontal' | 'inline' | 'vertical';
|
||||
|
||||
export interface FormProps {
|
||||
export interface FormProps extends React.FormHTMLAttributes<HTMLFormElement> {
|
||||
layout?: FormLayout;
|
||||
form?: WrappedFormUtils;
|
||||
onSubmit?: React.FormEventHandler<any>;
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import intersperse from 'intersperse';
|
||||
import Animate from 'rc-animate';
|
||||
import Row from '../grid/row';
|
||||
import Col, { ColProps } from '../grid/col';
|
||||
@@ -65,15 +66,20 @@ export default class FormItem extends React.Component<FormItemProps, any> {
|
||||
);
|
||||
}
|
||||
|
||||
getHelpMsg() {
|
||||
const props = this.props;
|
||||
const onlyControl = this.getOnlyControl();
|
||||
if (props.help === undefined && onlyControl) {
|
||||
getHelpMessage() {
|
||||
const { help } = this.props;
|
||||
if (help === undefined && this.getOnlyControl()) {
|
||||
const errors = this.getField().errors;
|
||||
return errors ? errors.map((e: any) => e.message).join(', ') : '';
|
||||
if (errors) {
|
||||
return intersperse(errors.map((e: any, index: number) => (
|
||||
React.isValidElement(e.message)
|
||||
? React.cloneElement(e.message, { key: index })
|
||||
: e.message
|
||||
)), ' ');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
return props.help;
|
||||
return help;
|
||||
}
|
||||
|
||||
getControls(children: React.ReactNode, recursively: boolean) {
|
||||
@@ -132,7 +138,7 @@ export default class FormItem extends React.Component<FormItemProps, any> {
|
||||
|
||||
renderHelp() {
|
||||
const prefixCls = this.props.prefixCls;
|
||||
const help = this.getHelpMsg();
|
||||
const help = this.getHelpMessage();
|
||||
const children = help ? (
|
||||
<div className={`${prefixCls}-explain`} key="help">
|
||||
{help}
|
||||
|
||||
100
components/form/__tests__/__snapshots__/message.test.js.snap
Normal file
100
components/form/__tests__/__snapshots__/message.test.js.snap
Normal file
@@ -0,0 +1,100 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Form should display custom message 1`] = `
|
||||
<form
|
||||
class="ant-form ant-form-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item ant-form-item-with-help"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
for="account"
|
||||
title="Account"
|
||||
>
|
||||
Account
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item-control-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control has-error"
|
||||
>
|
||||
<span
|
||||
class="ant-form-item-children"
|
||||
>
|
||||
<input
|
||||
data-__field="[object Object]"
|
||||
data-__meta="[object Object]"
|
||||
id="account"
|
||||
value="antd"
|
||||
/>
|
||||
</span>
|
||||
<div
|
||||
class="ant-form-explain show-help-enter"
|
||||
>
|
||||
<span>
|
||||
Account does not exist,
|
||||
<a
|
||||
href="https://www.alipay.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Forgot account?
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
|
||||
exports[`Form should display two message 1`] = `
|
||||
<form
|
||||
class="ant-form ant-form-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item ant-form-item-with-help"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
for="account"
|
||||
title="Account"
|
||||
>
|
||||
Account
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item-control-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control has-error"
|
||||
>
|
||||
<span
|
||||
class="ant-form-item-children"
|
||||
>
|
||||
<input
|
||||
data-__field="[object Object]"
|
||||
data-__meta="[object Object]"
|
||||
id="account"
|
||||
value="+=-/"
|
||||
/>
|
||||
</span>
|
||||
<div
|
||||
class="ant-form-explain show-help-enter"
|
||||
>
|
||||
Error message 1 Error message 2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
56
components/form/__tests__/message.test.js
Normal file
56
components/form/__tests__/message.test.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Form from '..';
|
||||
|
||||
describe('Form', () => {
|
||||
it('should display two message', () => {
|
||||
const rules = [{
|
||||
pattern: /^\w+$/,
|
||||
message: 'Error message 1',
|
||||
}, {
|
||||
pattern: /^\w+$/,
|
||||
message: 'Error message 2',
|
||||
}];
|
||||
let myForm;
|
||||
const Form1 = Form.create()(({ form }) => {
|
||||
myForm = form;
|
||||
return (
|
||||
<Form>
|
||||
<Form.Item label="Account">
|
||||
{form.getFieldDecorator('account', { initialValue: '+=-/', rules })(<input />)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
const wrapper = mount(<Form1 />);
|
||||
myForm.validateFields();
|
||||
|
||||
wrapper.update();
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should display custom message', () => {
|
||||
const rules = [{
|
||||
pattern: /^$/,
|
||||
message: (<span>Account does not exist, <a rel="noopener noreferrer" href="https://www.alipay.com/" target="_blank">Forgot account?</a></span>),
|
||||
}];
|
||||
let myForm;
|
||||
const Form1 = Form.create()(({ form }) => {
|
||||
myForm = form;
|
||||
return (
|
||||
<Form>
|
||||
<Form.Item label="Account">
|
||||
{form.getFieldDecorator('account', { initialValue: 'antd', rules })(<input />)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
const wrapper = mount(<Form1 />);
|
||||
myForm.validateFields();
|
||||
|
||||
wrapper.update();
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -147,7 +147,7 @@ Note:
|
||||
| enum | validate a value from a list of possible values | string | - |
|
||||
| len | validate an exact length of a field | number | - |
|
||||
| max | validate a max length of a field | number | - |
|
||||
| message | validation error message | string | - |
|
||||
| message | validation error message | string\|ReactNode | - |
|
||||
| min | validate a min length of a field | number | - |
|
||||
| pattern | validate from a regular expression | RegExp | - |
|
||||
| required | indicates whether field is required | boolean | `false` |
|
||||
|
||||
@@ -148,7 +148,7 @@ CustomizedForm = Form.create({})(CustomizedForm);
|
||||
| enum | 枚举类型 | string | - |
|
||||
| len | 字段长度 | number | - |
|
||||
| max | 最大长度 | number | - |
|
||||
| message | 校验文案 | string | - |
|
||||
| message | 校验文案 | string\|ReactNode | - |
|
||||
| min | 最小长度 | number | - |
|
||||
| pattern | 正则表达式校验 | RegExp | - |
|
||||
| required | 是否必选 | boolean | `false` |
|
||||
|
||||
@@ -22,17 +22,10 @@ import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
|
||||
export type BreakpointMap = {
|
||||
xs?: string;
|
||||
sm?: string;
|
||||
md?: string;
|
||||
lg?: string;
|
||||
xl?: string;
|
||||
xxl?: string
|
||||
};
|
||||
export type BreakpointMap = Partial<Record<Breakpoint, string>>;
|
||||
|
||||
export interface RowProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
gutter?: number | BreakpointMap;
|
||||
gutter?: number | Partial<Record<Breakpoint, number>>;
|
||||
type?: 'flex';
|
||||
align?: 'top' | 'middle' | 'bottom';
|
||||
justify?: 'start' | 'end' | 'center' | 'space-around' | 'space-between';
|
||||
@@ -122,7 +115,7 @@ export default class Row extends React.Component<RowProps, RowState> {
|
||||
render() {
|
||||
const {
|
||||
type, justify, align, className, style, children,
|
||||
prefixCls = 'ant-row', ...others,
|
||||
prefixCls = 'ant-row', ...others
|
||||
} = this.props;
|
||||
const gutter = this.getGutter();
|
||||
const classes = classNames({
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// this file is not used if use https://github.com/ant-design/babel-plugin-import
|
||||
const ENV = process.env.NODE_ENV;
|
||||
if (ENV !== 'production' &&
|
||||
ENV !== 'test' &&
|
||||
typeof console !== 'undefined' &&
|
||||
console.warn &&
|
||||
typeof window !== 'undefined') {
|
||||
|
||||
@@ -2,7 +2,12 @@ import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import RcInputNumber from 'rc-input-number';
|
||||
|
||||
export interface InputNumberProps {
|
||||
import { Omit } from '../_util/type';
|
||||
|
||||
// omitting this attrs because they conflicts with the ones defined in InputNumberProps
|
||||
export type OmitAttrs = 'defaultValue' | 'onChange' | 'size';
|
||||
|
||||
export interface InputNumberProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, OmitAttrs> {
|
||||
prefixCls?: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
@@ -10,7 +15,6 @@ export interface InputNumberProps {
|
||||
step?: number | string;
|
||||
defaultValue?: number;
|
||||
tabIndex?: number;
|
||||
onKeyDown?: React.FormEventHandler<any>;
|
||||
onChange?: (value: number | string | undefined) => void;
|
||||
disabled?: boolean;
|
||||
size?: 'large' | 'small' | 'default';
|
||||
|
||||
@@ -5,46 +5,23 @@ import omit from 'omit.js';
|
||||
import Group from './Group';
|
||||
import Search from './Search';
|
||||
import TextArea from './TextArea';
|
||||
import { Omit } from '../_util/type';
|
||||
|
||||
function fixControlledValue(value: undefined | null | string) {
|
||||
function fixControlledValue<T>(value: T) {
|
||||
if (typeof value === 'undefined' || value === null) {
|
||||
return '';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export interface AbstractInputProps {
|
||||
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix'> {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
defaultValue?: any;
|
||||
value?: any;
|
||||
tabIndex?: number;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export interface InputProps extends AbstractInputProps {
|
||||
placeholder?: string;
|
||||
type?: string;
|
||||
id?: number | string;
|
||||
name?: string;
|
||||
size?: 'large' | 'default' | 'small';
|
||||
maxLength?: number | string;
|
||||
disabled?: boolean;
|
||||
readOnly?: boolean;
|
||||
onPressEnter?: React.KeyboardEventHandler<HTMLInputElement>;
|
||||
addonBefore?: React.ReactNode;
|
||||
addonAfter?: React.ReactNode;
|
||||
onPressEnter?: React.FormEventHandler<HTMLInputElement>;
|
||||
onKeyDown?: React.FormEventHandler<HTMLInputElement>;
|
||||
onKeyUp?: React.FormEventHandler<HTMLInputElement>;
|
||||
onChange?: React.ChangeEventHandler<HTMLInputElement>;
|
||||
onClick?: React.FormEventHandler<HTMLInputElement>;
|
||||
onFocus?: React.FormEventHandler<HTMLInputElement>;
|
||||
onBlur?: React.FormEventHandler<HTMLInputElement>;
|
||||
autoComplete?: string;
|
||||
prefix?: React.ReactNode;
|
||||
suffix?: React.ReactNode;
|
||||
spellCheck?: boolean;
|
||||
autoFocus?: boolean;
|
||||
}
|
||||
|
||||
export default class Input extends React.Component<InputProps, any> {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import omit from 'omit.js';
|
||||
import classNames from 'classnames';
|
||||
import { AbstractInputProps } from './Input';
|
||||
import calculateNodeHeight from './calculateNodeHeight';
|
||||
|
||||
function onNextFrame(cb: () => void) {
|
||||
@@ -24,9 +23,10 @@ export interface AutoSizeType {
|
||||
maxRows?: number;
|
||||
}
|
||||
|
||||
export interface TextAreaProps extends AbstractInputProps {
|
||||
export interface TextAreaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
prefixCls?: string;
|
||||
autosize?: boolean | AutoSizeType;
|
||||
onPressEnter?: React.FormEventHandler<any>;
|
||||
onPressEnter?: React.KeyboardEventHandler<HTMLTextAreaElement>;
|
||||
}
|
||||
|
||||
export interface TextAreaState {
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
````
|
||||
|
||||
@@ -9,13 +9,11 @@ title:
|
||||
|
||||
我们为 `<Input />` 输入框定义了三种尺寸(大、默认、小),高度分别为 `40px`、`32px` 和 `24px`。
|
||||
|
||||
注意: 在表单里面,我们只使用大尺寸的输入框。
|
||||
|
||||
## en-US
|
||||
|
||||
There are three sizes of an Input box: `large` (40px)、`default` (32px) and `small` (24px).
|
||||
|
||||
Note: Inside of forms, only the large size is used.
|
||||
|
||||
````jsx
|
||||
import { Input } from 'antd';
|
||||
|
||||
@@ -21,11 +21,13 @@
|
||||
border-color: ~`colorPalette("@{color}", 5)`;
|
||||
outline: 0;
|
||||
box-shadow: 0 0 @outline-blur-size @outline-width fade(@color, 20%);
|
||||
border-right-width: 1px !important;
|
||||
}
|
||||
|
||||
// == when hoverd
|
||||
.hover(@color: @input-hover-border-color) {
|
||||
border-color: ~`colorPalette("@{color}", 5)`;
|
||||
border-right-width: 1px !important;
|
||||
}
|
||||
|
||||
.disabled() {
|
||||
@@ -135,6 +137,11 @@
|
||||
margin-bottom: 0;
|
||||
&:focus {
|
||||
z-index: 1; // Fix https://gw.alipayobjects.com/zos/rmsportal/DHNpoqfMXSfrSnlZvhsJ.png
|
||||
border-right-width: 1px;
|
||||
}
|
||||
&:hover {
|
||||
z-index: 1;
|
||||
border-right-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +274,6 @@
|
||||
// Undo float for .ant-input-group .ant-input
|
||||
.@{inputClass} {
|
||||
float: none;
|
||||
z-index: auto;
|
||||
}
|
||||
|
||||
// reset border for Select, DatePicker, AutoComplete, Cascader, Mention, TimePicker
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -31,6 +31,8 @@ const dimensionMap = {
|
||||
|
||||
export type CollapseType = 'clickTrigger' | 'responsive';
|
||||
|
||||
export type SiderTheme = 'light' | 'dark';
|
||||
|
||||
export interface SiderProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
prefixCls?: string;
|
||||
collapsible?: boolean;
|
||||
@@ -42,6 +44,7 @@ export interface SiderProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
width?: number | string;
|
||||
collapsedWidth?: number | string;
|
||||
breakpoint?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
|
||||
theme?: SiderTheme;
|
||||
}
|
||||
|
||||
export interface SiderState {
|
||||
@@ -73,6 +76,7 @@ export default class Sider extends React.Component<SiderProps, SiderState> {
|
||||
width: 200,
|
||||
collapsedWidth: 80,
|
||||
style: {},
|
||||
theme: 'dark' as SiderTheme,
|
||||
};
|
||||
|
||||
static childContextTypes = {
|
||||
@@ -174,9 +178,9 @@ export default class Sider extends React.Component<SiderProps, SiderState> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { prefixCls, className,
|
||||
const { prefixCls, className, theme,
|
||||
collapsible, reverseArrow, trigger, style, width, collapsedWidth,
|
||||
...others,
|
||||
...others
|
||||
} = this.props;
|
||||
const divProps = omit(others, ['collapsed',
|
||||
'defaultCollapsed', 'onCollapse', 'breakpoint']);
|
||||
@@ -210,7 +214,7 @@ export default class Sider extends React.Component<SiderProps, SiderState> {
|
||||
minWidth: siderWidth, // https://github.com/ant-design/ant-design/issues/6349
|
||||
width: siderWidth,
|
||||
};
|
||||
const siderCls = classNames(className, prefixCls, {
|
||||
const siderCls = classNames(className, prefixCls, `${prefixCls}-${theme}`, {
|
||||
[`${prefixCls}-collapsed`]: !!this.state.collapsed,
|
||||
[`${prefixCls}-has-trigger`]: collapsible && trigger !== null && !zeroWidthTrigger,
|
||||
[`${prefixCls}-below`]: !!this.state.below,
|
||||
|
||||
@@ -33,7 +33,7 @@ exports[`renders ./components/layout/demo/basic.md correctly 1`] = `
|
||||
class="ant-layout"
|
||||
>
|
||||
<div
|
||||
class="ant-layout-sider"
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
|
||||
>
|
||||
<div
|
||||
@@ -71,7 +71,7 @@ exports[`renders ./components/layout/demo/basic.md correctly 1`] = `
|
||||
Content
|
||||
</div>
|
||||
<div
|
||||
class="ant-layout-sider"
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
|
||||
>
|
||||
<div
|
||||
@@ -91,7 +91,7 @@ exports[`renders ./components/layout/demo/basic.md correctly 1`] = `
|
||||
class="ant-layout"
|
||||
>
|
||||
<div
|
||||
class="ant-layout-sider"
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
|
||||
>
|
||||
<div
|
||||
@@ -128,7 +128,7 @@ exports[`renders ./components/layout/demo/custom-trigger.md correctly 1`] = `
|
||||
class="ant-layout"
|
||||
>
|
||||
<div
|
||||
class="ant-layout-sider"
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
|
||||
>
|
||||
<div
|
||||
@@ -207,7 +207,7 @@ exports[`renders ./components/layout/demo/fixed.md correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-layout-header"
|
||||
style="position:fixed;width:100%"
|
||||
style="position:fixed;z-index:1;width:100%"
|
||||
>
|
||||
<div
|
||||
class="logo"
|
||||
@@ -302,7 +302,7 @@ exports[`renders ./components/layout/demo/fixed-sider.md correctly 1`] = `
|
||||
class="ant-layout"
|
||||
>
|
||||
<div
|
||||
class="ant-layout-sider"
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
style="overflow:auto;height:100vh;position:fixed;left:0;flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
|
||||
>
|
||||
<div
|
||||
@@ -557,7 +557,7 @@ exports[`renders ./components/layout/demo/responsive.md correctly 1`] = `
|
||||
class="ant-layout"
|
||||
>
|
||||
<div
|
||||
class="ant-layout-sider"
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
|
||||
>
|
||||
<div
|
||||
@@ -662,7 +662,7 @@ exports[`renders ./components/layout/demo/side.md correctly 1`] = `
|
||||
style="min-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-layout-sider ant-layout-sider-has-trigger"
|
||||
class="ant-layout-sider ant-layout-sider-dark ant-layout-sider-has-trigger"
|
||||
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
|
||||
>
|
||||
<div
|
||||
@@ -1006,7 +1006,7 @@ exports[`renders ./components/layout/demo/top-side.md correctly 1`] = `
|
||||
style="padding:24px 0;background:#fff"
|
||||
>
|
||||
<div
|
||||
class="ant-layout-sider"
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
style="background:#fff;flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
|
||||
>
|
||||
<div
|
||||
@@ -1176,7 +1176,7 @@ exports[`renders ./components/layout/demo/top-side-2.md correctly 1`] = `
|
||||
class="ant-layout"
|
||||
>
|
||||
<div
|
||||
class="ant-layout-sider"
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
style="background:#fff;flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -55,4 +55,18 @@ describe('Layout', () => {
|
||||
);
|
||||
expect(wrapper.find('.ant-layout-sider').hasClass('ant-layout-sider-zero-width')).toBe(true);
|
||||
});
|
||||
|
||||
it('detect ant-layout-sider-dark as default theme', async () => {
|
||||
const wrapper = mount(
|
||||
<Sider>Sider</Sider>
|
||||
);
|
||||
expect(wrapper.find('.ant-layout-sider').hasClass('ant-layout-sider-dark'));
|
||||
});
|
||||
|
||||
it('detect ant-layout-sider-light when set light theme', async () => {
|
||||
const wrapper = mount(
|
||||
<Sider theme="light">Sider</Sider>
|
||||
);
|
||||
expect(wrapper.find('.ant-layout-sider').hasClass('ant-layout-sider-light'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ const { Header, Content, Footer } = Layout;
|
||||
|
||||
ReactDOM.render(
|
||||
<Layout>
|
||||
<Header style={{ position: 'fixed', width: '100%' }}>
|
||||
<Header style={{ position: 'fixed', zIndex: 1, width: '100%' }}>
|
||||
<div className="logo" />
|
||||
<Menu
|
||||
theme="dark"
|
||||
|
||||
@@ -98,6 +98,7 @@ The sidebar.
|
||||
| trigger | specify the customized trigger, set to null to hide the trigger | string\|ReactNode | - |
|
||||
| width | width of the sidebar | number\|string | 200 |
|
||||
| onCollapse | the callback function, executed by clicking the trigger or activating the responsive layout | (collapsed, type) => {} | - |
|
||||
| theme | color theme of the sidebar | string: `light` `dark` | `dark` |
|
||||
|
||||
#### breakpoint width
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ title: Layout
|
||||
| trigger | 自定义 trigger,设置为 null 时隐藏 trigger | string\|ReactNode | - |
|
||||
| width | 宽度 | number\|string | 200 |
|
||||
| onCollapse | 展开-收起时的回调函数,有点击 trigger 以及响应式反馈两种方式可以触发 | (collapsed, type) => {} | - |
|
||||
| theme | 主题颜色 | string: `light` `dark` | `dark` |
|
||||
|
||||
#### breakpoint width
|
||||
|
||||
|
||||
@@ -110,3 +110,5 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@import './light';
|
||||
|
||||
11
components/layout/style/light.less
Normal file
11
components/layout/style/light.less
Normal file
@@ -0,0 +1,11 @@
|
||||
.@{layout-prefix-cls} {
|
||||
&-sider {
|
||||
&-light {
|
||||
background: @layout-sider-background-light;
|
||||
}
|
||||
&-light > &-trigger {
|
||||
color: @layout-trigger-color-light;
|
||||
background: @layout-trigger-background-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export const Meta = (props: ListItemMetaProps) => {
|
||||
avatar,
|
||||
title,
|
||||
description,
|
||||
...others,
|
||||
...others
|
||||
} = props;
|
||||
|
||||
const classString = classNames(`${prefixCls}-item-meta`, className);
|
||||
|
||||
@@ -126,4 +126,15 @@ describe('List.pagination', () => {
|
||||
.hasClass('ant-pagination-item-active')
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('specify the position of pagination', () => {
|
||||
const wrapper = mount(createList({ pagination: { position: 'top' } }));
|
||||
expect(wrapper.find('.ant-list').childAt(0).find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: 'bottom' } });
|
||||
expect(wrapper.find('.ant-list').children().last().find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: 'both' } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(2);
|
||||
expect(wrapper.find('.ant-list').childAt(0).find('.ant-pagination')).toHaveLength(1);
|
||||
expect(wrapper.find('.ant-list').children().last().find('.ant-pagination')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
category: Components
|
||||
type: Data Display
|
||||
title: List
|
||||
title: List
|
||||
cols: 1
|
||||
---
|
||||
|
||||
@@ -28,6 +28,16 @@ A list can be used to display content related to a single subject. The content c
|
||||
| pagination | Pagination [config](https://ant.design/components/pagination/), hide it by setting it to false | boolean \| object | false |
|
||||
| split | Toggles rendering of the split under the list item | boolean | true |
|
||||
|
||||
### pagination
|
||||
|
||||
Properties for pagination.
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
| position | specify the position of `Pagination` | 'top' \| 'bottom' \| 'both' | 'bottom' |
|
||||
|
||||
More about pagination, please check [`Pagination`](/components/pagination/).
|
||||
|
||||
### List grid props
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
|
||||
@@ -6,7 +6,7 @@ import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import defaultLocale from '../locale-provider/default';
|
||||
|
||||
import Spin from '../spin';
|
||||
import Pagination from '../pagination';
|
||||
import Pagination, { PaginationConfig } from '../pagination';
|
||||
import { Row } from '../grid';
|
||||
|
||||
import Item from './Item';
|
||||
@@ -49,7 +49,7 @@ export interface ListProps {
|
||||
itemLayout?: string;
|
||||
loading?: boolean | SpinProps;
|
||||
loadMore?: React.ReactNode;
|
||||
pagination?: any;
|
||||
pagination?: PaginationConfig;
|
||||
prefixCls?: string;
|
||||
rowKey?: any;
|
||||
renderItem: any;
|
||||
@@ -162,7 +162,7 @@ export default class List extends React.Component<ListProps> {
|
||||
footer,
|
||||
loading,
|
||||
locale,
|
||||
...rest,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
let loadingProp = loading;
|
||||
@@ -200,8 +200,9 @@ export default class List extends React.Component<ListProps> {
|
||||
...this.defaultPaginationProps,
|
||||
total: dataSource.length,
|
||||
current: paginationCurrent,
|
||||
...pagination,
|
||||
...pagination || {},
|
||||
};
|
||||
|
||||
const largestPage = Math.ceil(
|
||||
paginationProps.total / paginationProps.pageSize,
|
||||
);
|
||||
@@ -258,15 +259,18 @@ export default class List extends React.Component<ListProps> {
|
||||
);
|
||||
}
|
||||
|
||||
const paginationPosition = paginationProps.position || 'bottom';
|
||||
|
||||
return (
|
||||
<div className={classString} {...rest}>
|
||||
{(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent}
|
||||
{header && <div className={`${prefixCls}-header`}>{header}</div>}
|
||||
<Spin {...loadingProp}>
|
||||
{childrenContent}
|
||||
{children}
|
||||
</Spin>
|
||||
{footer && <div className={`${prefixCls}-footer`}>{footer}</div>}
|
||||
{loadMore || paginationContent}
|
||||
{loadMore || (paginationPosition === 'bottom' || paginationPosition === 'both') && paginationContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
category: Components
|
||||
type: Data Display
|
||||
title: List
|
||||
title: List
|
||||
subtitle: 列表
|
||||
cols: 1
|
||||
---
|
||||
@@ -30,6 +30,16 @@ cols: 1
|
||||
| size | list 的尺寸 | `default` \| `middle` \| `small` | `default` |
|
||||
| split | 是否展示分割线 | boolean | true |
|
||||
|
||||
### pagination
|
||||
|
||||
分页的配置项。
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| position | 指定分页显示的位置 | 'top' \| 'bottom' \| 'both' | 'bottom' |
|
||||
|
||||
更多配置项,请查看 [`Pagination`](/components/pagination/)。
|
||||
|
||||
### List grid props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|
||||
@@ -703,7 +703,7 @@ exports[`renders ./components/locale-provider/demo/all.md correctly 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell"
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell ant-fullcalendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="October 31, 2016"
|
||||
>
|
||||
@@ -1263,7 +1263,7 @@ exports[`renders ./components/locale-provider/demo/all.md correctly 1`] = `
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-fullcalendar-cell"
|
||||
class="ant-fullcalendar-cell ant-fullcalendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="November 30, 2016"
|
||||
>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,8 @@ title: LocaleProvider
|
||||
```jsx
|
||||
import { LocaleProvider } from 'antd';
|
||||
import fr_FR from 'antd/lib/locale-provider/fr_FR';
|
||||
import 'moment/locale/fr';
|
||||
import 'moment/src/locale/fr';
|
||||
// import 'moment/locale/fr'; if you are using webpack 1
|
||||
|
||||
...
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@ LocaleProvider 使用 React 的 [context](https://facebook.github.io/react/docs/
|
||||
```jsx
|
||||
import { LocaleProvider } from 'antd';
|
||||
import zh_CN from 'antd/lib/locale-provider/zh_CN';
|
||||
import 'moment/locale/zh-cn';
|
||||
import 'moment/src/locale/zh-cn';
|
||||
// import 'moment/locale/zh-cn'; if you are using webpack 1
|
||||
|
||||
...
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ export default {
|
||||
Transfer: {
|
||||
notFoundContent: 'Ничего не найдено',
|
||||
searchPlaceholder: 'Введите название для поиска',
|
||||
itemUnit: 'item',
|
||||
itemsUnit: 'items',
|
||||
itemUnit: 'элем.',
|
||||
itemsUnit: 'элем.',
|
||||
},
|
||||
Select: {
|
||||
notFoundContent: 'Ничего не найдено',
|
||||
|
||||
804
components/mention/__tests__/__snapshots__/demo.test.js.snap
Normal file
804
components/mention/__tests__/__snapshots__/demo.test.js.snap
Normal file
@@ -0,0 +1,804 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/mention/demo/async.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-mention-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor oneline"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="notranslate public-DraftEditor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<br
|
||||
data-text="true"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mention/demo/avatar.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-mention-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor oneline"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="notranslate public-DraftEditor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<br
|
||||
data-text="true"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mention/demo/basic.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-mention-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor oneline"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="notranslate public-DraftEditor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-text="true"
|
||||
>
|
||||
@afc163
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mention/demo/controllder-simple.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-mention-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor oneline"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="notranslate public-DraftEditor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-text="true"
|
||||
>
|
||||
@afc163
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mention/demo/controlled.md correctly 1`] = `
|
||||
<form
|
||||
class="ant-form ant-form-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-col-6 ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
for="control-mention"
|
||||
title="Top coders"
|
||||
>
|
||||
Top coders
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col-16 ant-form-item-control-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control has-success"
|
||||
>
|
||||
<span
|
||||
class="ant-form-item-children"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor oneline"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="notranslate public-DraftEditor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-text="true"
|
||||
>
|
||||
@afc163
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-row ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-col-14 ant-col-offset-6 ant-form-item-control-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control"
|
||||
>
|
||||
<span
|
||||
class="ant-form-item-children"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Submit
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="ant-btn"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Reset
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mention/demo/custom-tag.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-mention-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor oneline"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="public-DraftEditorPlaceholder-root"
|
||||
>
|
||||
<div
|
||||
class="public-DraftEditorPlaceholder-inner"
|
||||
id="placeholder-123"
|
||||
style="white-space:pre-wrap"
|
||||
>
|
||||
@someone
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="notranslate public-DraftEditor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<br
|
||||
data-text="true"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mention/demo/multilines.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-mention-wrapper multilines"
|
||||
style="width:100%;height:100px"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor"
|
||||
style="width:100%;height:100px"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
style="width:100%;height:100px"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="notranslate public-DraftEditor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<br
|
||||
data-text="true"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mention/demo/multiple-trigger.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-mention-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor oneline"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="public-DraftEditorPlaceholder-root"
|
||||
>
|
||||
<div
|
||||
class="public-DraftEditorPlaceholder-inner"
|
||||
id="placeholder-123"
|
||||
style="white-space:pre-wrap"
|
||||
>
|
||||
input @ to mention people, # to mention tag
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="notranslate public-DraftEditor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<br
|
||||
data-text="true"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mention/demo/placement.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-mention-placement-top ant-mention-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor oneline"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="notranslate public-DraftEditor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<br
|
||||
data-text="true"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mention/demo/popupContainer.md correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Click Me
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mention/demo/readonly.md correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="margin-bottom:10px"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-wrapper disabled"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor readonly oneline"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="public-DraftEditorPlaceholder-root"
|
||||
>
|
||||
<div
|
||||
class="public-DraftEditorPlaceholder-inner"
|
||||
id="placeholder-123"
|
||||
style="white-space:pre-wrap"
|
||||
>
|
||||
this is disabled Mention
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="public-DraftEditor-content"
|
||||
contenteditable="false"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<br
|
||||
data-text="true"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
style="width:0;opacity:0;border:0;position:absolute;left:0;top:0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-mention-wrapper readonly"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-editor readonly oneline"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-mention-toolbar"
|
||||
/>
|
||||
<div
|
||||
class="ant-mention-editor-wrapper"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="DraftEditor-root"
|
||||
>
|
||||
<div
|
||||
class="public-DraftEditorPlaceholder-root"
|
||||
>
|
||||
<div
|
||||
class="public-DraftEditorPlaceholder-inner"
|
||||
id="placeholder-123"
|
||||
style="white-space:pre-wrap"
|
||||
>
|
||||
this is readOnly Mention
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="DraftEditor-editorContainer"
|
||||
>
|
||||
<div
|
||||
aria-describedby="placeholder-123"
|
||||
class="public-DraftEditor-content"
|
||||
contenteditable="false"
|
||||
spellcheck="false"
|
||||
style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word"
|
||||
>
|
||||
<div
|
||||
data-contents="true"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
data-block="true"
|
||||
data-editor="123"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<div
|
||||
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<span
|
||||
data-offset-key="123-0-0"
|
||||
>
|
||||
<br
|
||||
data-text="true"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
style="width:0;opacity:0;border:0;position:absolute;left:0;top:0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,3 +1,5 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('mention', { skip: true });
|
||||
jest.mock('draft-js/lib/generateRandomKey', () => () => '123');
|
||||
|
||||
demoTest('mention', { skip: process.env.LIB_DIR === 'dist' });
|
||||
|
||||
61
components/mention/__tests__/index.test.js
Normal file
61
components/mention/__tests__/index.test.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Mention from '..';
|
||||
|
||||
const { toContentState } = Mention;
|
||||
|
||||
describe('Mention', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should has focus function', () => {
|
||||
const handleFocus = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Mention
|
||||
defaultValue={toContentState('@afc163')}
|
||||
onFocus={handleFocus}
|
||||
suggestions={['afc163', 'benjycui', 'yiminghe', 'RaoHai', '中文', 'にほんご']}
|
||||
/>
|
||||
);
|
||||
wrapper.instance().focus();
|
||||
jest.runAllTimers();
|
||||
expect(handleFocus).toBeCalled();
|
||||
});
|
||||
|
||||
it('basic suggestion', () => {
|
||||
const handleSearch = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Mention
|
||||
suggestions={['afc163', 'raohai']}
|
||||
onSearchChange={handleSearch}
|
||||
/>
|
||||
);
|
||||
wrapper.find('DraftEditorContents').simulate('focus');
|
||||
const ed = wrapper.find('.public-DraftEditor-content');
|
||||
ed.simulate('beforeInput', { data: '@a' });
|
||||
jest.runAllTimers();
|
||||
expect(handleSearch).toBeCalledWith('a', '@');
|
||||
});
|
||||
|
||||
it('change suggestions', () => {
|
||||
const container = mount(<div id="container" />);
|
||||
const wrapper = mount(
|
||||
<Mention suggestions={['afc163', 'raohai']} getSuggestionContainer={() => container.getDOMNode()} />
|
||||
);
|
||||
wrapper.find('DraftEditorContents').simulate('focus');
|
||||
const ed = wrapper.find('.public-DraftEditor-content');
|
||||
ed.simulate('beforeInput', { data: '@' });
|
||||
jest.runAllTimers();
|
||||
expect(container.getDOMNode().querySelectorAll('.ant-mention-dropdown-item').length).toBe(2);
|
||||
expect(container.getDOMNode().querySelectorAll('.ant-mention-dropdown-item')[0].innerHTML).toBe('afc163');
|
||||
wrapper.setState({ suggestions: ['yesmeck', 'yiminghe', 'lucy'] });
|
||||
jest.runAllTimers();
|
||||
expect(container.getDOMNode().querySelectorAll('.ant-mention-dropdown-item').length).toBe(3);
|
||||
expect(container.getDOMNode().querySelectorAll('.ant-mention-dropdown-item')[0].innerHTML).toBe('yesmeck');
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,8 @@ class SubMenu extends React.Component<any, any> {
|
||||
static contextTypes = {
|
||||
antdMenuTheme: PropTypes.string,
|
||||
};
|
||||
// fix issue:https://github.com/ant-design/ant-design/issues/8666
|
||||
static isSubMenu = 1;
|
||||
private subMenu: any;
|
||||
onKeyDown = (e: React.MouseEvent<HTMLElement>) => {
|
||||
this.subMenu.onKeyDown(e);
|
||||
|
||||
@@ -14,6 +14,23 @@ describe('Menu', () => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('If has select nested submenu item ,the menu items on the grandfather level should be highlight', () => {
|
||||
const wrapper = mount(
|
||||
<Menu defaultSelectedKeys={['1-3-2']} mode="vertical">
|
||||
<SubMenu key="1" title="submenu1">
|
||||
<Menu.Item key="1-1">Option 1</Menu.Item>
|
||||
<Menu.Item key="1-2">Option 2</Menu.Item>
|
||||
<SubMenu key="1-3" title="submenu1-3">
|
||||
<Menu.Item key="1-3-1">Option 3</Menu.Item>
|
||||
<Menu.Item key="1-3-2">Option 4</Menu.Item>
|
||||
</SubMenu>
|
||||
</SubMenu>
|
||||
<Menu.Item key="2">menu2</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
expect(wrapper.find('.ant-menu-submenu-selected').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should accept defaultOpenKeys in mode horizontal', () => {
|
||||
const wrapper = mount(
|
||||
<Menu defaultOpenKeys={['1']} mode="horizontal">
|
||||
|
||||
@@ -52,6 +52,7 @@ export interface MenuProps {
|
||||
subMenuCloseDelay?: number;
|
||||
subMenuOpenDelay?: number;
|
||||
getPopupContainer?: (triggerNode: Element) => HTMLElement;
|
||||
focusable?: boolean;
|
||||
}
|
||||
|
||||
export interface MenuState {
|
||||
@@ -63,10 +64,10 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
|
||||
static Item = Item;
|
||||
static SubMenu = SubMenu;
|
||||
static ItemGroup = ItemGroup;
|
||||
static defaultProps = {
|
||||
static defaultProps: Partial<MenuProps> = {
|
||||
prefixCls: 'ant-menu',
|
||||
className: '',
|
||||
theme: 'light' as MenuTheme, // or dark
|
||||
theme: 'light', // or dark
|
||||
focusable: false,
|
||||
};
|
||||
static childContextTypes = {
|
||||
|
||||
@@ -279,14 +279,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
> .@{menu-prefix-cls}-item > a {
|
||||
display: block;
|
||||
color: @menu-item-color;
|
||||
&:hover {
|
||||
color: @menu-highlight-color;
|
||||
> .@{menu-prefix-cls}-item {
|
||||
> a {
|
||||
display: block;
|
||||
color: @menu-item-color;
|
||||
&:hover {
|
||||
color: @menu-highlight-color;
|
||||
}
|
||||
&:before {
|
||||
bottom: -2px;
|
||||
}
|
||||
}
|
||||
&:before {
|
||||
bottom: -2px;
|
||||
&-selected > a {
|
||||
color: @menu-highlight-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,3 +61,14 @@ exports[`renders ./components/message/demo/other.md correctly 1`] = `
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/message/demo/thenable.md correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Display a sequence of message
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
@@ -85,6 +85,17 @@ describe('message', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should be called like promise', () => {
|
||||
jest.useRealTimers();
|
||||
const defaultDuration = 3;
|
||||
const now = Date.now();
|
||||
message.info('whatever').then(() => {
|
||||
// calculate the approximately duration value
|
||||
const aboutDuration = parseInt((Date.now() - now) / 1000, 10);
|
||||
expect(aboutDuration).toBe(defaultDuration);
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/8201
|
||||
it('should hide message correctly', () => {
|
||||
let hide;
|
||||
|
||||
28
components/message/demo/thenable.md
Normal file
28
components/message/demo/thenable.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
order: 5
|
||||
title:
|
||||
zh-CN: Promise 接口
|
||||
en-US: Promise interface
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以通过 then 接口在关闭后运行 callback 。以上用例将在每个 message 将要结束时通过 then 显示新的 message 。
|
||||
|
||||
## en-US
|
||||
`message` provides promise interface for `onClose`. The above example will display a new message when old message is about to finish.
|
||||
|
||||
````jsx
|
||||
import { message, Button } from 'antd';
|
||||
|
||||
const success = () => {
|
||||
message.loading('Action in progress..', 2.5)
|
||||
.then(() => message.success('Loading finished', 2.5))
|
||||
.then(() => message.info('Loading finished is finished', 2.5));
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<Button onClick={success}>Display a sequence of message</Button>
|
||||
, mountNode);
|
||||
````
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user