mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-11-30 11:08:45 +08:00
chore: Merge master branch and fix conflicts
This commit is contained in:
commit
f955620601
@ -1,7 +1,3 @@
|
||||
const commonGlobals = {
|
||||
gtag: true,
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
'airbnb',
|
||||
@ -38,7 +34,6 @@ module.exports = {
|
||||
{
|
||||
files: ['*.md'],
|
||||
globals: {
|
||||
...commonGlobals,
|
||||
React: true,
|
||||
ReactDOM: true,
|
||||
mountNode: true,
|
||||
@ -124,5 +119,7 @@ module.exports = {
|
||||
'unicorn/expiring-todo-comments': 2,
|
||||
'unicorn/no-abusive-eslint-disable': 2,
|
||||
},
|
||||
globals: commonGlobals,
|
||||
globals: {
|
||||
gtag: true,
|
||||
},
|
||||
};
|
||||
|
@ -15,6 +15,30 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.1.5
|
||||
|
||||
`2020-04-25`
|
||||
|
||||
- 🐞 Fix Button.Group align style. [#23590](https://github.com/ant-design/ant-design/pull/23590)
|
||||
- 🐞 Fix Select cannot trigger open by clicking arrow icon. [#23448](https://github.com/ant-design/ant-design/pull/23448)
|
||||
- 🐞 Fix Form fields shake when `@form-item-margin-bottom` is customize and switching the validing info. [#23436](https://github.com/ant-design/ant-design/pull/23436) [@yoyo837](https://github.com/yoyo837)
|
||||
- 🐞 Fix the first Divider render differently with others. [#23438](https://github.com/ant-design/ant-design/pull/23438)
|
||||
- 🐞 Fix nest ConfigProvider missing `prefixCls` value. [#23423](https://github.com/ant-design/ant-design/pull/23423)
|
||||
- 🐞 Fix Carousel tabbed Radio/Checkbox to non-active slide. [#23380](https://github.com/ant-design/ant-design/pull/23380)
|
||||
- 🐞 Fix Tree with virtual scroll frozen by quick `loadData`. [#23581](https://github.com/ant-design/ant-design/pull/23581)
|
||||
- 🐞 Fix Steps style in IE11 when direction is vertical. [#23561](https://github.com/ant-design/ant-design/pull/23561) [@AdrianoRuberto](https://github.com/AdrianoRuberto)
|
||||
- 🐞 Fix Input.Search height affected by `suffix` and `react key` warning. [#23527](https://github.com/ant-design/ant-design/pull/23527)
|
||||
- 🐞 Fix Menu behavior when hover on submenu gap. [#23511](https://github.com/ant-design/ant-design/pull/23511)
|
||||
- 🐞 Fix Tree custom icon missing when node is loading data. [#23494](https://github.com/ant-design/ant-design/pull/23494)
|
||||
- RTL
|
||||
- 🐞 Fix Alert RTL style when set both `showIcon` and `closable`. [#23526](https://github.com/ant-design/ant-design/pull/23526)
|
||||
- 🐞 Fix Button RTL style when loading. [#23399](https://github.com/ant-design/ant-design/pull/23399)
|
||||
- 🐞 Fix Collapse that icon position is incorrect in RTL. [#23445](https://github.com/ant-design/ant-design/pull/23445)
|
||||
- 🐞 Fix Select group label style in RTL. [#23404](https://github.com/ant-design/ant-design/pull/23404)
|
||||
- 🐞 Fix Statistic RTL style. [#23397](https://github.com/ant-design/ant-design/pull/23397)
|
||||
- TypeScript
|
||||
- 🐞 Fix type definition of `selections` for Table. [#23462](https://github.com/ant-design/ant-design/pull/23462) [@xiaoxintang](https://github.com/xiaoxintang)
|
||||
|
||||
## 4.1.4
|
||||
|
||||
`2020-04-18`
|
||||
|
@ -15,6 +15,30 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.1.5
|
||||
|
||||
`2020-04-25`
|
||||
|
||||
- 🐞 修复 Button.Group 中按钮没有对齐的问题。[#23590](https://github.com/ant-design/ant-design/pull/23590)
|
||||
- 🐞 修复 Select 箭头图标点击无法触发下拉的问题。[#23448](https://github.com/ant-design/ant-design/pull/23448)
|
||||
- 🐞 修复 Form 自定义 `@form-item-margin-bottom` 变量时表单校验抖动的问题。[#23436](https://github.com/ant-design/ant-design/pull/23436) [@yoyo837](https://github.com/yoyo837)
|
||||
- 🐞 修复第一个 Divider 渲染时样式不一致的问题。[#23438](https://github.com/ant-design/ant-design/pull/23438)
|
||||
- 🐞 修复嵌套 ConfigProvider 会丢失 `prefixCls` 值的问题。[#23423](https://github.com/ant-design/ant-design/pull/23423)
|
||||
- 🐞 修复 Carousel 键盘切换到非活跃 slide 上的 Radio/Checkbox 的问题。[#23380](https://github.com/ant-design/ant-design/pull/23380)
|
||||
- 🐞 修复 Tree 使用虚拟滚动时会因为 `loadData` 更新过快而锁死的问题。[#23581](https://github.com/ant-design/ant-design/pull/23581)
|
||||
- 🐞 修复 Steps 组件竖直展示时在 IE11 下样式错误的问题。[#23561](https://github.com/ant-design/ant-design/pull/23561) [@AdrianoRuberto](https://github.com/AdrianoRuberto)
|
||||
- 🐞 修复 Input.Search 高度被 `suffix` 撑高的问题和报 `react key` 重复警告的问题。[#23527](https://github.com/ant-design/ant-design/pull/23527)
|
||||
- 🐞 修复 Menu 鼠标移到缝隙处子菜单会消失的问题。[#23511](https://github.com/ant-design/ant-design/pull/23511)
|
||||
- 🐞 修复 Tree 自定义图标在加载状态下消失的问题。[#23494](https://github.com/ant-design/ant-design/pull/23494)
|
||||
- RTL
|
||||
- 🐞 修复 Alert 在 `showIcon` 和 `closable` 都存在时的 RTL 样式问题。[#23526](https://github.com/ant-design/ant-design/pull/23526)
|
||||
- 🐞 修复 Button 在 RTL 下 loading 样式不正确的问题。[#23399](https://github.com/ant-design/ant-design/pull/23399)
|
||||
- 🐞 修复 Collapse 在 RTL 下切换图标位置不正确的问题。[#23445](https://github.com/ant-design/ant-design/pull/23445)
|
||||
- 🐞 修复 Select 分组名称的 RTL 样式问题。[#23404](https://github.com/ant-design/ant-design/pull/23404)
|
||||
- 🐞 修复 Statistic 的 RTL 样式不正确的问题。[#23397](https://github.com/ant-design/ant-design/pull/23397)
|
||||
- TypeScript
|
||||
- 🐞 修复 Table 的 `selections` 类型定义。[#23462](https://github.com/ant-design/ant-design/pull/23462) [@xiaoxintang](https://github.com/xiaoxintang)
|
||||
|
||||
## 4.1.4
|
||||
|
||||
`2020-04-18`
|
||||
|
@ -33,7 +33,7 @@ Uma solução empresarial de design e biblioteca UI para React.
|
||||
|
||||
## 🖥 Suporte aos ambientes
|
||||
|
||||
- Navegadores modernos e Internet Explorer 11+ (com [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
|
||||
- Navegadores modernos e Internet Explorer 11 (com [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
|
||||
- Renderização no lado do servidor (server-side)
|
||||
- [Electron](https://www.electronjs.org/)
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
## 🖥 支持环境
|
||||
|
||||
- 现代浏览器和 IE11 及以上。
|
||||
- 现代浏览器和 IE11(需要 [polyfills](https://ant.design/docs/react/getting-started-cn#兼容性))。
|
||||
- 支持服务端渲染。
|
||||
- [Electron](https://www.electronjs.org/)
|
||||
|
||||
|
12
README.md
12
README.md
@ -14,7 +14,7 @@ An enterprise-class UI design language and React UI library.
|
||||
|
||||
[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![FOSSA Status][fossa-image]][fossa-url] [![Issues need help][help-wanted-image]][help-wanted-url]
|
||||
|
||||
[![Follow Twitter][twitter-image]][twitter-url] [![Gitter][gitter-english-image]][gitter-english-url] [![Gitter][gitter-chinese-image]][gitter-chinese-url]
|
||||
[![Follow Twitter][twitter-image]][twitter-url] [![Gitter][gitter-english-image]][gitter-english-url] [![Gitter][gitter-chinese-image]][gitter-chinese-url] [![[SemVer stability]][semver-stability-image]][semver-stability-url]
|
||||
|
||||
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
|
||||
[npm-url]: http://npmjs.org/package/antd
|
||||
@ -38,10 +38,12 @@ An enterprise-class UI design language and React UI library.
|
||||
[help-wanted-url]: https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22
|
||||
[twitter-image]: https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social
|
||||
[twitter-url]: https://twitter.com/AntDesignUI
|
||||
[gitter-english-image]: https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D
|
||||
[gitter-english-image]: https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D
|
||||
[gitter-english-url]: https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
|
||||
[gitter-chinese-image]: https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D
|
||||
[gitter-chinese-image]: https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D
|
||||
[gitter-chinese-url]: https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
[semver-stability-url]: https://dependabot.com/compatibility-score.html/?dependency-name=antd&package-manager=npm_and_yarn&new-version=latest
|
||||
[semver-stability-image]: https://api.dependabot.com/badges/compatibility_score?dependency-name=antd&package-manager=npm_and_yarn&target-version=latest&version-scheme=semver
|
||||
|
||||
</div>
|
||||
|
||||
@ -60,7 +62,7 @@ English | [Português](./README-pt_BR.md) | [简体中文](./README-zh_CN.md)
|
||||
|
||||
## 🖥 Environment Support
|
||||
|
||||
- Modern browsers and Internet Explorer 11+ (with [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
|
||||
- Modern browsers and Internet Explorer 11 (with [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
|
||||
- Server-side Rendering
|
||||
- [Electron](https://www.electronjs.org/)
|
||||
|
||||
@ -110,7 +112,7 @@ See [i18n](http://ant.design/docs/react/i18n).
|
||||
## 🔗 Links
|
||||
|
||||
- [Home page](http://ant.design/)
|
||||
- [Components](http://ant.design/docs/react/introduce)
|
||||
- [Components](https://ant.design/components/button/)
|
||||
- [Ant Design Pro](http://pro.ant.design/)
|
||||
- [Change Log](CHANGELOG.en-US.md)
|
||||
- [rc-components](http://react-component.github.io/)
|
||||
|
@ -1,71 +0,0 @@
|
||||
const __NULL__ = { notExist: true };
|
||||
|
||||
type ElementType<P> = {
|
||||
prototype: P;
|
||||
};
|
||||
|
||||
export function spyElementPrototypes<P extends {}>(Element: ElementType<P>, properties: P) {
|
||||
const propNames = Object.keys(properties);
|
||||
const originDescriptors = {};
|
||||
|
||||
propNames.forEach(propName => {
|
||||
const originDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, propName);
|
||||
originDescriptors[propName] = originDescriptor || __NULL__;
|
||||
|
||||
const spyProp = properties[propName];
|
||||
|
||||
if (typeof spyProp === 'function') {
|
||||
// If is a function
|
||||
Element.prototype[propName] = function spyFunc(...args) {
|
||||
return spyProp.call(this, originDescriptor, ...args);
|
||||
};
|
||||
} else {
|
||||
// Otherwise tread as a property
|
||||
Object.defineProperty(Element.prototype, propName, {
|
||||
...spyProp,
|
||||
set(value) {
|
||||
if (spyProp.set) {
|
||||
return spyProp.set.call(this, originDescriptor, value);
|
||||
}
|
||||
return originDescriptor.set(value);
|
||||
},
|
||||
get() {
|
||||
if (spyProp.get) {
|
||||
return spyProp.get.call(this, originDescriptor);
|
||||
}
|
||||
return originDescriptor.get();
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
mockRestore() {
|
||||
propNames.forEach(propName => {
|
||||
const originDescriptor = originDescriptors[propName];
|
||||
if (originDescriptor === __NULL__) {
|
||||
delete Element.prototype[propName];
|
||||
} else if (typeof originDescriptor === 'function') {
|
||||
Element.prototype[propName] = originDescriptor;
|
||||
} else {
|
||||
Object.defineProperty(Element.prototype, propName, originDescriptor);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
type FunctionPropertyNames<T> = {
|
||||
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
|
||||
}[keyof T] &
|
||||
string;
|
||||
|
||||
export function spyElementPrototype<P extends {}, K extends FunctionPropertyNames<P>>(
|
||||
Element: ElementType<P>,
|
||||
propName: K,
|
||||
property: P[K],
|
||||
) {
|
||||
return spyElementPrototypes(Element, {
|
||||
[propName]: property,
|
||||
});
|
||||
}
|
@ -9,6 +9,7 @@ interface Motion {
|
||||
motionEnter?: boolean;
|
||||
motionLeave?: boolean;
|
||||
motionLeaveImmediately?: boolean; // Trigger leave motion immediately
|
||||
motionDeadline?: number;
|
||||
removeOnLeave?: boolean;
|
||||
leavedClassName?: string;
|
||||
onAppearStart?: MotionFunc;
|
||||
@ -35,6 +36,7 @@ const collapseMotion: Motion = {
|
||||
onEnterActive: getRealHeight,
|
||||
onLeaveStart: getCurrentHeight,
|
||||
onLeaveActive: getCollapsedHeight,
|
||||
motionDeadline: 500,
|
||||
};
|
||||
|
||||
export default collapseMotion;
|
||||
|
@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Affix from '..';
|
||||
import { mount, ReactWrapper, HTMLAttributes } from 'enzyme';
|
||||
import ResizeObserverImpl from 'rc-resize-observer';
|
||||
import Affix, { AffixProps, AffixState } from '..';
|
||||
import { getObserverEntities } from '../utils';
|
||||
import Button from '../../button';
|
||||
import { spyElementPrototype } from '../../__tests__/util/domHook';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
const events: any = {};
|
||||
const events: Partial<Record<keyof HTMLElementEventMap, (ev: Partial<Event>) => void>> = {};
|
||||
|
||||
class AffixMounter extends React.Component<{
|
||||
offsetBottom?: number;
|
||||
@ -16,12 +16,14 @@ class AffixMounter extends React.Component<{
|
||||
}> {
|
||||
private container: HTMLDivElement;
|
||||
|
||||
private affix: Affix;
|
||||
public affix: Affix;
|
||||
|
||||
componentDidMount() {
|
||||
this.container.addEventListener = jest.fn().mockImplementation((event, cb) => {
|
||||
events[event] = cb;
|
||||
});
|
||||
this.container.addEventListener = jest
|
||||
.fn()
|
||||
.mockImplementation((event: keyof HTMLElementEventMap, cb: (ev: Partial<Event>) => void) => {
|
||||
events[event] = cb;
|
||||
});
|
||||
}
|
||||
|
||||
getTarget = () => this.container;
|
||||
@ -30,7 +32,7 @@ class AffixMounter extends React.Component<{
|
||||
return (
|
||||
<div
|
||||
ref={node => {
|
||||
this.container = node;
|
||||
this.container = node!;
|
||||
}}
|
||||
className="container"
|
||||
>
|
||||
@ -38,7 +40,7 @@ class AffixMounter extends React.Component<{
|
||||
className="fixed"
|
||||
target={this.getTarget}
|
||||
ref={ele => {
|
||||
this.affix = ele;
|
||||
this.affix = ele!;
|
||||
}}
|
||||
{...this.props}
|
||||
>
|
||||
@ -52,18 +54,19 @@ class AffixMounter extends React.Component<{
|
||||
describe('Affix Render', () => {
|
||||
rtlTest(Affix);
|
||||
|
||||
let wrapper;
|
||||
let domMock;
|
||||
const domMock = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect');
|
||||
let affixMounterWrapper: ReactWrapper<unknown, unknown, AffixMounter>;
|
||||
let affixWrapper: ReactWrapper<AffixProps, AffixState, Affix>;
|
||||
|
||||
const classRect: any = {
|
||||
const classRect: Record<string, DOMRect> = {
|
||||
container: {
|
||||
top: 0,
|
||||
bottom: 100,
|
||||
},
|
||||
} as DOMRect,
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
domMock = spyElementPrototype(HTMLElement, 'getBoundingClientRect', function mockBounding() {
|
||||
domMock.mockImplementation(function fn(this: HTMLElement) {
|
||||
return (
|
||||
classRect[this.className] || {
|
||||
top: 0,
|
||||
@ -77,11 +80,14 @@ describe('Affix Render', () => {
|
||||
domMock.mockRestore();
|
||||
});
|
||||
|
||||
const movePlaceholder = async top => {
|
||||
const movePlaceholder = async (top: number) => {
|
||||
classRect.fixed = {
|
||||
top,
|
||||
bottom: top,
|
||||
};
|
||||
} as DOMRect;
|
||||
if (events.scroll == null) {
|
||||
throw new Error('scroll should be set');
|
||||
}
|
||||
events.scroll({
|
||||
type: 'scroll',
|
||||
});
|
||||
@ -91,53 +97,53 @@ describe('Affix Render', () => {
|
||||
it('Anchor render perfectly', async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
wrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
|
||||
affixMounterWrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
|
||||
await movePlaceholder(-100);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
|
||||
await movePlaceholder(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
});
|
||||
|
||||
it('support offsetBottom', async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
wrapper = mount(<AffixMounter offsetBottom={0} />, {
|
||||
affixMounterWrapper = mount(<AffixMounter offsetBottom={0} />, {
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(300);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
|
||||
await movePlaceholder(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
|
||||
await movePlaceholder(300);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
});
|
||||
|
||||
it('updatePosition when offsetTop changed', async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
wrapper = mount(<AffixMounter offsetTop={0} />, {
|
||||
affixMounterWrapper = mount(<AffixMounter offsetTop={0} />, {
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(-100);
|
||||
expect(wrapper.instance().affix.state.affixStyle.top).toBe(0);
|
||||
wrapper.setProps({
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle?.top).toBe(0);
|
||||
affixMounterWrapper.setProps({
|
||||
offsetTop: 10,
|
||||
});
|
||||
await sleep(20);
|
||||
expect(wrapper.instance().affix.state.affixStyle.top).toBe(10);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle?.top).toBe(10);
|
||||
});
|
||||
|
||||
describe('updatePosition when target changed', () => {
|
||||
@ -145,11 +151,11 @@ describe('Affix Render', () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
const container = document.querySelector('#id') as HTMLDivElement;
|
||||
const getTarget = () => container;
|
||||
wrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||
wrapper.setProps({ target: null });
|
||||
expect(wrapper.instance().state.status).toBe(0);
|
||||
expect(wrapper.instance().state.affixStyle).toBe(undefined);
|
||||
expect(wrapper.instance().state.placeholderStyle).toBe(undefined);
|
||||
affixWrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||
affixWrapper.setProps({ target: () => null });
|
||||
expect(affixWrapper.instance().state.status).toBe(0);
|
||||
expect(affixWrapper.instance().state.affixStyle).toBe(undefined);
|
||||
expect(affixWrapper.instance().state.placeholderStyle).toBe(undefined);
|
||||
});
|
||||
|
||||
it('instance change', async () => {
|
||||
@ -157,53 +163,68 @@ describe('Affix Render', () => {
|
||||
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
let target = container;
|
||||
let target: HTMLDivElement | null = container;
|
||||
|
||||
const originLength = getObserverLength();
|
||||
const getTarget = () => target;
|
||||
wrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||
affixWrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||
await sleep(50);
|
||||
|
||||
expect(getObserverLength()).toBe(originLength + 1);
|
||||
target = null;
|
||||
wrapper.setProps({});
|
||||
wrapper.update();
|
||||
affixWrapper.setProps({});
|
||||
affixWrapper.update();
|
||||
await sleep(50);
|
||||
expect(getObserverLength()).toBe(originLength);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updatePosition when size changed', () => {
|
||||
function test(name, index) {
|
||||
it(name, async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
it.each([
|
||||
{ name: 'inner', index: 0 },
|
||||
{ name: 'outer', index: 1 },
|
||||
])(name, async ({ index }) => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
const updateCalled = jest.fn();
|
||||
wrapper = mount(<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />, {
|
||||
const updateCalled = jest.fn();
|
||||
affixMounterWrapper = mount(
|
||||
<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />,
|
||||
{
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
await sleep(20);
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(300);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
await sleep(20);
|
||||
wrapper.update();
|
||||
await movePlaceholder(300);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
await sleep(20);
|
||||
affixMounterWrapper.update();
|
||||
|
||||
// Mock trigger resize
|
||||
updateCalled.mockReset();
|
||||
wrapper
|
||||
.find('ResizeObserver')
|
||||
.at(index)
|
||||
.instance()
|
||||
.onResize([{ target: { getBoundingClientRect: () => ({ width: 99, height: 99 }) } }]);
|
||||
await sleep(20);
|
||||
// Mock trigger resize
|
||||
updateCalled.mockReset();
|
||||
const resizeObserverInstance: ReactWrapper<
|
||||
HTMLAttributes,
|
||||
unknown,
|
||||
ResizeObserverImpl
|
||||
> = affixMounterWrapper.find('ResizeObserver') as any;
|
||||
resizeObserverInstance
|
||||
.at(index)
|
||||
.instance()
|
||||
.onResize(
|
||||
[
|
||||
{
|
||||
target: {
|
||||
getBoundingClientRect: () => ({ width: 99, height: 99 }),
|
||||
} as Element,
|
||||
contentRect: {} as DOMRect,
|
||||
},
|
||||
],
|
||||
({} as unknown) as ResizeObserver,
|
||||
);
|
||||
await sleep(20);
|
||||
|
||||
expect(updateCalled).toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
|
||||
test('inner', 0);
|
||||
test('outer', 1);
|
||||
expect(updateCalled).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -709,7 +709,7 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-warning"
|
||||
class="ant-alert ant-alert-warning ant-alert-closable"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
@ -740,6 +740,32 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
|
||||
<span
|
||||
class="ant-alert-description"
|
||||
/>
|
||||
<button
|
||||
class="ant-alert-close-icon"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-error"
|
||||
@ -851,7 +877,7 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-with-description"
|
||||
class="ant-alert ant-alert-warning ant-alert-with-description ant-alert-closable"
|
||||
data-show="true"
|
||||
>
|
||||
<span
|
||||
@ -887,6 +913,32 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
|
||||
>
|
||||
This is a warning notice about copywriting.
|
||||
</span>
|
||||
<button
|
||||
class="ant-alert-close-icon"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert ant-alert-error ant-alert-with-description"
|
@ -39,20 +39,20 @@ describe('Alert', () => {
|
||||
|
||||
describe('data and aria props', () => {
|
||||
it('sets data attributes on input', () => {
|
||||
const wrapper = mount(<Alert data-test="test-id" data-id="12345" />);
|
||||
const wrapper = mount(<Alert data-test="test-id" data-id="12345" message={null} />);
|
||||
const input = wrapper.find('.ant-alert').getDOMNode();
|
||||
expect(input.getAttribute('data-test')).toBe('test-id');
|
||||
expect(input.getAttribute('data-id')).toBe('12345');
|
||||
});
|
||||
|
||||
it('sets aria attributes on input', () => {
|
||||
const wrapper = mount(<Alert aria-describedby="some-label" />);
|
||||
const wrapper = mount(<Alert aria-describedby="some-label" message={null} />);
|
||||
const input = wrapper.find('.ant-alert').getDOMNode();
|
||||
expect(input.getAttribute('aria-describedby')).toBe('some-label');
|
||||
});
|
||||
|
||||
it('sets role attribute on input', () => {
|
||||
const wrapper = mount(<Alert role="status" />);
|
||||
const wrapper = mount(<Alert role="status" message={null} />);
|
||||
const input = wrapper.find('.ant-alert').getDOMNode();
|
||||
expect(input.getAttribute('role')).toBe('status');
|
||||
});
|
||||
@ -60,6 +60,8 @@ describe('Alert', () => {
|
||||
|
||||
const testIt = process.env.REACT === '15' ? it.skip : it;
|
||||
testIt('ErrorBoundary', () => {
|
||||
// TODO: Change to @ts-expect-error once typescript is at 3.9
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line react/jsx-no-undef
|
||||
const ThrowError = () => <NotExisted />;
|
||||
const wrapper = mount(
|
||||
@ -83,12 +85,7 @@ describe('Alert', () => {
|
||||
);
|
||||
wrapper.find('.ant-alert').simulate('mouseenter');
|
||||
await sleep(0);
|
||||
expect(
|
||||
wrapper
|
||||
.find(Tooltip)
|
||||
.instance()
|
||||
.getPopupDomNode(),
|
||||
).toBeTruthy();
|
||||
expect(wrapper.find<Tooltip>(Tooltip).instance().getPopupDomNode()).toBeTruthy();
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
@ -104,12 +101,7 @@ describe('Alert', () => {
|
||||
);
|
||||
wrapper.find('.ant-alert').simulate('click');
|
||||
await sleep(0);
|
||||
expect(
|
||||
wrapper
|
||||
.find(Popconfirm)
|
||||
.instance()
|
||||
.getPopupDomNode(),
|
||||
).toBeTruthy();
|
||||
expect(wrapper.find<Popconfirm>(Popconfirm).instance().getPopupDomNode()).toBeTruthy();
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
});
|
@ -20,7 +20,7 @@ ReactDOM.render(
|
||||
<div>
|
||||
<Alert message="Success Tips" type="success" showIcon />
|
||||
<Alert message="Informational Notes" type="info" showIcon />
|
||||
<Alert message="Warning" type="warning" showIcon />
|
||||
<Alert message="Warning" type="warning" showIcon closable />
|
||||
<Alert message="Error" type="error" showIcon />
|
||||
<Alert
|
||||
message="Success Tips"
|
||||
@ -39,6 +39,7 @@ ReactDOM.render(
|
||||
description="This is a warning notice about copywriting."
|
||||
type="warning"
|
||||
showIcon
|
||||
closable
|
||||
/>
|
||||
<Alert
|
||||
message="Error"
|
||||
|
@ -37,6 +37,8 @@ export interface AlertProps {
|
||||
afterClose?: () => void;
|
||||
/** Whether to show icon */
|
||||
showIcon?: boolean;
|
||||
/** https://www.w3.org/TR/2014/REC-html5-20141028/dom.html#aria-role-attribute */
|
||||
role?: string;
|
||||
style?: React.CSSProperties;
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
|
@ -188,4 +188,4 @@
|
||||
}
|
||||
}
|
||||
|
||||
@import './rtl.less';
|
||||
@import './rtl';
|
||||
|
@ -4,13 +4,20 @@
|
||||
@alert-prefix-cls: ~'@{ant-prefix}-alert';
|
||||
|
||||
.@{alert-prefix-cls} {
|
||||
&-rtl {
|
||||
&&-rtl {
|
||||
padding: 8px 37px 8px 15px;
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
&&-closable {
|
||||
.@{alert-prefix-cls}-rtl& {
|
||||
.@{alert-prefix-cls}.@{alert-prefix-cls}-rtl& {
|
||||
padding-right: 37px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
&&-no-icon&-closable {
|
||||
.@{alert-prefix-cls}.@{alert-prefix-cls}-rtl& {
|
||||
padding-right: 15px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
@ -30,14 +37,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-with-description {
|
||||
.@{alert-prefix-cls}-rtl& {
|
||||
&-with-description,
|
||||
&-with-description&-closable {
|
||||
.@{alert-prefix-cls}.@{alert-prefix-cls}-rtl& {
|
||||
padding: 15px 64px 15px 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&-with-description&-no-icon {
|
||||
.@{alert-prefix-cls}-rtl& {
|
||||
.@{alert-prefix-cls}.@{alert-prefix-cls}-rtl& {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,29 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Anchor from '..';
|
||||
import { spyElementPrototypes } from '../../__tests__/util/domHook';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
const { Link } = Anchor;
|
||||
|
||||
describe('Anchor Render', () => {
|
||||
const getBoundingClientRectMock = jest.fn(() => ({
|
||||
width: 100,
|
||||
height: 100,
|
||||
top: 1000,
|
||||
}));
|
||||
const getClientRectsMock = jest.fn(() => ({
|
||||
length: 1,
|
||||
}));
|
||||
const headingSpy = spyElementPrototypes(HTMLHeadingElement, {
|
||||
getBoundingClientRect: getBoundingClientRectMock,
|
||||
getClientRects: getClientRectsMock,
|
||||
const getBoundingClientRectMock = jest.spyOn(
|
||||
HTMLHeadingElement.prototype,
|
||||
'getBoundingClientRect',
|
||||
);
|
||||
const getClientRectsMock = jest.spyOn(HTMLHeadingElement.prototype, 'getClientRects');
|
||||
|
||||
beforeAll(() => {
|
||||
getBoundingClientRectMock.mockReturnValue({
|
||||
width: 100,
|
||||
height: 100,
|
||||
top: 1000,
|
||||
});
|
||||
getClientRectsMock.mockReturnValue({ length: 1 });
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
headingSpy.mockRestore();
|
||||
getBoundingClientRectMock.mockRestore();
|
||||
getClientRectsMock.mockRestore();
|
||||
});
|
||||
|
||||
it('Anchor render perfectly', () => {
|
||||
|
@ -50,73 +50,50 @@ export interface ScrollNumberState {
|
||||
count?: string | number | null;
|
||||
}
|
||||
|
||||
class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState> {
|
||||
static defaultProps = {
|
||||
count: null,
|
||||
onAnimated() {},
|
||||
};
|
||||
const ScrollNumber: React.FC<ScrollNumberProps> = props => {
|
||||
const [animateStarted, setAnimateStarted] = React.useState(true);
|
||||
const [count, setCount] = React.useState(props.count);
|
||||
const [prevCount, setPrevCount] = React.useState(props.count);
|
||||
const [lastCount, setLastCount] = React.useState(props.count);
|
||||
|
||||
static getDerivedStateFromProps(nextProps: ScrollNumberProps, nextState: ScrollNumberState) {
|
||||
if ('count' in nextProps) {
|
||||
if (nextState.count === nextProps.count) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
animateStarted: true,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
if (prevCount !== props.count) {
|
||||
setAnimateStarted(true);
|
||||
setPrevCount(props.count);
|
||||
}
|
||||
|
||||
lastCount?: string | number | null;
|
||||
|
||||
private timeout?: number;
|
||||
|
||||
constructor(props: ScrollNumberProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
animateStarted: true,
|
||||
count: props.count,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(_: any, prevState: ScrollNumberState) {
|
||||
this.lastCount = prevState.count;
|
||||
const { animateStarted } = this.state;
|
||||
React.useEffect(() => {
|
||||
setLastCount(count);
|
||||
let timeout: number;
|
||||
if (animateStarted) {
|
||||
this.clearTimeout();
|
||||
// Let browser has time to reset the scroller before actually
|
||||
// performing the transition.
|
||||
this.timeout = setTimeout(() => {
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState(
|
||||
(__, props) => ({
|
||||
animateStarted: false,
|
||||
count: props.count,
|
||||
}),
|
||||
this.onAnimated,
|
||||
);
|
||||
timeout = setTimeout(() => {
|
||||
setAnimateStarted(false);
|
||||
setCount(props.count);
|
||||
if (props.onAnimated) {
|
||||
props.onAnimated();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
};
|
||||
}, [animateStarted, count, props.count, props.onAnimated]);
|
||||
|
||||
componentWillUnmount() {
|
||||
this.clearTimeout();
|
||||
}
|
||||
|
||||
getPositionByNum(num: number, i: number) {
|
||||
const { count } = this.state;
|
||||
const getPositionByNum = (num: number, i: number) => {
|
||||
const currentCount = Math.abs(Number(count));
|
||||
const lastCount = Math.abs(Number(this.lastCount));
|
||||
const currentDigit = Math.abs(getNumberArray(this.state.count)[i] as number);
|
||||
const lastDigit = Math.abs(getNumberArray(this.lastCount)[i] as number);
|
||||
const lstCount = Math.abs(Number(lastCount));
|
||||
const currentDigit = Math.abs(getNumberArray(count)[i] as number);
|
||||
const lastDigit = Math.abs(getNumberArray(lstCount)[i] as number);
|
||||
|
||||
if (this.state.animateStarted) {
|
||||
if (animateStarted) {
|
||||
return 10 + num;
|
||||
}
|
||||
|
||||
// 同方向则在同一侧切换数字
|
||||
if (currentCount > lastCount) {
|
||||
if (currentCount > lstCount) {
|
||||
if (currentDigit >= lastDigit) {
|
||||
return 10 + num;
|
||||
}
|
||||
@ -126,20 +103,12 @@ class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState>
|
||||
return 10 + num;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
onAnimated = () => {
|
||||
const { onAnimated } = this.props;
|
||||
if (onAnimated) {
|
||||
onAnimated();
|
||||
}
|
||||
};
|
||||
|
||||
renderCurrentNumber(prefixCls: string, num: number | string, i: number) {
|
||||
const renderCurrentNumber = (prefixCls: string, num: number | string, i: number) => {
|
||||
if (typeof num === 'number') {
|
||||
const position = this.getPositionByNum(num, i);
|
||||
const removeTransition =
|
||||
this.state.animateStarted || getNumberArray(this.lastCount)[i] === undefined;
|
||||
const position = getPositionByNum(num, i);
|
||||
const removeTransition = animateStarted || getNumberArray(lastCount)[i] === undefined;
|
||||
return React.createElement(
|
||||
'span',
|
||||
{
|
||||
@ -161,19 +130,18 @@ class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState>
|
||||
{num}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderNumberElement(prefixCls: string) {
|
||||
const { count } = this.state;
|
||||
const renderNumberElement = (prefixCls: string) => {
|
||||
if (count && Number(count) % 1 === 0) {
|
||||
return getNumberArray(count)
|
||||
.map((num, i) => this.renderCurrentNumber(prefixCls, num, i))
|
||||
.map((num, i) => renderCurrentNumber(prefixCls, num, i))
|
||||
.reverse();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
renderScrollNumber = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const renderScrollNumber = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
@ -181,9 +149,9 @@ class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState>
|
||||
title,
|
||||
component = 'sup',
|
||||
displayComponent,
|
||||
} = this.props;
|
||||
} = props;
|
||||
// fix https://fb.me/react-unknown-prop
|
||||
const restProps = omit(this.props, [
|
||||
const restProps = omit(props, [
|
||||
'count',
|
||||
'onAnimated',
|
||||
'component',
|
||||
@ -214,19 +182,15 @@ class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState>
|
||||
),
|
||||
});
|
||||
}
|
||||
return React.createElement(component as any, newProps, this.renderNumberElement(prefixCls));
|
||||
return React.createElement(component as any, newProps, renderNumberElement(prefixCls));
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderScrollNumber}</ConfigConsumer>;
|
||||
}
|
||||
return <ConfigConsumer>{renderScrollNumber}</ConfigConsumer>;
|
||||
};
|
||||
|
||||
private clearTimeout(): void {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
ScrollNumber.defaultProps = {
|
||||
count: null,
|
||||
onAnimated() {},
|
||||
};
|
||||
|
||||
export default ScrollNumber;
|
||||
|
@ -32,40 +32,47 @@ function isPresetColor(color?: string): boolean {
|
||||
return (PresetColorTypes as any[]).indexOf(color) !== -1;
|
||||
}
|
||||
|
||||
export default class Badge extends React.Component<BadgeProps, any> {
|
||||
static defaultProps = {
|
||||
count: null,
|
||||
showZero: false,
|
||||
dot: false,
|
||||
overflowCount: 99,
|
||||
};
|
||||
|
||||
getNumberedDisplayCount() {
|
||||
const { count, overflowCount } = this.props;
|
||||
const Badge: React.FC<BadgeProps> = props => {
|
||||
const getNumberedDisplayCount = () => {
|
||||
const { count, overflowCount } = props;
|
||||
const displayCount =
|
||||
(count as number) > (overflowCount as number) ? `${overflowCount}+` : count;
|
||||
return displayCount as string | number | null;
|
||||
}
|
||||
};
|
||||
|
||||
getDisplayCount() {
|
||||
const isDot = this.isDot();
|
||||
const hasStatus = (): boolean => {
|
||||
const { status, color } = props;
|
||||
return !!status || !!color;
|
||||
};
|
||||
|
||||
const isZero = () => {
|
||||
const numberedDisplayCount = getNumberedDisplayCount();
|
||||
return numberedDisplayCount === '0' || numberedDisplayCount === 0;
|
||||
};
|
||||
|
||||
const isDot = () => {
|
||||
const { dot } = props;
|
||||
return (dot && !isZero()) || hasStatus();
|
||||
};
|
||||
|
||||
const getDisplayCount = () => {
|
||||
// dot mode don't need count
|
||||
if (isDot) {
|
||||
if (isDot()) {
|
||||
return '';
|
||||
}
|
||||
return this.getNumberedDisplayCount();
|
||||
}
|
||||
return getNumberedDisplayCount();
|
||||
};
|
||||
|
||||
getScrollNumberTitle() {
|
||||
const { title, count } = this.props;
|
||||
const getScrollNumberTitle = () => {
|
||||
const { title, count } = props;
|
||||
if (title) {
|
||||
return title;
|
||||
}
|
||||
return typeof count === 'string' || typeof count === 'number' ? count : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
getStyleWithOffset() {
|
||||
const { offset, style } = this.props;
|
||||
const getStyleWithOffset = () => {
|
||||
const { offset, style } = props;
|
||||
return offset
|
||||
? {
|
||||
right: -parseInt(offset[0] as string, 10),
|
||||
@ -73,79 +80,61 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
...style,
|
||||
}
|
||||
: style;
|
||||
}
|
||||
};
|
||||
|
||||
getBadgeClassName(prefixCls: string, direction: string = 'ltr') {
|
||||
const { className, children } = this.props;
|
||||
const getBadgeClassName = (prefixCls: string, direction: string = 'ltr') => {
|
||||
const { className, children } = props;
|
||||
return classNames(className, prefixCls, {
|
||||
[`${prefixCls}-status`]: this.hasStatus(),
|
||||
[`${prefixCls}-status`]: hasStatus(),
|
||||
[`${prefixCls}-not-a-wrapper`]: !children,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
}) as string;
|
||||
}
|
||||
};
|
||||
|
||||
hasStatus(): boolean {
|
||||
const { status, color } = this.props;
|
||||
return !!status || !!color;
|
||||
}
|
||||
|
||||
isZero() {
|
||||
const numberedDisplayCount = this.getNumberedDisplayCount();
|
||||
return numberedDisplayCount === '0' || numberedDisplayCount === 0;
|
||||
}
|
||||
|
||||
isDot() {
|
||||
const { dot } = this.props;
|
||||
const isZero = this.isZero();
|
||||
return (dot && !isZero) || this.hasStatus();
|
||||
}
|
||||
|
||||
isHidden() {
|
||||
const { showZero } = this.props;
|
||||
const displayCount = this.getDisplayCount();
|
||||
const isZero = this.isZero();
|
||||
const isDot = this.isDot();
|
||||
const isHidden = () => {
|
||||
const { showZero } = props;
|
||||
const displayCount = getDisplayCount();
|
||||
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
|
||||
return (isEmpty || (isZero && !showZero)) && !isDot;
|
||||
}
|
||||
return (isEmpty || (isZero() && !showZero)) && !isDot();
|
||||
};
|
||||
|
||||
renderStatusText(prefixCls: string) {
|
||||
const { text } = this.props;
|
||||
const hidden = this.isHidden();
|
||||
const renderStatusText = (prefixCls: string) => {
|
||||
const { text } = props;
|
||||
const hidden = isHidden();
|
||||
return hidden || !text ? null : <span className={`${prefixCls}-status-text`}>{text}</span>;
|
||||
}
|
||||
};
|
||||
|
||||
renderDisplayComponent() {
|
||||
const { count } = this.props;
|
||||
const renderDisplayComponent = () => {
|
||||
const { count } = props;
|
||||
const customNode = count as React.ReactElement<any>;
|
||||
if (!customNode || typeof customNode !== 'object') {
|
||||
return undefined;
|
||||
}
|
||||
return React.cloneElement(customNode, {
|
||||
style: {
|
||||
...this.getStyleWithOffset(),
|
||||
...getStyleWithOffset(),
|
||||
...(customNode.props && customNode.props.style),
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderBadgeNumber(prefixCls: string, scrollNumberPrefixCls: string) {
|
||||
const { status, count, color } = this.props;
|
||||
const renderBadgeNumber = (prefixCls: string, scrollNumberPrefixCls: string) => {
|
||||
const { status, count, color } = props;
|
||||
|
||||
const displayCount = this.getDisplayCount();
|
||||
const isDot = this.isDot();
|
||||
const hidden = this.isHidden();
|
||||
const displayCount = getDisplayCount();
|
||||
const dot = isDot();
|
||||
const hidden = isHidden();
|
||||
|
||||
const scrollNumberCls = classNames({
|
||||
[`${prefixCls}-dot`]: isDot,
|
||||
[`${prefixCls}-count`]: !isDot,
|
||||
[`${prefixCls}-dot`]: dot,
|
||||
[`${prefixCls}-count`]: !dot,
|
||||
[`${prefixCls}-multiple-words`]:
|
||||
!isDot && count && count.toString && count.toString().length > 1,
|
||||
!dot && count && count.toString && count.toString().length > 1,
|
||||
[`${prefixCls}-status-${status}`]: !!status,
|
||||
[`${prefixCls}-status-${color}`]: isPresetColor(color),
|
||||
});
|
||||
|
||||
let statusStyle: React.CSSProperties | undefined = this.getStyleWithOffset();
|
||||
let statusStyle: React.CSSProperties | undefined = getStyleWithOffset();
|
||||
if (color && !isPresetColor(color)) {
|
||||
statusStyle = statusStyle || {};
|
||||
statusStyle.background = color;
|
||||
@ -157,15 +146,15 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
data-show={!hidden}
|
||||
className={scrollNumberCls}
|
||||
count={displayCount}
|
||||
displayComponent={this.renderDisplayComponent()} // <Badge status="success" count={<Icon type="xxx" />}></Badge>
|
||||
title={this.getScrollNumberTitle()}
|
||||
displayComponent={renderDisplayComponent()} // <Badge status="success" count={<Icon type="xxx" />}></Badge>
|
||||
title={getScrollNumberTitle()}
|
||||
style={statusStyle}
|
||||
key="scrollNumber"
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderBadge = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const renderBadge = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
scrollNumberPrefixCls: customizeScrollNumberPrefixCls,
|
||||
@ -174,7 +163,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
text,
|
||||
color,
|
||||
...restProps
|
||||
} = this.props;
|
||||
} = props;
|
||||
const omitArr = [
|
||||
'count',
|
||||
'showZero',
|
||||
@ -189,11 +178,11 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
const prefixCls = getPrefixCls('badge', customizePrefixCls);
|
||||
const scrollNumberPrefixCls = getPrefixCls('scroll-number', customizeScrollNumberPrefixCls);
|
||||
|
||||
const scrollNumber = this.renderBadgeNumber(prefixCls, scrollNumberPrefixCls);
|
||||
const statusText = this.renderStatusText(prefixCls);
|
||||
const scrollNumber = renderBadgeNumber(prefixCls, scrollNumberPrefixCls);
|
||||
const statusText = renderStatusText(prefixCls);
|
||||
|
||||
const statusCls = classNames({
|
||||
[`${prefixCls}-status-dot`]: this.hasStatus(),
|
||||
[`${prefixCls}-status-dot`]: hasStatus(),
|
||||
[`${prefixCls}-status-${status}`]: !!status,
|
||||
[`${prefixCls}-status-${color}`]: isPresetColor(color),
|
||||
});
|
||||
@ -203,13 +192,13 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
}
|
||||
|
||||
// <Badge status="success" />
|
||||
if (!children && this.hasStatus()) {
|
||||
const styleWithOffset = this.getStyleWithOffset();
|
||||
if (!children && hasStatus()) {
|
||||
const styleWithOffset = getStyleWithOffset();
|
||||
const statusTextColor = styleWithOffset && styleWithOffset.color;
|
||||
return (
|
||||
<span
|
||||
{...omit(restProps, omitArr)}
|
||||
className={this.getBadgeClassName(prefixCls, direction)}
|
||||
className={getBadgeClassName(prefixCls, direction)}
|
||||
style={styleWithOffset}
|
||||
>
|
||||
<span className={statusCls} style={statusStyle} />
|
||||
@ -221,7 +210,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
}
|
||||
|
||||
return (
|
||||
<span {...omit(restProps, omitArr)} className={this.getBadgeClassName(prefixCls, direction)}>
|
||||
<span {...omit(restProps, omitArr)} className={getBadgeClassName(prefixCls, direction)}>
|
||||
{children}
|
||||
<Animate
|
||||
component=""
|
||||
@ -236,7 +225,14 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderBadge}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
return <ConfigConsumer>{renderBadge}</ConfigConsumer>;
|
||||
};
|
||||
|
||||
Badge.defaultProps = {
|
||||
count: null,
|
||||
showZero: false,
|
||||
dot: false,
|
||||
overflowCount: 99,
|
||||
};
|
||||
|
||||
export default Badge;
|
||||
|
@ -531,8 +531,66 @@ exports[`renders ./components/button/demo/legacy-group.md correctly 1`] = `
|
||||
Button 2
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="ant-tooltip-disabled-compatible-wrapper"
|
||||
style="display:inline-block;cursor:not-allowed"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-icon-only"
|
||||
disabled=""
|
||||
style="pointer-events:none"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="download"
|
||||
class="anticon anticon-download"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="download"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-icon-only"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="download"
|
||||
class="anticon anticon-download"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="download"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<div
|
||||
class="ant-btn-group"
|
||||
@ -553,8 +611,66 @@ exports[`renders ./components/button/demo/legacy-group.md correctly 1`] = `
|
||||
Button 2
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="ant-tooltip-disabled-compatible-wrapper"
|
||||
style="display:inline-block;cursor:not-allowed"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-icon-only"
|
||||
disabled=""
|
||||
style="pointer-events:none"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="download"
|
||||
class="anticon anticon-download"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="download"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-icon-only"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="download"
|
||||
class="anticon anticon-download"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="download"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<div
|
||||
class="ant-btn-group ant-btn-group-lg"
|
||||
@ -575,6 +691,63 @@ exports[`renders ./components/button/demo/legacy-group.md correctly 1`] = `
|
||||
Button 2
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="ant-tooltip-disabled-compatible-wrapper"
|
||||
style="display:inline-block;cursor:not-allowed"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-icon-only"
|
||||
disabled=""
|
||||
style="pointer-events:none"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="download"
|
||||
class="anticon anticon-download"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="download"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-icon-only"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="download"
|
||||
class="anticon anticon-download"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="download"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,7 +15,8 @@ Debug usage
|
||||
Debug usage
|
||||
|
||||
```jsx
|
||||
import { Button } from 'antd';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import { DownloadOutlined } from '@ant-design/icons';
|
||||
|
||||
function getGroup(props) {
|
||||
return (
|
||||
@ -23,6 +24,12 @@ function getGroup(props) {
|
||||
<Button.Group {...props}>
|
||||
<Button type="primary">Button 1</Button>
|
||||
<Button type="primary">Button 2</Button>
|
||||
<Tooltip title="Tooltip">
|
||||
<Button type="primary" icon={<DownloadOutlined />} disabled />
|
||||
</Tooltip>
|
||||
<Tooltip title="Tooltip">
|
||||
<Button type="primary" icon={<DownloadOutlined />} />
|
||||
</Tooltip>
|
||||
</Button.Group>
|
||||
</div>
|
||||
);
|
||||
@ -31,9 +38,17 @@ function getGroup(props) {
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
{getGroup({ size: 'small' })}
|
||||
<br />
|
||||
{getGroup()}
|
||||
<br />
|
||||
{getGroup({ size: 'large' })}
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
```css
|
||||
#components-button-demo-legacy-group .ant-btn {
|
||||
margin: 0;
|
||||
}
|
||||
```
|
||||
|
@ -165,7 +165,7 @@
|
||||
}
|
||||
.button-group-base(@btnClassName) {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
> .@{btnClassName},
|
||||
> span > .@{btnClassName} {
|
||||
position: relative;
|
||||
@ -179,7 +179,7 @@
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
> .@{btnClassName}-icon-only {
|
||||
.@{btnClassName}-icon-only {
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
// size
|
||||
@ -187,7 +187,7 @@
|
||||
&-lg > span > .@{btnClassName} {
|
||||
.button-size(@btn-height-lg; @btn-padding-horizontal-lg; @btn-font-size-lg; 0);
|
||||
}
|
||||
&-lg > .@{btnClassName}.@{btnClassName}-icon-only {
|
||||
&-lg .@{btnClassName}.@{btnClassName}-icon-only {
|
||||
.square(@btn-height-lg);
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
@ -199,7 +199,7 @@
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
}
|
||||
&-sm > .@{btnClassName}.@{btnClassName}-icon-only {
|
||||
&-sm .@{btnClassName}.@{btnClassName}-icon-only {
|
||||
.square(@btn-height-sm);
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
|
||||
function getAction(actions: React.ReactNode[]) {
|
||||
if (!actions || !actions.length) {
|
||||
@ -32,72 +32,68 @@ export interface CommentProps {
|
||||
datetime?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default class Comment extends React.Component<CommentProps, {}> {
|
||||
renderNested = (prefixCls: string, children: any) => {
|
||||
return <div className={classNames(`${prefixCls}-nested`)}>{children}</div>;
|
||||
const Comment: React.FC<CommentProps> = ({
|
||||
actions,
|
||||
author,
|
||||
avatar,
|
||||
children,
|
||||
className,
|
||||
content,
|
||||
prefixCls: customizePrefixCls,
|
||||
style,
|
||||
datetime,
|
||||
...otherProps
|
||||
}) => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
|
||||
const renderNested = (prefixCls: string, nestedChildren: any) => {
|
||||
return <div className={classNames(`${prefixCls}-nested`)}>{nestedChildren}</div>;
|
||||
};
|
||||
|
||||
renderComment = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const {
|
||||
actions,
|
||||
author,
|
||||
avatar,
|
||||
children,
|
||||
className,
|
||||
content,
|
||||
prefixCls: customizePrefixCls,
|
||||
style,
|
||||
datetime,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
const prefixCls = getPrefixCls('comment', customizePrefixCls);
|
||||
|
||||
const prefixCls = getPrefixCls('comment', customizePrefixCls);
|
||||
const avatarDom = (
|
||||
<div className={`${prefixCls}-avatar`}>
|
||||
{typeof avatar === 'string' ? <img src={avatar} alt="comment-avatar" /> : avatar}
|
||||
</div>
|
||||
);
|
||||
|
||||
const avatarDom = (
|
||||
<div className={`${prefixCls}-avatar`}>
|
||||
{typeof avatar === 'string' ? <img src={avatar} alt="comment-avatar" /> : avatar}
|
||||
</div>
|
||||
);
|
||||
const actionDom =
|
||||
actions && actions.length ? (
|
||||
<ul className={`${prefixCls}-actions`}>{getAction(actions)}</ul>
|
||||
) : null;
|
||||
|
||||
const actionDom =
|
||||
actions && actions.length ? (
|
||||
<ul className={`${prefixCls}-actions`}>{getAction(actions)}</ul>
|
||||
) : null;
|
||||
const authorContent = (
|
||||
<div className={`${prefixCls}-content-author`}>
|
||||
{author && <span className={`${prefixCls}-content-author-name`}>{author}</span>}
|
||||
{datetime && <span className={`${prefixCls}-content-author-time`}>{datetime}</span>}
|
||||
</div>
|
||||
);
|
||||
|
||||
const authorContent = (
|
||||
<div className={`${prefixCls}-content-author`}>
|
||||
{author && <span className={`${prefixCls}-content-author-name`}>{author}</span>}
|
||||
{datetime && <span className={`${prefixCls}-content-author-time`}>{datetime}</span>}
|
||||
</div>
|
||||
);
|
||||
const contentDom = (
|
||||
<div className={`${prefixCls}-content`}>
|
||||
{authorContent}
|
||||
<div className={`${prefixCls}-content-detail`}>{content}</div>
|
||||
{actionDom}
|
||||
</div>
|
||||
);
|
||||
|
||||
const contentDom = (
|
||||
<div className={`${prefixCls}-content`}>
|
||||
{authorContent}
|
||||
<div className={`${prefixCls}-content-detail`}>{content}</div>
|
||||
{actionDom}
|
||||
</div>
|
||||
);
|
||||
const comment = (
|
||||
<div className={`${prefixCls}-inner`}>
|
||||
{avatarDom}
|
||||
{contentDom}
|
||||
</div>
|
||||
);
|
||||
|
||||
const comment = (
|
||||
<div className={`${prefixCls}-inner`}>
|
||||
{avatarDom}
|
||||
{contentDom}
|
||||
</div>
|
||||
);
|
||||
const cls = classNames(prefixCls, className, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
});
|
||||
return (
|
||||
<div {...otherProps} className={cls} style={style}>
|
||||
{comment}
|
||||
{children ? renderNested(prefixCls, children) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const cls = classNames(prefixCls, className, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
});
|
||||
return (
|
||||
<div {...otherProps} className={cls} style={style}>
|
||||
{comment}
|
||||
{children ? this.renderNested(prefixCls, children) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderComment}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
export default Comment;
|
||||
|
28
components/config-provider/__tests__/container.test.js
Normal file
28
components/config-provider/__tests__/container.test.js
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import ConfigProvider from '..';
|
||||
import DatePicker from '../../date-picker';
|
||||
import Slider from '../../slider';
|
||||
|
||||
describe('ConfigProvider.GetPopupContainer', () => {
|
||||
it('Datepicker', () => {
|
||||
const getPopupContainer = jest.fn(node => node.parentNode);
|
||||
mount(
|
||||
<ConfigProvider getPopupContainer={getPopupContainer}>
|
||||
<DatePicker open />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(getPopupContainer).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Slider', () => {
|
||||
const getPopupContainer = jest.fn(node => node.parentNode);
|
||||
const wrapper = mount(
|
||||
<ConfigProvider getPopupContainer={getPopupContainer}>
|
||||
<Slider />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
wrapper.find('.ant-slider-handle').first().simulate('mouseenter');
|
||||
expect(getPopupContainer).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -8,7 +8,7 @@ import focusTest from '../../../tests/shared/focusTest';
|
||||
describe('DatePicker', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
focusTest(DatePicker, true);
|
||||
focusTest(DatePicker, { refFocus: true });
|
||||
|
||||
beforeEach(() => {
|
||||
MockDate.set(moment('2016-11-22'));
|
||||
|
@ -9,7 +9,7 @@ import focusTest from '../../../tests/shared/focusTest';
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
describe('RangePicker', () => {
|
||||
focusTest(RangePicker, true);
|
||||
focusTest(RangePicker, { refFocus: true });
|
||||
|
||||
beforeEach(() => {
|
||||
setMockDate();
|
||||
|
@ -15,7 +15,7 @@ describe('WeekPicker', () => {
|
||||
resetMockDate();
|
||||
});
|
||||
|
||||
focusTest(WeekPicker, true);
|
||||
focusTest(WeekPicker, { refFocus: true });
|
||||
|
||||
it('should support style prop', () => {
|
||||
const wrapper = mount(<WeekPicker style={{ width: 400 }} />);
|
||||
|
@ -49,9 +49,10 @@ export default function generateRangePicker<DateType>(
|
||||
};
|
||||
|
||||
renderPicker = (locale: PickerLocale) => {
|
||||
const { getPrefixCls, direction } = this.context;
|
||||
const { getPrefixCls, direction, getPopupContainer } = this.context;
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
getPopupContainer: customGetPopupContainer,
|
||||
className,
|
||||
size: customizeSize,
|
||||
bordered = true,
|
||||
@ -94,6 +95,7 @@ export default function generateRangePicker<DateType>(
|
||||
{...additionalOverrideProps}
|
||||
locale={locale!.lang}
|
||||
prefixCls={prefixCls}
|
||||
getPopupContainer={customGetPopupContainer || getPopupContainer}
|
||||
generateConfig={generateConfig}
|
||||
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
|
||||
nextIcon={<span className={`${prefixCls}-next-icon`} />}
|
||||
|
@ -62,9 +62,10 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
};
|
||||
|
||||
renderPicker = (locale: PickerLocale) => {
|
||||
const { getPrefixCls, direction } = this.context;
|
||||
const { getPrefixCls, direction, getPopupContainer } = this.context;
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
getPopupContainer: customizeGetPopupContainer,
|
||||
className,
|
||||
size: customizeSize,
|
||||
bordered = true,
|
||||
@ -115,6 +116,7 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
})}
|
||||
prefixCls={prefixCls}
|
||||
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
|
||||
generateConfig={generateConfig}
|
||||
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
|
||||
nextIcon={<span className={`${prefixCls}-next-icon`} />}
|
||||
|
@ -48,6 +48,10 @@
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&&-disabled &-suffix {
|
||||
color: @disabled-color;
|
||||
}
|
||||
|
||||
&&-borderless {
|
||||
background-color: transparent !important;
|
||||
border-color: transparent !important;
|
||||
@ -105,7 +109,7 @@
|
||||
&-suffix {
|
||||
align-self: center;
|
||||
margin-left: @padding-xs / 2;
|
||||
color: @text-color-secondary;
|
||||
color: @disabled-color;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
|
||||
describe('InputNumber', () => {
|
||||
focusTest(InputNumber, true);
|
||||
focusTest(InputNumber, { refFocus: true });
|
||||
mountTest(InputNumber);
|
||||
rtlTest(InputNumber);
|
||||
|
||||
|
@ -197,17 +197,13 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
);
|
||||
}
|
||||
|
||||
renderClearableLabeledInput() {
|
||||
render() {
|
||||
const { prefixCls, inputType, element } = this.props;
|
||||
if (inputType === ClearableInputType[0]) {
|
||||
return this.renderTextAreaWithClearIcon(prefixCls, element);
|
||||
}
|
||||
return this.renderInputWithLabel(prefixCls, this.renderLabeledIcon(prefixCls, element));
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.renderClearableLabeledInput();
|
||||
}
|
||||
}
|
||||
|
||||
export default ClearableLabeledInput;
|
||||
|
@ -70,14 +70,9 @@ export default class Search extends React.Component<SearchProps, any> {
|
||||
|
||||
if (enterButton) {
|
||||
return (
|
||||
<SizeContext.Consumer>
|
||||
<SizeContext.Consumer key="enterButton">
|
||||
{size => (
|
||||
<Button
|
||||
className={`${prefixCls}-button`}
|
||||
type="primary"
|
||||
size={customizeSize || size}
|
||||
key="enterButton"
|
||||
>
|
||||
<Button className={`${prefixCls}-button`} type="primary" size={customizeSize || size}>
|
||||
<LoadingOutlined />
|
||||
</Button>
|
||||
)}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/input/demo/addon.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<div
|
||||
style="margin-bottom:16px"
|
||||
>
|
||||
@ -28,7 +28,7 @@ exports[`renders ./components/input/demo/addon.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
style="margin-bottom:16px"
|
||||
>
|
||||
@ -167,7 +167,7 @@ exports[`renders ./components/input/demo/addon.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
style="margin-bottom:16px"
|
||||
>
|
||||
@ -208,7 +208,7 @@ exports[`renders ./components/input/demo/addon.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
style="margin-bottom:16px"
|
||||
>
|
||||
@ -239,12 +239,12 @@ exports[`renders ./components/input/demo/addon.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<div
|
||||
class="ant-mentions"
|
||||
style="width:100px"
|
||||
@ -252,12 +252,12 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
<textarea
|
||||
rows="1"
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
<textarea
|
||||
class="ant-input"
|
||||
rows="1"
|
||||
style="width:100px"
|
||||
/>
|
||||
/>,
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
@ -265,13 +265,13 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
<span>
|
||||
Button
|
||||
</span>
|
||||
</button>
|
||||
</button>,
|
||||
<input
|
||||
class="ant-input"
|
||||
style="width:100px"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
/>,
|
||||
<span
|
||||
class="ant-typography"
|
||||
>
|
||||
@ -304,7 +304,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</span>,
|
||||
<span
|
||||
class="ant-input-affix-wrapper"
|
||||
style="width:100px"
|
||||
@ -324,7 +324,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
>
|
||||
2
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
<span
|
||||
class="ant-input-group-wrapper"
|
||||
style="width:100px"
|
||||
@ -348,7 +348,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
2
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
<div
|
||||
class="ant-input-number"
|
||||
style="width:100px"
|
||||
@ -426,7 +426,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-picker"
|
||||
style="width:100px"
|
||||
@ -466,7 +466,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-picker"
|
||||
style="width:100px"
|
||||
@ -509,7 +509,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-select ant-select-single ant-select-show-arrow"
|
||||
style="width:100px"
|
||||
@ -567,7 +567,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-select ant-tree-select ant-select-single ant-select-show-arrow"
|
||||
style="width:100px"
|
||||
@ -623,7 +623,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
tabindex="0"
|
||||
@ -682,7 +682,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
<div
|
||||
class="ant-picker ant-picker-range"
|
||||
>
|
||||
@ -763,7 +763,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-picker"
|
||||
>
|
||||
@ -802,7 +802,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
@ -845,7 +845,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
Shanghai
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-select ant-select-auto-complete ant-select-single ant-select-show-search"
|
||||
style="width:100px"
|
||||
@ -874,8 +874,8 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
input here
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
</div>,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-group-wrapper"
|
||||
>
|
||||
@ -907,7 +907,7 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
.com
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
<span
|
||||
class="ant-input-affix-wrapper"
|
||||
style="width:50px"
|
||||
@ -922,13 +922,13 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
>
|
||||
Y
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
<input
|
||||
class="ant-input"
|
||||
style="width:50px"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
/>,
|
||||
<span
|
||||
class="ant-input-affix-wrapper"
|
||||
style="width:50px"
|
||||
@ -943,12 +943,12 @@ exports[`renders ./components/input/demo/align.md correctly 1`] = `
|
||||
>
|
||||
Y
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/allowClear.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<span
|
||||
class="ant-input-affix-wrapper"
|
||||
>
|
||||
@ -983,9 +983,9 @@ exports[`renders ./components/input/demo/allowClear.md correctly 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
>
|
||||
@ -1014,31 +1014,31 @@ exports[`renders ./components/input/demo/allowClear.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/autosize-textarea.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<textarea
|
||||
class="ant-input"
|
||||
placeholder="Autosize height based on content lines"
|
||||
/>
|
||||
/>,
|
||||
<div
|
||||
style="margin:24px 0"
|
||||
/>
|
||||
/>,
|
||||
<textarea
|
||||
class="ant-input"
|
||||
placeholder="Autosize height with minimum and maximum number of lines"
|
||||
/>
|
||||
/>,
|
||||
<div
|
||||
style="margin:24px 0"
|
||||
/>
|
||||
/>,
|
||||
<textarea
|
||||
class="ant-input"
|
||||
placeholder="Controlled autosize"
|
||||
/>
|
||||
</div>
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/basic.md correctly 1`] = `
|
||||
@ -1945,7 +1945,7 @@ exports[`renders ./components/input/demo/password-input.md correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/presuffix.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<span
|
||||
class="ant-input-affix-wrapper"
|
||||
>
|
||||
@ -2007,9 +2007,9 @@ exports[`renders ./components/input/demo/presuffix.md correctly 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-affix-wrapper"
|
||||
>
|
||||
@ -2028,9 +2028,9 @@ exports[`renders ./components/input/demo/presuffix.md correctly 1`] = `
|
||||
>
|
||||
RMB
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-disabled"
|
||||
>
|
||||
@ -2050,12 +2050,12 @@ exports[`renders ./components/input/demo/presuffix.md correctly 1`] = `
|
||||
>
|
||||
RMB
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<span
|
||||
class="ant-input-search ant-input-affix-wrapper"
|
||||
style="width:200px"
|
||||
@ -2091,9 +2091,9 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-search ant-input-search-enter-button ant-input-group-wrapper"
|
||||
>
|
||||
@ -2136,9 +2136,9 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-search ant-input-search-enter-button ant-input-search-large ant-input-group-wrapper ant-input-group-wrapper-lg"
|
||||
>
|
||||
@ -2164,12 +2164,69 @@ exports[`renders ./components/input/demo/search-input.md correctly 1`] = `
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-search ant-input-search-enter-button ant-input-search-large ant-input-group-wrapper ant-input-group-wrapper-lg"
|
||||
>
|
||||
<span
|
||||
class="ant-input-wrapper ant-input-group"
|
||||
>
|
||||
<span
|
||||
class="ant-input-search ant-input-search-enter-button ant-input-search-large ant-input-affix-wrapper ant-input-affix-wrapper-lg"
|
||||
>
|
||||
<input
|
||||
class="ant-input ant-input-lg"
|
||||
placeholder="input search text"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="audio"
|
||||
class="anticon anticon-audio"
|
||||
role="img"
|
||||
style="font-size:16px;color:#1890ff;padding-right:4px"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="audio"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M842 454c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8 0 140.3-113.7 254-254 254S258 594.3 258 454c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8 0 168.7 126.6 307.9 290 327.6V884H326.7c-13.7 0-24.7 14.3-24.7 32v36c0 4.4 2.8 8 6.2 8h407.6c3.4 0 6.2-3.6 6.2-8v-36c0-17.7-11-32-24.7-32H548V782.1c165.3-18 294-158 294-328.1zM512 624c93.9 0 170-75.2 170-168V232c0-92.8-76.1-168-170-168s-170 75.2-170 168v224c0 92.8 76.1 168 170 168zm-94-392c0-50.6 41.9-92 94-92s94 41.4 94 92v224c0 50.6-41.9 92-94 92s-94-41.4-94-92V232z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-group-addon"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-input-search-button ant-btn-primary ant-btn-lg"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Search
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/search-input-loading.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<span
|
||||
class="ant-input-search ant-input-affix-wrapper"
|
||||
>
|
||||
@ -2203,9 +2260,9 @@ exports[`renders ./components/input/demo/search-input-loading.md correctly 1`] =
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-search ant-input-search-enter-button ant-input-group-wrapper"
|
||||
>
|
||||
@ -2248,14 +2305,12 @@ exports[`renders ./components/input/demo/search-input-loading.md correctly 1`] =
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/size.md correctly 1`] = `
|
||||
<div
|
||||
class="example-input"
|
||||
>
|
||||
Array [
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-lg"
|
||||
>
|
||||
@ -2289,7 +2344,9 @@ exports[`renders ./components/input/demo/size.md correctly 1`] = `
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-affix-wrapper"
|
||||
>
|
||||
@ -2323,7 +2380,9 @@ exports[`renders ./components/input/demo/size.md correctly 1`] = `
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-sm"
|
||||
>
|
||||
@ -2357,47 +2416,8 @@ exports[`renders ./components/input/demo/size.md correctly 1`] = `
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-password ant-input-password-large ant-input-affix-wrapper ant-input-affix-wrapper-lg"
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
class="ant-input ant-input-lg"
|
||||
placeholder="large Password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="eye-invisible"
|
||||
class="anticon anticon-eye-invisible ant-input-password-icon"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="eye-invisible"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"
|
||||
/>
|
||||
<path
|
||||
d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/textarea.md correctly 1`] = `
|
||||
@ -2408,7 +2428,7 @@ exports[`renders ./components/input/demo/textarea.md correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/textarea-resize.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<button
|
||||
class="ant-btn"
|
||||
style="margin-bottom:16px"
|
||||
@ -2417,14 +2437,14 @@ exports[`renders ./components/input/demo/textarea-resize.md correctly 1`] = `
|
||||
<span>
|
||||
Auto Resize: false
|
||||
</span>
|
||||
</button>
|
||||
</button>,
|
||||
<textarea
|
||||
class="ant-input"
|
||||
rows="4"
|
||||
>
|
||||
The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows. The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows.
|
||||
</textarea>
|
||||
</div>
|
||||
</textarea>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/tooltip.md correctly 1`] = `
|
||||
|
@ -35,7 +35,7 @@ const selectAfter = (
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Input addonBefore="http://" addonAfter=".com" defaultValue="mysite" />
|
||||
</div>
|
||||
@ -48,7 +48,7 @@ ReactDOM.render(
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Input addonBefore="http://" suffix=".com" defaultValue="mysite" />
|
||||
</div>
|
||||
</div>,
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
@ -68,7 +68,7 @@ const options = [
|
||||
];
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<>
|
||||
<Mentions style={{ width: 100 }} rows={1} />
|
||||
<Input.TextArea rows={1} style={{ width: 100 }} />
|
||||
<Button type="primary">Button</Button>
|
||||
@ -101,7 +101,7 @@ ReactDOM.render(
|
||||
<Input style={narrowStyle} suffix="Y" />
|
||||
<Input style={narrowStyle} />
|
||||
<Input style={narrowStyle} defaultValue="1" suffix="Y" />
|
||||
</div>,
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
@ -23,12 +23,12 @@ const onChange = e => {
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<>
|
||||
<Input placeholder="input with clear icon" allowClear onChange={onChange} />
|
||||
<br />
|
||||
<br />
|
||||
<TextArea placeholder="textarea with clear icon" allowClear onChange={onChange} />
|
||||
</div>,
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
@ -31,7 +31,7 @@ class Demo extends React.Component {
|
||||
const { value } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<TextArea placeholder="Autosize height based on content lines" autoSize />
|
||||
<div style={{ margin: '24px 0' }} />
|
||||
<TextArea
|
||||
@ -45,7 +45,7 @@ class Demo extends React.Component {
|
||||
placeholder="Controlled autosize"
|
||||
autoSize={{ minRows: 3, maxRows: 5 }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import { Input, Tooltip } from 'antd';
|
||||
import { InfoCircleOutlined, UserOutlined } from '@ant-design/icons';
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<>
|
||||
<Input
|
||||
placeholder="Enter your username"
|
||||
prefix={<UserOutlined className="site-form-item-icon" />}
|
||||
@ -31,12 +31,10 @@ ReactDOM.render(
|
||||
<br />
|
||||
<br />
|
||||
<Input prefix="¥" suffix="RMB" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<Input prefix="¥" suffix="RMB" disabled />
|
||||
</div>,
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
@ -19,12 +19,12 @@ import { Input } from 'antd';
|
||||
const { Search } = Input;
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<>
|
||||
<Search placeholder="input search loading deault" loading />
|
||||
<br />
|
||||
<br />
|
||||
<Search placeholder="input search loading with enterButton" loading enterButton />
|
||||
</div>,
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
@ -15,11 +15,22 @@ Example of creating a search box by grouping a standard input with a search butt
|
||||
|
||||
```jsx
|
||||
import { Input } from 'antd';
|
||||
import { AudioOutlined } from '@ant-design/icons';
|
||||
|
||||
const { Search } = Input;
|
||||
|
||||
const suffix = (
|
||||
<AudioOutlined
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: '#1890ff',
|
||||
paddingRight: 4,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<>
|
||||
<Search
|
||||
placeholder="input search text"
|
||||
onSearch={value => console.log(value)}
|
||||
@ -36,7 +47,16 @@ ReactDOM.render(
|
||||
size="large"
|
||||
onSearch={value => console.log(value)}
|
||||
/>
|
||||
</div>,
|
||||
<br />
|
||||
<br />
|
||||
<Search
|
||||
placeholder="input search text"
|
||||
enterButton="Search"
|
||||
size="large"
|
||||
suffix={suffix}
|
||||
onSearch={value => console.log(value)}
|
||||
/>
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
@ -18,19 +18,15 @@ import { Input } from 'antd';
|
||||
import { UserOutlined } from '@ant-design/icons';
|
||||
|
||||
ReactDOM.render(
|
||||
<div className="example-input">
|
||||
<>
|
||||
<Input size="large" placeholder="large size" prefix={<UserOutlined />} />
|
||||
<br />
|
||||
<br />
|
||||
<Input placeholder="default size" prefix={<UserOutlined />} />
|
||||
<br />
|
||||
<br />
|
||||
<Input size="small" placeholder="small size" prefix={<UserOutlined />} />
|
||||
<Input.Password size="large" placeholder="large Password" />
|
||||
</div>,
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
```css
|
||||
.example-input > span {
|
||||
width: 200px;
|
||||
margin: 0 8px 8px 0;
|
||||
}
|
||||
```
|
||||
|
@ -31,7 +31,7 @@ class Demo extends React.Component {
|
||||
const { autoResize } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<Button
|
||||
onClick={() => this.setState({ autoResize: !autoResize })}
|
||||
style={{ marginBottom: 16 }}
|
||||
@ -39,7 +39,7 @@ class Demo extends React.Component {
|
||||
Auto Resize: {String(autoResize)}
|
||||
</Button>
|
||||
<TextArea rows={4} autoSize={autoResize} defaultValue={defaultValue} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,15 @@
|
||||
&-affix-wrapper {
|
||||
.input();
|
||||
display: inline-flex;
|
||||
max-height: @input-height-base;
|
||||
|
||||
&-lg {
|
||||
max-height: @input-height-lg;
|
||||
}
|
||||
|
||||
&-sm {
|
||||
max-height: @input-height-sm;
|
||||
}
|
||||
|
||||
&-disabled {
|
||||
.@{ant-prefix}-input[disabled] {
|
||||
@ -15,7 +24,8 @@
|
||||
}
|
||||
|
||||
> input.@{ant-prefix}-input {
|
||||
padding: 0;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
@ -33,7 +43,9 @@
|
||||
|
||||
&-prefix,
|
||||
&-suffix {
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-prefix {
|
||||
|
@ -210,17 +210,26 @@
|
||||
z-index: @zindex-dropdown;
|
||||
border-radius: @border-radius-base;
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/13955
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.0001;
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/13955
|
||||
&-placement-rightTop::before {
|
||||
top: 0;
|
||||
left: -7px;
|
||||
}
|
||||
|
||||
> .@{menu-prefix-cls} {
|
||||
background-color: @menu-bg;
|
||||
border-radius: @border-radius-base;
|
||||
|
@ -4,24 +4,19 @@ import PageHeader from '..';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { spyElementPrototypes } from '../../__tests__/util/domHook';
|
||||
|
||||
describe('PageHeader', () => {
|
||||
mountTest(PageHeader);
|
||||
rtlTest(PageHeader);
|
||||
|
||||
let spy;
|
||||
const mockGetBoundingClientRect = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect');
|
||||
|
||||
beforeAll(() => {
|
||||
spy = spyElementPrototypes(HTMLElement, {
|
||||
getBoundingClientRect: () => ({
|
||||
width: 100,
|
||||
}),
|
||||
});
|
||||
mockGetBoundingClientRect.mockReturnValue({ width: 100 });
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
spy.mockRestore();
|
||||
mockGetBoundingClientRect.mockRestore();
|
||||
});
|
||||
|
||||
it('pageHeader should not contain back it back', () => {
|
||||
|
@ -17,7 +17,7 @@ export interface PageHeaderProps {
|
||||
subTitle?: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
breadcrumb?: BreadcrumbProps;
|
||||
tags?: React.ReactElement<Tag> | React.ReactElement<Tag>[];
|
||||
tags?: typeof Tag | typeof Tag[];
|
||||
footer?: React.ReactNode;
|
||||
extra?: React.ReactNode;
|
||||
avatar?: AvatarProps;
|
||||
|
@ -1005,7 +1005,7 @@ exports[`renders ./components/result/demo/500.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-result-subtitle"
|
||||
>
|
||||
Sorry, the server is wrong.
|
||||
Sorry, something went wrong.
|
||||
</div>
|
||||
<div
|
||||
class="ant-result-extra"
|
||||
|
@ -11,7 +11,7 @@ title:
|
||||
|
||||
## en-US
|
||||
|
||||
The server is wrong.
|
||||
Something went wrong on server.
|
||||
|
||||
```jsx
|
||||
import { Result, Button } from 'antd';
|
||||
@ -20,7 +20,7 @@ ReactDOM.render(
|
||||
<Result
|
||||
status="500"
|
||||
title="500"
|
||||
subTitle="Sorry, the server is wrong."
|
||||
subTitle="Sorry, something went wrong."
|
||||
extra={<Button type="primary">Back Home</Button>}
|
||||
/>,
|
||||
mountNode,
|
||||
|
@ -10,64 +10,23 @@ import { sleep } from '../../../tests/utils';
|
||||
describe('Slider', () => {
|
||||
mountTest(Slider);
|
||||
rtlTest(Slider);
|
||||
focusTest(Slider);
|
||||
focusTest(Slider, { refFocus: true });
|
||||
|
||||
it('should show tooltip when hovering slider handler', () => {
|
||||
const wrapper = mount(<Slider defaultValue={30} />);
|
||||
wrapper
|
||||
.find('.ant-slider-handle')
|
||||
.at(0)
|
||||
.simulate('mouseEnter');
|
||||
expect(
|
||||
render(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
),
|
||||
).toMatchSnapshot();
|
||||
wrapper
|
||||
.find('.ant-slider-handle')
|
||||
.at(0)
|
||||
.simulate('mouseLeave');
|
||||
expect(
|
||||
render(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
),
|
||||
).toMatchSnapshot();
|
||||
wrapper.find('.ant-slider-handle').at(0).simulate('mouseEnter');
|
||||
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
|
||||
wrapper.find('.ant-slider-handle').at(0).simulate('mouseLeave');
|
||||
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('when tooltipVisible is true, tooltip should show always, or should never show', () => {
|
||||
let wrapper = mount(<Slider defaultValue={30} tooltipVisible />);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-tooltip-content')
|
||||
.at(0)
|
||||
.hasClass('ant-tooltip-hidden'),
|
||||
).toBe(false);
|
||||
wrapper
|
||||
.find('.ant-slider-handle')
|
||||
.at(0)
|
||||
.simulate('mouseEnter');
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-tooltip-content')
|
||||
.at(0)
|
||||
.hasClass('ant-tooltip-hidden'),
|
||||
).toBe(false);
|
||||
wrapper
|
||||
.find('.ant-slider-handle')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-tooltip-content')
|
||||
.at(0)
|
||||
.hasClass('ant-tooltip-hidden'),
|
||||
).toBe(false);
|
||||
expect(wrapper.find('.ant-tooltip-content').at(0).hasClass('ant-tooltip-hidden')).toBe(false);
|
||||
wrapper.find('.ant-slider-handle').at(0).simulate('mouseEnter');
|
||||
expect(wrapper.find('.ant-tooltip-content').at(0).hasClass('ant-tooltip-hidden')).toBe(false);
|
||||
wrapper.find('.ant-slider-handle').at(0).simulate('click');
|
||||
expect(wrapper.find('.ant-tooltip-content').at(0).hasClass('ant-tooltip-hidden')).toBe(false);
|
||||
wrapper = mount(<Slider defaultValue={30} tooltipVisible={false} />);
|
||||
expect(wrapper.find('.ant-tooltip-content').length).toBe(0);
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ import RcHandle from 'rc-slider/lib/Handle';
|
||||
import classNames from 'classnames';
|
||||
import { TooltipPlacement } from '../tooltip';
|
||||
import SliderTooltip from './SliderTooltip';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
|
||||
export interface SliderMarks {
|
||||
[key: number]:
|
||||
@ -57,36 +57,19 @@ export interface SliderProps {
|
||||
getTooltipPopupContainer?: (triggerNode: HTMLElement) => HTMLElement;
|
||||
}
|
||||
|
||||
export interface SliderState {
|
||||
visibles: { [index: number]: boolean };
|
||||
}
|
||||
export type Visibles = { [index: number]: boolean };
|
||||
|
||||
export default class Slider extends React.Component<SliderProps, SliderState> {
|
||||
static defaultProps = {
|
||||
tipFormatter(value: number) {
|
||||
return typeof value === 'number' ? value.toString() : '';
|
||||
},
|
||||
const Slider = React.forwardRef<unknown, SliderProps>((props, ref) => {
|
||||
const { getPrefixCls, direction, getPopupContainer } = React.useContext(ConfigContext);
|
||||
const [visibles, setVisibles] = React.useState<Visibles>({});
|
||||
|
||||
const toggleTooltipVisible = (index: number, visible: boolean) => {
|
||||
const temp = { ...visibles };
|
||||
temp[index] = visible;
|
||||
setVisibles(temp);
|
||||
};
|
||||
|
||||
rcSlider: any;
|
||||
|
||||
constructor(props: SliderProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visibles: {},
|
||||
};
|
||||
}
|
||||
|
||||
toggleTooltipVisible = (index: number, visible: boolean) => {
|
||||
this.setState(({ visibles }) => ({
|
||||
visibles: {
|
||||
...visibles,
|
||||
[index]: visible,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
handleWithTooltip: HandleGeneratorFn = ({
|
||||
const handleWithTooltip: HandleGeneratorFn = ({
|
||||
tooltipPrefixCls,
|
||||
prefixCls,
|
||||
info: { value, dragging, index, ...restProps },
|
||||
@ -97,8 +80,7 @@ export default class Slider extends React.Component<SliderProps, SliderState> {
|
||||
tooltipPlacement,
|
||||
getTooltipPopupContainer,
|
||||
vertical,
|
||||
} = this.props;
|
||||
const { visibles } = this.state;
|
||||
} = props;
|
||||
const isTipFormatter = tipFormatter ? visibles[index] || dragging : false;
|
||||
const visible = tooltipVisible || (tooltipVisible === undefined && isTipFormatter);
|
||||
return (
|
||||
@ -110,72 +92,41 @@ export default class Slider extends React.Component<SliderProps, SliderState> {
|
||||
transitionName="zoom-down"
|
||||
key={index}
|
||||
overlayClassName={`${prefixCls}-tooltip`}
|
||||
getPopupContainer={getTooltipPopupContainer || (() => document.body)}
|
||||
getPopupContainer={getTooltipPopupContainer || getPopupContainer || (() => document.body)}
|
||||
>
|
||||
<RcHandle
|
||||
{...restProps}
|
||||
value={value}
|
||||
onMouseEnter={() => this.toggleTooltipVisible(index, true)}
|
||||
onMouseLeave={() => this.toggleTooltipVisible(index, false)}
|
||||
onMouseEnter={() => toggleTooltipVisible(index, true)}
|
||||
onMouseLeave={() => toggleTooltipVisible(index, false)}
|
||||
/>
|
||||
</SliderTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
saveSlider = (node: any) => {
|
||||
this.rcSlider = node;
|
||||
};
|
||||
|
||||
focus() {
|
||||
this.rcSlider.focus();
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
tooltipPrefixCls: customizeTooltipPrefixCls,
|
||||
range,
|
||||
className,
|
||||
...restProps
|
||||
} = props;
|
||||
const prefixCls = getPrefixCls('slider', customizePrefixCls);
|
||||
const tooltipPrefixCls = getPrefixCls('tooltip', customizeTooltipPrefixCls);
|
||||
const cls = classNames(className, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
});
|
||||
// make reverse default on rtl direction
|
||||
if (direction === 'rtl' && !restProps.vertical) {
|
||||
restProps.reverse = !restProps.reverse;
|
||||
}
|
||||
|
||||
blur() {
|
||||
this.rcSlider.blur();
|
||||
}
|
||||
|
||||
renderSlider = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
tooltipPrefixCls: customizeTooltipPrefixCls,
|
||||
range,
|
||||
className,
|
||||
...restProps
|
||||
} = this.props;
|
||||
const prefixCls = getPrefixCls('slider', customizePrefixCls);
|
||||
const tooltipPrefixCls = getPrefixCls('tooltip', customizeTooltipPrefixCls);
|
||||
const cls = classNames(className, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
});
|
||||
// make reverse default on rtl direction
|
||||
if (direction === 'rtl' && !restProps.vertical) {
|
||||
restProps.reverse = !restProps.reverse;
|
||||
}
|
||||
if (range) {
|
||||
return (
|
||||
<RcRange
|
||||
{...restProps}
|
||||
className={cls}
|
||||
ref={this.saveSlider}
|
||||
handle={(info: HandleGeneratorInfo) =>
|
||||
this.handleWithTooltip({
|
||||
tooltipPrefixCls,
|
||||
prefixCls,
|
||||
info,
|
||||
})
|
||||
}
|
||||
prefixCls={prefixCls}
|
||||
tooltipPrefixCls={tooltipPrefixCls}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (range) {
|
||||
return (
|
||||
<RcSlider
|
||||
<RcRange
|
||||
{...restProps}
|
||||
className={cls}
|
||||
ref={this.saveSlider}
|
||||
ref={ref}
|
||||
handle={(info: HandleGeneratorInfo) =>
|
||||
this.handleWithTooltip({
|
||||
handleWithTooltip({
|
||||
tooltipPrefixCls,
|
||||
prefixCls,
|
||||
info,
|
||||
@ -185,9 +136,31 @@ export default class Slider extends React.Component<SliderProps, SliderState> {
|
||||
tooltipPrefixCls={tooltipPrefixCls}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderSlider}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<RcSlider
|
||||
{...restProps}
|
||||
className={cls}
|
||||
ref={ref}
|
||||
handle={(info: HandleGeneratorInfo) =>
|
||||
handleWithTooltip({
|
||||
tooltipPrefixCls,
|
||||
prefixCls,
|
||||
info,
|
||||
})
|
||||
}
|
||||
prefixCls={prefixCls}
|
||||
tooltipPrefixCls={tooltipPrefixCls}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Slider.displayName = 'Slider';
|
||||
|
||||
Slider.defaultProps = {
|
||||
tipFormatter(value: number) {
|
||||
return typeof value === 'number' ? value.toString() : '';
|
||||
},
|
||||
};
|
||||
|
||||
export default Slider;
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
.@{steps-prefix-cls}-item {
|
||||
&-icon {
|
||||
.@{steps-prefix-cls}-rtl & {
|
||||
.@{steps-prefix-cls}.@{steps-prefix-cls}-rtl & {
|
||||
margin-right: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
@ -129,6 +129,7 @@
|
||||
.@{steps-prefix-cls}-item-title {
|
||||
.@{steps-prefix-cls}-rtl& {
|
||||
padding-right: 0;
|
||||
padding-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
flex-direction: column;
|
||||
.@{steps-prefix-cls}-item {
|
||||
display: block;
|
||||
flex: 1 0 auto;
|
||||
overflow: visible;
|
||||
&-icon {
|
||||
float: left;
|
||||
|
@ -11261,14 +11261,16 @@ exports[`renders ./components/table/demo/resizable-column.md correctly 1`] = `
|
||||
<tr>
|
||||
<th
|
||||
class="ant-table-cell react-resizable"
|
||||
handle="[object Object]"
|
||||
>
|
||||
Date
|
||||
<span
|
||||
class="react-resizable-handle react-resizable-handle-se"
|
||||
class="react-resizable-handle"
|
||||
/>
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell ant-table-column-has-sorters react-resizable"
|
||||
handle="[object Object]"
|
||||
>
|
||||
<div
|
||||
class="ant-table-column-sorters-with-tooltip"
|
||||
@ -11330,23 +11332,25 @@ exports[`renders ./components/table/demo/resizable-column.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="react-resizable-handle react-resizable-handle-se"
|
||||
class="react-resizable-handle"
|
||||
/>
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell react-resizable"
|
||||
handle="[object Object]"
|
||||
>
|
||||
Type
|
||||
<span
|
||||
class="react-resizable-handle react-resizable-handle-se"
|
||||
class="react-resizable-handle"
|
||||
/>
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell react-resizable"
|
||||
handle="[object Object]"
|
||||
>
|
||||
Note
|
||||
<span
|
||||
class="react-resizable-handle react-resizable-handle-se"
|
||||
class="react-resizable-handle"
|
||||
/>
|
||||
</th>
|
||||
<th
|
||||
|
@ -24,6 +24,10 @@ describe('Table.typescript', () => {
|
||||
);
|
||||
expect(table).toBeTruthy();
|
||||
});
|
||||
it('selections', () => {
|
||||
const table = <Table rowSelection={{ selections: [Table.SELECTION_ALL] }} />;
|
||||
expect(table).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Table.typescript types', () => {
|
||||
|
@ -46,64 +46,69 @@ const columns = [
|
||||
},
|
||||
];
|
||||
|
||||
const getRandomuserParams = params => {
|
||||
return {
|
||||
results: params.pagination.pageSize,
|
||||
page: params.pagination.current,
|
||||
...params,
|
||||
};
|
||||
};
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
data: [],
|
||||
pagination: {},
|
||||
pagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
loading: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.fetch();
|
||||
const { pagination } = this.state;
|
||||
this.fetch({ pagination });
|
||||
}
|
||||
|
||||
handleTableChange = (pagination, filters, sorter) => {
|
||||
const pager = { ...this.state.pagination };
|
||||
pager.current = pagination.current;
|
||||
this.setState({
|
||||
pagination: pager,
|
||||
});
|
||||
this.fetch({
|
||||
results: pagination.pageSize,
|
||||
page: pagination.current,
|
||||
sortField: sorter.field,
|
||||
sortOrder: sorter.order,
|
||||
pagination,
|
||||
...filters,
|
||||
});
|
||||
};
|
||||
|
||||
fetch = (params = {}) => {
|
||||
console.log('params:', params);
|
||||
this.setState({ loading: true });
|
||||
reqwest({
|
||||
url: 'https://randomuser.me/api',
|
||||
method: 'get',
|
||||
data: {
|
||||
results: 10,
|
||||
...params,
|
||||
},
|
||||
type: 'json',
|
||||
data: getRandomuserParams(params),
|
||||
}).then(data => {
|
||||
const pagination = { ...this.state.pagination };
|
||||
// Read total count from server
|
||||
// pagination.total = data.totalCount;
|
||||
pagination.total = 200;
|
||||
console.log(data);
|
||||
this.setState({
|
||||
loading: false,
|
||||
data: data.results,
|
||||
pagination,
|
||||
pagination: {
|
||||
...params.pagination,
|
||||
total: 200,
|
||||
// 200 is mock data, you should read it from server
|
||||
// total: data.totalCount,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { data, pagination, loading } = this.state;
|
||||
return (
|
||||
<Table
|
||||
columns={columns}
|
||||
rowKey={record => record.login.uuid}
|
||||
dataSource={this.state.data}
|
||||
pagination={this.state.pagination}
|
||||
loading={this.state.loading}
|
||||
dataSource={data}
|
||||
pagination={pagination}
|
||||
loading={loading}
|
||||
onChange={this.handleTableChange}
|
||||
/>
|
||||
);
|
||||
|
@ -28,14 +28,14 @@ const ResizeableTitle = props => {
|
||||
<Resizable
|
||||
width={width}
|
||||
height={0}
|
||||
handle={resizeHandle => (
|
||||
handle={
|
||||
<span
|
||||
className={`react-resizable-handle react-resizable-handle-${resizeHandle}`}
|
||||
className="react-resizable-handle"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
onResize={onResize}
|
||||
draggableOpts={{ enableUserSelectHack: false }}
|
||||
>
|
||||
|
@ -21,8 +21,8 @@ import {
|
||||
const EMPTY_LIST: any[] = [];
|
||||
|
||||
// TODO: warning if use ajax!!!
|
||||
export const SELECTION_ALL = 'SELECT_ALL';
|
||||
export const SELECTION_INVERT = 'SELECT_INVERT';
|
||||
export const SELECTION_ALL = 'SELECT_ALL' as const;
|
||||
export const SELECTION_INVERT = 'SELECT_INVERT' as const;
|
||||
|
||||
function getFixedType<RecordType>(column: ColumnsType<RecordType>[number]): FixedType | undefined {
|
||||
return column && column.fixed;
|
||||
@ -41,7 +41,10 @@ interface UseSelectionConfig<RecordType> {
|
||||
getPopupContainer?: GetPopupContainer;
|
||||
}
|
||||
|
||||
type INTERNAL_SELECTION_ITEM = SelectionItem | typeof SELECTION_ALL | typeof SELECTION_INVERT;
|
||||
export type INTERNAL_SELECTION_ITEM =
|
||||
| SelectionItem
|
||||
| typeof SELECTION_ALL
|
||||
| typeof SELECTION_INVERT;
|
||||
|
||||
function flattenData<RecordType>(
|
||||
data: RecordType[] | undefined,
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
import { CheckboxProps } from '../checkbox';
|
||||
import { PaginationConfig } from '../pagination';
|
||||
import { Breakpoint } from '../_util/responsiveObserve';
|
||||
|
||||
import { INTERNAL_SELECTION_ITEM } from './hooks/useSelection';
|
||||
export { GetRowKey, ExpandableConfig };
|
||||
|
||||
export type Key = React.Key;
|
||||
@ -134,7 +134,7 @@ export interface TableRowSelection<T> {
|
||||
onSelectAll?: (selected: boolean, selectedRows: T[], changeRows: T[]) => void;
|
||||
/** @deprecated This function is meaningless and should use `onChange` instead */
|
||||
onSelectInvert?: (selectedRowKeys: Key[]) => void;
|
||||
selections?: SelectionItem[] | boolean;
|
||||
selections?: INTERNAL_SELECTION_ITEM[] | boolean;
|
||||
hideDefaultSelections?: boolean;
|
||||
fixed?: boolean;
|
||||
columnWidth?: string | number;
|
||||
|
@ -32,6 +32,16 @@
|
||||
.@{table-prefix-cls}-expanded-row-fixed {
|
||||
margin: -@padding-vertical -@padding-horizontal;
|
||||
}
|
||||
|
||||
.@{table-prefix-cls}-tbody {
|
||||
// ========================= Nest Table ===========================
|
||||
.@{table-prefix-cls}-wrapper:only-child {
|
||||
.@{table-prefix-cls} {
|
||||
margin: -@padding-vertical -@padding-horizontal -@padding-vertical (@padding-horizontal +
|
||||
ceil(@font-size-sm * 1.4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,5 +179,3 @@
|
||||
border-top-color: @component-background;
|
||||
}
|
||||
}
|
||||
|
||||
@import './card-style.rtl.less';
|
||||
|
@ -1,20 +0,0 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@tab-prefix-cls: ~'@{ant-prefix}-tabs';
|
||||
|
||||
// card style
|
||||
.@{tab-prefix-cls} {
|
||||
&&-card &-card-bar &-tab &-close-x {
|
||||
.@{tab-prefix-cls}-rtl& {
|
||||
margin-right: 3px;
|
||||
margin-left: -5px;
|
||||
}
|
||||
}
|
||||
|
||||
&-extra-content {
|
||||
.@{tab-prefix-cls}-rtl & {
|
||||
float: left !important;
|
||||
}
|
||||
}
|
||||
}
|
@ -297,6 +297,7 @@
|
||||
float: none;
|
||||
margin: @tabs-vertical-margin;
|
||||
padding: @tabs-vertical-padding;
|
||||
text-align: center;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
@ -368,9 +369,6 @@
|
||||
margin-right: -1px;
|
||||
margin-bottom: 0;
|
||||
border-right: @border-width-base @border-style-base @border-color-split;
|
||||
.@{tab-prefix-cls}-tab {
|
||||
text-align: right;
|
||||
}
|
||||
.@{tab-prefix-cls}-nav-container {
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
@ -44,4 +44,40 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{tab-prefix-cls}-left-bar,
|
||||
.@{tab-prefix-cls}-right-bar {
|
||||
.@{tab-prefix-cls}-tab {
|
||||
.@{tab-prefix-cls}-rtl& {
|
||||
margin: @tabs-vertical-margin;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{tab-prefix-cls}-right-bar {
|
||||
.@{tab-prefix-cls}-ink-bar {
|
||||
.@{tab-prefix-cls}-rtl& {
|
||||
right: auto;
|
||||
left: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// card
|
||||
&&-card &-card-bar &-tab &-close-x {
|
||||
.@{tab-prefix-cls}-rtl& {
|
||||
margin-right: 3px;
|
||||
margin-left: -5px;
|
||||
}
|
||||
}
|
||||
|
||||
&-extra-content {
|
||||
.@{tab-prefix-cls}-rtl & {
|
||||
float: left !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,16 +10,16 @@ export interface CheckableTagProps {
|
||||
onChange?: (checked: boolean) => void;
|
||||
}
|
||||
|
||||
export default class CheckableTag extends React.Component<CheckableTagProps> {
|
||||
handleClick = () => {
|
||||
const { checked, onChange } = this.props;
|
||||
const CheckableTag: React.FC<CheckableTagProps> = props => {
|
||||
const handleClick = () => {
|
||||
const { checked, onChange } = props;
|
||||
if (onChange) {
|
||||
onChange(!checked);
|
||||
}
|
||||
};
|
||||
|
||||
renderCheckableTag = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const { prefixCls: customizePrefixCls, className, checked, ...restProps } = this.props;
|
||||
const renderCheckableTag = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const { prefixCls: customizePrefixCls, className, checked, ...restProps } = props;
|
||||
const prefixCls = getPrefixCls('tag', customizePrefixCls);
|
||||
const cls = classNames(
|
||||
prefixCls,
|
||||
@ -31,10 +31,10 @@ export default class CheckableTag extends React.Component<CheckableTagProps> {
|
||||
);
|
||||
|
||||
delete (restProps as any).onChange; // TypeScript cannot check delete now.
|
||||
return <span {...(restProps as any)} className={cls} onClick={this.handleClick} />;
|
||||
return <span {...(restProps as any)} className={cls} onClick={handleClick} />;
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderCheckableTag}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
return <ConfigConsumer>{renderCheckableTag}</ConfigConsumer>;
|
||||
};
|
||||
|
||||
export default CheckableTag;
|
||||
|
@ -27,61 +27,57 @@ export interface TagProps extends React.HTMLAttributes<HTMLSpanElement> {
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
interface TagState {
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
const PresetColorRegex = new RegExp(`^(${PresetColorTypes.join('|')})(-inverse)?$`);
|
||||
const PresetStatusColorRegex = new RegExp(`^(${PresetStatusColorTypes.join('|')})$`);
|
||||
|
||||
class Tag extends React.Component<TagProps, TagState> {
|
||||
static CheckableTag = CheckableTag;
|
||||
interface TagType extends React.FC<TagProps> {
|
||||
CheckableTag: typeof CheckableTag;
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
closable: false,
|
||||
};
|
||||
const Tag: TagType = props => {
|
||||
const [visible, setVisible] = React.useState(true);
|
||||
|
||||
static getDerivedStateFromProps(nextProps: TagProps) {
|
||||
if ('visible' in nextProps) {
|
||||
return {
|
||||
visible: nextProps.visible,
|
||||
};
|
||||
React.useEffect(() => {
|
||||
if ('visible' in props) {
|
||||
setVisible(props.visible!);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, [props.visible]);
|
||||
|
||||
state = {
|
||||
visible: true,
|
||||
const isPresetColor = (): boolean => {
|
||||
const { color } = props;
|
||||
if (!color) {
|
||||
return false;
|
||||
}
|
||||
return PresetColorRegex.test(color) || PresetStatusColorRegex.test(color);
|
||||
};
|
||||
|
||||
getTagStyle() {
|
||||
const { color, style } = this.props;
|
||||
const isPresetColor = this.isPresetColor();
|
||||
const getTagStyle = () => {
|
||||
const { color, style } = props;
|
||||
return {
|
||||
backgroundColor: color && !isPresetColor ? color : undefined,
|
||||
backgroundColor: color && !isPresetColor() ? color : undefined,
|
||||
...style,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
getTagClassName({ getPrefixCls, direction }: ConfigConsumerProps) {
|
||||
const { prefixCls: customizePrefixCls, className, color } = this.props;
|
||||
const { visible } = this.state;
|
||||
const isPresetColor = this.isPresetColor();
|
||||
const getTagClassName = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const { prefixCls: customizePrefixCls, className, color } = props;
|
||||
const presetColor = isPresetColor();
|
||||
const prefixCls = getPrefixCls('tag', customizePrefixCls);
|
||||
return classNames(
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}-${color}`]: isPresetColor,
|
||||
[`${prefixCls}-has-color`]: color && !isPresetColor,
|
||||
[`${prefixCls}-${color}`]: presetColor,
|
||||
[`${prefixCls}-has-color`]: color && !presetColor,
|
||||
[`${prefixCls}-hidden`]: !visible,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
setVisible(visible: boolean, e: React.MouseEvent<HTMLElement>) {
|
||||
const { onClose } = this.props;
|
||||
const handleIconClick = (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.stopPropagation();
|
||||
const { onClose } = props;
|
||||
if (onClose) {
|
||||
onClose(e);
|
||||
}
|
||||
@ -89,31 +85,18 @@ class Tag extends React.Component<TagProps, TagState> {
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
if (!('visible' in this.props)) {
|
||||
this.setState({ visible });
|
||||
if (!('visible' in props)) {
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
handleIconClick = (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.stopPropagation();
|
||||
this.setVisible(false, e);
|
||||
};
|
||||
|
||||
isPresetColor(): boolean {
|
||||
const { color } = this.props;
|
||||
if (!color) {
|
||||
return false;
|
||||
}
|
||||
return PresetColorRegex.test(color) || PresetStatusColorRegex.test(color);
|
||||
}
|
||||
const renderCloseIcon = () => {
|
||||
const { closable } = props;
|
||||
return closable ? <CloseOutlined onClick={handleIconClick} /> : null;
|
||||
};
|
||||
|
||||
renderCloseIcon() {
|
||||
const { closable } = this.props;
|
||||
return closable ? <CloseOutlined onClick={this.handleIconClick} /> : null;
|
||||
}
|
||||
|
||||
renderTag = (configProps: ConfigConsumerProps) => {
|
||||
const { children, icon, ...otherProps } = this.props;
|
||||
const renderTag = (configProps: ConfigConsumerProps) => {
|
||||
const { children, icon, ...otherProps } = props;
|
||||
const isNeedWave =
|
||||
'onClick' in otherProps || (children && (children as React.ReactElement<any>).type === 'a');
|
||||
const tagProps = omit(otherProps, ['onClose', 'color', 'visible', 'closable', 'prefixCls']);
|
||||
@ -129,26 +112,26 @@ class Tag extends React.Component<TagProps, TagState> {
|
||||
|
||||
return isNeedWave ? (
|
||||
<Wave>
|
||||
<span
|
||||
{...tagProps}
|
||||
className={this.getTagClassName(configProps)}
|
||||
style={this.getTagStyle()}
|
||||
>
|
||||
<span {...tagProps} className={getTagClassName(configProps)} style={getTagStyle()}>
|
||||
{kids}
|
||||
{this.renderCloseIcon()}
|
||||
{renderCloseIcon()}
|
||||
</span>
|
||||
</Wave>
|
||||
) : (
|
||||
<span {...tagProps} className={this.getTagClassName(configProps)} style={this.getTagStyle()}>
|
||||
<span {...tagProps} className={getTagClassName(configProps)} style={getTagStyle()}>
|
||||
{kids}
|
||||
{this.renderCloseIcon()}
|
||||
{renderCloseIcon()}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderTag}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
return <ConfigConsumer>{renderTag}</ConfigConsumer>;
|
||||
};
|
||||
|
||||
Tag.defaultProps = {
|
||||
closable: false,
|
||||
};
|
||||
|
||||
Tag.CheckableTag = CheckableTag;
|
||||
|
||||
export default Tag;
|
||||
|
@ -18,7 +18,7 @@ describe('TimePicker', () => {
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
|
||||
focusTest(TimePicker, true);
|
||||
focusTest(TimePicker, { refFocus: true });
|
||||
mountTest(TimePicker);
|
||||
rtlTest(TimePicker);
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { conductExpandParent } from 'rc-tree/lib/util';
|
||||
import { EventDataNode, DataNode, Key } from 'rc-tree/lib/interface';
|
||||
@ -8,7 +7,7 @@ import { convertDataToEntities, convertTreeToData } from 'rc-tree/lib/utils/tree
|
||||
import FileOutlined from '@ant-design/icons/FileOutlined';
|
||||
import FolderOpenOutlined from '@ant-design/icons/FolderOpenOutlined';
|
||||
import FolderOutlined from '@ant-design/icons/FolderOutlined';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
|
||||
import Tree, { TreeProps, AntdTreeNodeAttribute } from './Tree';
|
||||
import { calcRangeKeys, convertDirectoryKeysToNodes } from './utils/dictUtil';
|
||||
@ -36,109 +35,116 @@ function getTreeData({ treeData, children }: DirectoryTreeProps) {
|
||||
return treeData || convertTreeToData(children);
|
||||
}
|
||||
|
||||
class DirectoryTree extends React.Component<DirectoryTreeProps, DirectoryTreeState> {
|
||||
static defaultProps = {
|
||||
showIcon: true,
|
||||
expandAction: 'click' as DirectoryTreeProps['expandAction'],
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps: DirectoryTreeProps) {
|
||||
const newState: DirectoryTreeState = {};
|
||||
if ('expandedKeys' in nextProps) {
|
||||
newState.expandedKeys = nextProps.expandedKeys;
|
||||
}
|
||||
if ('selectedKeys' in nextProps) {
|
||||
newState.selectedKeys = nextProps.selectedKeys;
|
||||
}
|
||||
return newState;
|
||||
}
|
||||
|
||||
state: DirectoryTreeState;
|
||||
|
||||
tree: Tree;
|
||||
|
||||
onDebounceExpand: (event: React.MouseEvent<HTMLElement>, node: EventDataNode) => void;
|
||||
|
||||
const DirectoryTree: React.FC<DirectoryTreeProps> = ({
|
||||
defaultExpandAll,
|
||||
defaultExpandParent,
|
||||
defaultExpandedKeys,
|
||||
...props
|
||||
}) => {
|
||||
// Shift click usage
|
||||
lastSelectedKey?: Key;
|
||||
const lastSelectedKey = React.useRef<Key>();
|
||||
|
||||
cachedSelectedKeys?: Key[];
|
||||
const cachedSelectedKeys = React.useRef<Key[]>();
|
||||
|
||||
constructor(props: DirectoryTreeProps) {
|
||||
super(props);
|
||||
const ref = React.createRef<any>();
|
||||
|
||||
const { defaultExpandAll, defaultExpandParent, expandedKeys, defaultExpandedKeys } = props;
|
||||
const getInitExpandedKeys = () => {
|
||||
const { keyEntities } = convertDataToEntities(getTreeData(props));
|
||||
|
||||
// Selected keys
|
||||
this.state = {
|
||||
selectedKeys: props.selectedKeys || props.defaultSelectedKeys || [],
|
||||
};
|
||||
let initExpandedKeys: any;
|
||||
|
||||
// Expanded keys
|
||||
if (defaultExpandAll) {
|
||||
this.state.expandedKeys = Object.keys(keyEntities);
|
||||
initExpandedKeys = Object.keys(keyEntities);
|
||||
} else if (defaultExpandParent) {
|
||||
this.state.expandedKeys = conductExpandParent(
|
||||
expandedKeys || defaultExpandedKeys,
|
||||
initExpandedKeys = conductExpandParent(
|
||||
props.expandedKeys || defaultExpandedKeys,
|
||||
keyEntities,
|
||||
);
|
||||
} else {
|
||||
this.state.expandedKeys = expandedKeys || defaultExpandedKeys;
|
||||
initExpandedKeys = props.expandedKeys || defaultExpandedKeys;
|
||||
}
|
||||
return initExpandedKeys;
|
||||
};
|
||||
|
||||
const [selectedKeys, setSelectedKeys] = React.useState(
|
||||
props.selectedKeys || props.defaultSelectedKeys || [],
|
||||
);
|
||||
const [expandedKeys, setExpandedKeys] = React.useState(getInitExpandedKeys());
|
||||
|
||||
React.useEffect(() => {
|
||||
if ('selectedKeys' in props) {
|
||||
setSelectedKeys(props.selectedKeys!);
|
||||
}
|
||||
}, [props.selectedKeys]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if ('expandedKeys' in props) {
|
||||
setExpandedKeys(props.expandedKeys);
|
||||
}
|
||||
}, [props.expandedKeys]);
|
||||
|
||||
const expandFolderNode = (event: React.MouseEvent<HTMLElement>, node: any) => {
|
||||
const { isLeaf } = node;
|
||||
|
||||
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onDebounceExpand = debounce(this.expandFolderNode, 200, {
|
||||
leading: true,
|
||||
});
|
||||
}
|
||||
// Call internal rc-tree expand function
|
||||
// https://github.com/ant-design/ant-design/issues/12567
|
||||
ref.current.onNodeExpand(event, node);
|
||||
};
|
||||
|
||||
onExpand = (
|
||||
expandedKeys: Key[],
|
||||
const onDebounceExpand = debounce(expandFolderNode, 200, {
|
||||
leading: true,
|
||||
});
|
||||
const onExpand = (
|
||||
keys: Key[],
|
||||
info: {
|
||||
node: EventDataNode;
|
||||
expanded: boolean;
|
||||
nativeEvent: MouseEvent;
|
||||
},
|
||||
) => {
|
||||
const { onExpand } = this.props;
|
||||
|
||||
this.setUncontrolledState({ expandedKeys });
|
||||
|
||||
if (!('expandedKeys' in props)) {
|
||||
setExpandedKeys(keys);
|
||||
}
|
||||
// Call origin function
|
||||
if (onExpand) {
|
||||
return onExpand(expandedKeys, info);
|
||||
if (props.onExpand) {
|
||||
return props.onExpand(keys, info);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
onClick = (event: React.MouseEvent<HTMLElement>, node: EventDataNode) => {
|
||||
const { onClick, expandAction } = this.props;
|
||||
const onClick = (event: React.MouseEvent<HTMLElement>, node: EventDataNode) => {
|
||||
const { expandAction } = props;
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'click') {
|
||||
this.onDebounceExpand(event, node);
|
||||
onDebounceExpand(event, node);
|
||||
}
|
||||
|
||||
if (onClick) {
|
||||
onClick(event, node);
|
||||
if (props.onClick) {
|
||||
props.onClick(event, node);
|
||||
}
|
||||
};
|
||||
|
||||
onDoubleClick = (event: React.MouseEvent<HTMLElement>, node: EventDataNode) => {
|
||||
const { onDoubleClick, expandAction } = this.props;
|
||||
const onDoubleClick = (event: React.MouseEvent<HTMLElement>, node: EventDataNode) => {
|
||||
const { expandAction } = props;
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'doubleClick') {
|
||||
this.onDebounceExpand(event, node);
|
||||
onDebounceExpand(event, node);
|
||||
}
|
||||
|
||||
if (onDoubleClick) {
|
||||
onDoubleClick(event, node);
|
||||
if (props.onDoubleClick) {
|
||||
props.onDoubleClick(event, node);
|
||||
}
|
||||
};
|
||||
|
||||
onSelect = (
|
||||
const onSelect = (
|
||||
keys: Key[],
|
||||
event: {
|
||||
event: 'select';
|
||||
@ -148,13 +154,12 @@ class DirectoryTree extends React.Component<DirectoryTreeProps, DirectoryTreeSta
|
||||
nativeEvent: MouseEvent;
|
||||
},
|
||||
) => {
|
||||
const { onSelect, multiple } = this.props;
|
||||
const { expandedKeys = [] } = this.state;
|
||||
const { multiple } = props;
|
||||
const { node, nativeEvent } = event;
|
||||
const { key = '' } = node;
|
||||
|
||||
const treeData = getTreeData(this.props);
|
||||
const newState: DirectoryTreeState = {};
|
||||
const treeData = getTreeData(props);
|
||||
// const newState: DirectoryTreeState = {};
|
||||
|
||||
// We need wrap this event since some value is not same
|
||||
const newEvent: any = {
|
||||
@ -171,90 +176,63 @@ class DirectoryTree extends React.Component<DirectoryTreeProps, DirectoryTreeSta
|
||||
if (multiple && ctrlPick) {
|
||||
// Control click
|
||||
newSelectedKeys = keys;
|
||||
this.lastSelectedKey = key;
|
||||
this.cachedSelectedKeys = newSelectedKeys;
|
||||
lastSelectedKey.current = key;
|
||||
cachedSelectedKeys.current = newSelectedKeys;
|
||||
newEvent.selectedNodes = convertDirectoryKeysToNodes(treeData, newSelectedKeys);
|
||||
} else if (multiple && shiftPick) {
|
||||
// Shift click
|
||||
newSelectedKeys = Array.from(
|
||||
new Set([
|
||||
...(this.cachedSelectedKeys || []),
|
||||
...calcRangeKeys(treeData, expandedKeys, key, this.lastSelectedKey),
|
||||
...(cachedSelectedKeys.current || []),
|
||||
...calcRangeKeys(treeData, expandedKeys, key, lastSelectedKey.current),
|
||||
]),
|
||||
);
|
||||
newEvent.selectedNodes = convertDirectoryKeysToNodes(treeData, newSelectedKeys);
|
||||
} else {
|
||||
// Single click
|
||||
newSelectedKeys = [key];
|
||||
this.lastSelectedKey = key;
|
||||
this.cachedSelectedKeys = newSelectedKeys;
|
||||
lastSelectedKey.current = key;
|
||||
cachedSelectedKeys.current = newSelectedKeys;
|
||||
newEvent.selectedNodes = convertDirectoryKeysToNodes(treeData, newSelectedKeys);
|
||||
}
|
||||
newState.selectedKeys = newSelectedKeys;
|
||||
|
||||
if (onSelect) {
|
||||
onSelect(newSelectedKeys, newEvent);
|
||||
if (props.onSelect) {
|
||||
props.onSelect(newSelectedKeys, newEvent);
|
||||
}
|
||||
|
||||
this.setUncontrolledState(newState);
|
||||
};
|
||||
|
||||
setTreeRef = (node: Tree) => {
|
||||
this.tree = node;
|
||||
};
|
||||
|
||||
expandFolderNode = (event: React.MouseEvent<HTMLElement>, node: any) => {
|
||||
const { isLeaf } = node;
|
||||
|
||||
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get internal rc-tree
|
||||
const internalTree = this.tree.tree;
|
||||
|
||||
// Call internal rc-tree expand function
|
||||
// https://github.com/ant-design/ant-design/issues/12567
|
||||
internalTree.onNodeExpand(event, node);
|
||||
};
|
||||
|
||||
setUncontrolledState = (state: DirectoryTreeState) => {
|
||||
const newState = omit(state, Object.keys(this.props));
|
||||
if (Object.keys(newState).length) {
|
||||
this.setState(newState);
|
||||
if (!('selectedKeys' in props)) {
|
||||
setSelectedKeys(newSelectedKeys);
|
||||
}
|
||||
};
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
|
||||
renderDirectoryTree = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const { prefixCls: customizePrefixCls, className, ...props } = this.props;
|
||||
const { expandedKeys, selectedKeys } = this.state;
|
||||
const { prefixCls: customizePrefixCls, className, ...otherProps } = props;
|
||||
|
||||
const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
||||
const connectClassName = classNames(`${prefixCls}-directory`, className, {
|
||||
[`${prefixCls}-directory-rtl`]: direction === 'rtl',
|
||||
});
|
||||
const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
||||
const connectClassName = classNames(`${prefixCls}-directory`, className, {
|
||||
[`${prefixCls}-directory-rtl`]: direction === 'rtl',
|
||||
});
|
||||
|
||||
return (
|
||||
<Tree
|
||||
icon={getIcon}
|
||||
ref={this.setTreeRef}
|
||||
blockNode
|
||||
{...props}
|
||||
prefixCls={prefixCls}
|
||||
className={connectClassName}
|
||||
expandedKeys={expandedKeys}
|
||||
selectedKeys={selectedKeys}
|
||||
onSelect={this.onSelect}
|
||||
onClick={this.onClick}
|
||||
onDoubleClick={this.onDoubleClick}
|
||||
onExpand={this.onExpand}
|
||||
/>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Tree
|
||||
icon={getIcon}
|
||||
ref={ref}
|
||||
blockNode
|
||||
{...otherProps}
|
||||
prefixCls={prefixCls}
|
||||
className={connectClassName}
|
||||
expandedKeys={expandedKeys}
|
||||
selectedKeys={selectedKeys}
|
||||
onSelect={onSelect}
|
||||
onClick={onClick}
|
||||
onDoubleClick={onDoubleClick}
|
||||
onExpand={onExpand}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderDirectoryTree}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
DirectoryTree.defaultProps = {
|
||||
showIcon: true,
|
||||
expandAction: 'click' as DirectoryTreeProps['expandAction'],
|
||||
};
|
||||
|
||||
export default DirectoryTree;
|
||||
|
@ -4,7 +4,7 @@ import classNames from 'classnames';
|
||||
import { DataNode, Key } from 'rc-tree/lib/interface';
|
||||
|
||||
import DirectoryTree from './DirectoryTree';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import collapseMotion from '../_util/motion';
|
||||
import renderSwitcherIcon from './utils/iconUtil';
|
||||
|
||||
@ -135,62 +135,58 @@ export interface TreeProps extends Omit<RcTreeProps, 'prefixCls'> {
|
||||
blockNode?: boolean;
|
||||
}
|
||||
|
||||
export default class Tree extends React.Component<TreeProps, any> {
|
||||
static TreeNode = TreeNode;
|
||||
|
||||
static DirectoryTree = DirectoryTree;
|
||||
|
||||
static defaultProps = {
|
||||
checkable: false,
|
||||
showIcon: false,
|
||||
motion: {
|
||||
...collapseMotion,
|
||||
motionAppear: false,
|
||||
},
|
||||
blockNode: false,
|
||||
};
|
||||
|
||||
tree: any;
|
||||
|
||||
setTreeRef = (node: any) => {
|
||||
this.tree = node;
|
||||
};
|
||||
|
||||
renderTree = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const { props } = this;
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
showIcon,
|
||||
showLine,
|
||||
switcherIcon,
|
||||
blockNode,
|
||||
children,
|
||||
} = props;
|
||||
const { checkable } = props;
|
||||
const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
||||
return (
|
||||
<RcTree
|
||||
itemHeight={20}
|
||||
ref={this.setTreeRef}
|
||||
{...props}
|
||||
prefixCls={prefixCls}
|
||||
className={classNames(className, {
|
||||
[`${prefixCls}-icon-hide`]: !showIcon,
|
||||
[`${prefixCls}-block-node`]: blockNode,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
})}
|
||||
checkable={checkable ? <span className={`${prefixCls}-checkbox-inner`} /> : checkable}
|
||||
switcherIcon={(nodeProps: AntTreeNodeProps) =>
|
||||
renderSwitcherIcon(prefixCls, switcherIcon, showLine, nodeProps)
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</RcTree>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderTree}</ConfigConsumer>;
|
||||
}
|
||||
interface CompoundedComponent
|
||||
extends React.ForwardRefExoticComponent<TreeProps & React.RefAttributes<RcTree>> {
|
||||
TreeNode: typeof TreeNode;
|
||||
DirectoryTree: typeof DirectoryTree;
|
||||
}
|
||||
|
||||
const Tree = React.forwardRef<RcTree, TreeProps>((props, ref) => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
showIcon,
|
||||
showLine,
|
||||
switcherIcon,
|
||||
blockNode,
|
||||
children,
|
||||
checkable,
|
||||
} = props;
|
||||
const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
||||
return (
|
||||
<RcTree
|
||||
itemHeight={20}
|
||||
ref={ref}
|
||||
{...props}
|
||||
prefixCls={prefixCls}
|
||||
className={classNames(className, {
|
||||
[`${prefixCls}-icon-hide`]: !showIcon,
|
||||
[`${prefixCls}-block-node`]: blockNode,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
})}
|
||||
checkable={checkable ? <span className={`${prefixCls}-checkbox-inner`} /> : checkable}
|
||||
switcherIcon={(nodeProps: AntTreeNodeProps) =>
|
||||
renderSwitcherIcon(prefixCls, switcherIcon, showLine, nodeProps)
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</RcTree>
|
||||
);
|
||||
}) as CompoundedComponent;
|
||||
|
||||
Tree.TreeNode = TreeNode;
|
||||
|
||||
Tree.DirectoryTree = DirectoryTree;
|
||||
|
||||
Tree.defaultProps = {
|
||||
checkable: false,
|
||||
showIcon: false,
|
||||
motion: {
|
||||
...collapseMotion,
|
||||
motionAppear: false,
|
||||
},
|
||||
blockNode: false,
|
||||
};
|
||||
|
||||
export default Tree;
|
||||
|
@ -46,36 +46,20 @@ describe('Directory Tree', () => {
|
||||
it('click', () => {
|
||||
const wrapper = mount(createTree());
|
||||
|
||||
wrapper
|
||||
.find(TreeNode)
|
||||
.find('.ant-tree-node-content-wrapper')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
jest.runAllTimers();
|
||||
wrapper
|
||||
.find(TreeNode)
|
||||
.find('.ant-tree-node-content-wrapper')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('double click', () => {
|
||||
const wrapper = mount(createTree({ expandAction: 'doubleClick' }));
|
||||
|
||||
wrapper
|
||||
.find(TreeNode)
|
||||
.find('.ant-tree-node-content-wrapper')
|
||||
.at(0)
|
||||
.simulate('doubleClick');
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('doubleClick');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
jest.runAllTimers();
|
||||
wrapper
|
||||
.find(TreeNode)
|
||||
.find('.ant-tree-node-content-wrapper')
|
||||
.at(0)
|
||||
.simulate('doubleClick');
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('doubleClick');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -106,11 +90,7 @@ describe('Directory Tree', () => {
|
||||
it(action, () => {
|
||||
const wrapper = mount(<StateDirTree expandAction={action} />);
|
||||
|
||||
wrapper
|
||||
.find(TreeNode)
|
||||
.find('.ant-tree-node-content-wrapper')
|
||||
.at(0)
|
||||
.simulate(action);
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate(action);
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
@ -179,20 +159,12 @@ describe('Directory Tree', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
wrapper
|
||||
.find(TreeNode)
|
||||
.find('.ant-tree-node-content-wrapper')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
expect(onSelect.mock.calls[0][1].selected).toBeTruthy();
|
||||
expect(onSelect.mock.calls[0][1].selectedNodes.length).toBe(1);
|
||||
|
||||
// Click twice should keep selected
|
||||
wrapper
|
||||
.find(TreeNode)
|
||||
.find('.ant-tree-node-content-wrapper')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
expect(onSelect.mock.calls[1][1].selected).toBeTruthy();
|
||||
expect(onSelect.mock.calls[0][0]).toEqual(onSelect.mock.calls[1][0]);
|
||||
expect(onSelect.mock.calls[1][1].selectedNodes.length).toBe(1);
|
||||
@ -201,11 +173,7 @@ describe('Directory Tree', () => {
|
||||
// Ref: https://github.com/facebook/react/blob/master/packages/react-dom/src/test-utils/ReactTestUtils.js#L360
|
||||
nativeEventProto.ctrlKey = true;
|
||||
|
||||
wrapper
|
||||
.find(TreeNode)
|
||||
.find('.ant-tree-node-content-wrapper')
|
||||
.at(1)
|
||||
.simulate('click');
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(1).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(onSelect.mock.calls[2][0].length).toBe(2);
|
||||
expect(onSelect.mock.calls[2][1].selected).toBeTruthy();
|
||||
@ -214,11 +182,7 @@ describe('Directory Tree', () => {
|
||||
delete nativeEventProto.ctrlKey;
|
||||
nativeEventProto.shiftKey = true;
|
||||
|
||||
wrapper
|
||||
.find(TreeNode)
|
||||
.find('.ant-tree-node-content-wrapper')
|
||||
.at(4)
|
||||
.simulate('click');
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(4).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(onSelect.mock.calls[3][0].length).toBe(5);
|
||||
expect(onSelect.mock.calls[3][1].selected).toBeTruthy();
|
||||
@ -226,4 +190,11 @@ describe('Directory Tree', () => {
|
||||
|
||||
delete nativeEventProto.shiftKey;
|
||||
});
|
||||
|
||||
it('onDoubleClick', () => {
|
||||
const onDoubleClick = jest.fn();
|
||||
const wrapper = mount(createTree({ onDoubleClick }));
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('doubleclick');
|
||||
expect(onDoubleClick).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
@ -96,6 +96,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-loading-icon {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
// >>> Checkbox
|
||||
@ -137,14 +141,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== Loading ======================
|
||||
.@{tree-node-prefix-cls}-loading {
|
||||
// Icon
|
||||
.@{tree-prefix-cls}-iconEle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Draggable =====================
|
||||
&-node-content-wrapper[draggable='true'] {
|
||||
line-height: @tree-title-height - 4px;
|
||||
|
@ -313,6 +313,7 @@ class Upload extends React.Component<UploadProps, UploadState> {
|
||||
[`${prefixCls}-drag-uploading`]: fileList.some(file => file.status === 'uploading'),
|
||||
[`${prefixCls}-drag-hover`]: dragState === 'dragover',
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
@ -3,7 +3,6 @@ import { mount } from 'enzyme';
|
||||
import Upload from '..';
|
||||
import UploadList from '../UploadList';
|
||||
import Form from '../../form';
|
||||
import { spyElementPrototypes } from '../../__tests__/util/domHook';
|
||||
import { errorRequest, successRequest } from './requests';
|
||||
import { setup, teardown } from './mock';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
@ -35,35 +34,16 @@ describe('Upload List', () => {
|
||||
function setSize(width, height) {
|
||||
size = { width, height };
|
||||
}
|
||||
const imageSpy = spyElementPrototypes(Image, {
|
||||
src: {
|
||||
set() {
|
||||
if (this.onload) {
|
||||
this.onload();
|
||||
}
|
||||
},
|
||||
},
|
||||
width: {
|
||||
get: () => size.width,
|
||||
},
|
||||
height: {
|
||||
get: () => size.height,
|
||||
},
|
||||
});
|
||||
const mockWidthGet = jest.spyOn(Image.prototype, 'width', 'get');
|
||||
const mockHeightGet = jest.spyOn(Image.prototype, 'height', 'get');
|
||||
const mockSrcSet = jest.spyOn(Image.prototype, 'src', 'set');
|
||||
|
||||
let drawImageCallback = null;
|
||||
function hookDrawImageCall(callback) {
|
||||
drawImageCallback = callback;
|
||||
}
|
||||
const canvasSpy = spyElementPrototypes(HTMLCanvasElement, {
|
||||
getContext: () => ({
|
||||
drawImage: (...args) => {
|
||||
if (drawImageCallback) drawImageCallback(...args);
|
||||
},
|
||||
}),
|
||||
|
||||
toDataURL: () => 'data:image/png;base64,',
|
||||
});
|
||||
const mockGetCanvasContext = jest.spyOn(HTMLCanvasElement.prototype, 'getContext');
|
||||
const mockToDataURL = jest.spyOn(HTMLCanvasElement.prototype, 'toDataURL');
|
||||
|
||||
// HTMLCanvasElement.prototype
|
||||
|
||||
@ -76,12 +56,29 @@ describe('Upload List', () => {
|
||||
let open;
|
||||
beforeAll(() => {
|
||||
open = jest.spyOn(window, 'open').mockImplementation(() => {});
|
||||
mockWidthGet.mockImplementation(() => size.width);
|
||||
mockHeightGet.mockImplementation(() => size.height);
|
||||
mockSrcSet.mockImplementation(function fn() {
|
||||
if (this.onload) {
|
||||
this.onload();
|
||||
}
|
||||
});
|
||||
|
||||
mockGetCanvasContext.mockReturnValue({
|
||||
drawImage: (...args) => {
|
||||
if (drawImageCallback) drawImageCallback(...args);
|
||||
},
|
||||
});
|
||||
mockToDataURL.mockReturnValue('data:image/png;base64,');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
window.URL.createObjectURL = originCreateObjectURL;
|
||||
imageSpy.mockRestore();
|
||||
canvasSpy.mockRestore();
|
||||
mockWidthGet.mockRestore();
|
||||
mockHeightGet.mockRestore();
|
||||
mockSrcSet.mockRestore();
|
||||
mockGetCanvasContext.mockRestore();
|
||||
mockToDataURL.mockRestore();
|
||||
open.mockRestore();
|
||||
});
|
||||
|
||||
|
@ -59,6 +59,9 @@
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
.@{upload-prefix-cls}-disabled& {
|
||||
border-color: @border-color-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ According to our [release schedule](changelog#Release-Schedule), we maintain two
|
||||
|
||||
We are using [GitHub Issues](https://github.com/ant-design/ant-design/issues) for bug tracking. The best way to get your bug fixed is using our [issue helper](http://new-issue.ant.design) and provide reproduction steps with this [template](https://u.ant.design/codesandbox-repro).
|
||||
|
||||
Before you report a bug, please make sure you've searched exists issues, and read our [FAQ](/docs/react/faq).
|
||||
Before you report a bug, please make sure you've searched existing issues, and read our [FAQ](/docs/react/faq).
|
||||
|
||||
## Proposing a Change
|
||||
|
||||
|
@ -51,14 +51,14 @@ module.exports = {
|
||||
}, {
|
||||
loader: 'less-loader', // compiles Less to CSS
|
||||
+ options: {
|
||||
+ modifyVars: {
|
||||
+ 'primary-color': '#1DA57A',
|
||||
+ 'link-color': '#1DA57A',
|
||||
+ 'border-radius-base': '2px',
|
||||
+ // or
|
||||
+ 'hack': `true; @import "your-less-file-path.less";`, // Override with less file
|
||||
+ lessOptions: { // If you are using less-loader@5 please spread the lessOptions to options directly
|
||||
+ modifyVars: {
|
||||
+ 'primary-color': '#1DA57A',
|
||||
+ 'link-color': '#1DA57A',
|
||||
+ 'border-radius-base': '2px',
|
||||
+ },
|
||||
+ javascriptEnabled: true,
|
||||
+ },
|
||||
+ javascriptEnabled: true,
|
||||
+ },
|
||||
}],
|
||||
// ...other rules
|
||||
@ -67,7 +67,10 @@ module.exports = {
|
||||
}
|
||||
```
|
||||
|
||||
Note that do not exclude antd package in node_modules when using less-loader.
|
||||
Note:
|
||||
|
||||
1. Don't exclude `node_modules/antd` when using less-loader.
|
||||
2. `lessOptions` usage is supported at [less-loader@6.0.0](https://github.com/webpack-contrib/less-loader/releases/tag/v6.0.0).
|
||||
|
||||
### Customize in Umi
|
||||
|
||||
@ -195,16 +198,17 @@ module.exports = {
|
||||
}, {
|
||||
loader: 'less-loader', // compiles Less to CSS
|
||||
+ options: {
|
||||
+ modifyVars: getThemeVariables({
|
||||
+ dark: true, // enable dark mode
|
||||
+ compact: true, // enable compact mode
|
||||
+ }),
|
||||
+ javascriptEnabled: true,
|
||||
+ lessOptions: { // If you are using less-loader@5 please spread the lessOptions to options directly
|
||||
+ modifyVars: getThemeVariables({
|
||||
+ dark: true, // 开启暗黑模式
|
||||
+ compact: true, // 开启紧凑模式
|
||||
+ }),
|
||||
+ javascriptEnabled: true,
|
||||
+ },
|
||||
+ },
|
||||
}],
|
||||
}],
|
||||
};
|
||||
```
|
||||
|
||||
## Related Articles
|
||||
|
||||
@ -213,3 +217,4 @@ module.exports = {
|
||||
- [Theming Ant Design with Sass and Webpack](https://gist.github.com/Kruemelkatze/057f01b8e15216ae707dc7e6c9061ef7)
|
||||
- [Using Sass/Scss with React App (create-react-app)](https://medium.com/@mzohaib.qc/using-sass-scss-with-react-app-create-react-app-d03072083ef8)
|
||||
- [Dynamic Theming in Browser using Ant Design](https://medium.com/@mzohaib.qc/ant-design-dynamic-runtime-theme-1f9a1a030ba0)
|
||||
```
|
||||
|
@ -51,14 +51,14 @@ module.exports = {
|
||||
}, {
|
||||
loader: 'less-loader', // compiles Less to CSS
|
||||
+ options: {
|
||||
+ modifyVars: {
|
||||
+ 'primary-color': '#1DA57A',
|
||||
+ 'link-color': '#1DA57A',
|
||||
+ 'border-radius-base': '2px',
|
||||
+ // or
|
||||
+ 'hack': `true; @import "your-less-file-path.less";`, // Override with less file
|
||||
+ lessOptions: { // 如果使用less-loader@5,请移除 lessOptions 这一级直接配置选项。
|
||||
+ modifyVars: {
|
||||
+ 'primary-color': '#1DA57A',
|
||||
+ 'link-color': '#1DA57A',
|
||||
+ 'border-radius-base': '2px',
|
||||
+ },
|
||||
+ javascriptEnabled: true,
|
||||
+ },
|
||||
+ javascriptEnabled: true,
|
||||
+ },
|
||||
}],
|
||||
// ...other rules
|
||||
@ -67,7 +67,10 @@ module.exports = {
|
||||
}
|
||||
```
|
||||
|
||||
注意 less-loader 的处理范围不要过滤掉 `node_modules` 下的 antd 包。
|
||||
注意:
|
||||
|
||||
1. less-loader 的处理范围不要过滤掉 `node_modules` 下的 antd 包。
|
||||
2. `lessOptions` 的配置写法在 [less-loader@6.0.0](https://github.com/webpack-contrib/less-loader/releases/tag/v6.0.0) 里支持。
|
||||
|
||||
### 在 Umi 里配置主题
|
||||
|
||||
@ -173,11 +176,13 @@ module.exports = {
|
||||
}, {
|
||||
loader: 'less-loader', // compiles Less to CSS
|
||||
+ options: {
|
||||
+ modifyVars: getThemeVariables({
|
||||
+ dark: true, // 开启暗黑模式
|
||||
+ compact: true, // 开启紧凑模式
|
||||
+ }),
|
||||
+ javascriptEnabled: true,
|
||||
+ lessOptions: { // 如果使用less-loader@5,请移除 lessOptions 这一级直接配置选项。
|
||||
+ modifyVars: getThemeVariables({
|
||||
+ dark: true, // 开启暗黑模式
|
||||
+ compact: true, // 开启紧凑模式
|
||||
+ }),
|
||||
+ javascriptEnabled: true,
|
||||
+ },
|
||||
+ },
|
||||
}],
|
||||
}],
|
||||
|
@ -36,7 +36,7 @@ Following the Ant Design specification, we developed a React UI library `antd` t
|
||||
|
||||
## Environment Support
|
||||
|
||||
- Modern browsers and Internet Explorer 11+ (with [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
|
||||
- Modern browsers and Internet Explorer 11 (with [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
|
||||
- Server-side Rendering
|
||||
- [Electron](https://www.electronjs.org/)
|
||||
|
||||
@ -76,6 +76,7 @@ We provide `antd.js` `antd.css` and `antd.min.js` `antd.min.css` under `antd/dis
|
||||
|
||||
```jsx
|
||||
import { DatePicker } from 'antd';
|
||||
|
||||
ReactDOM.render(<DatePicker />, mountNode);
|
||||
```
|
||||
|
||||
@ -122,7 +123,7 @@ import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
|
||||
## Links
|
||||
|
||||
- [Home page](https://ant.design/)
|
||||
- [Components](/docs/react/introduce)
|
||||
- [Components](https://ant.design/components/button/)
|
||||
- [Ant Design Pro](https://pro.ant.design/)
|
||||
- [Change Log](/changelog)
|
||||
- [rc-components](http://react-component.github.io/)
|
||||
|
@ -36,7 +36,7 @@ title: Ant Design of React
|
||||
|
||||
## 支持环境
|
||||
|
||||
- 现代浏览器和 IE11 及以上(需要 [polyfills](https://ant.design/docs/react/getting-started-cn#兼容性))。
|
||||
- 现代浏览器和 IE11(需要 [polyfills](https://ant.design/docs/react/getting-started-cn#兼容性))。
|
||||
- 支持服务端渲染。
|
||||
- [Electron](https://electronjs.org/)
|
||||
|
||||
@ -76,6 +76,7 @@ $ yarn add antd
|
||||
|
||||
```jsx
|
||||
import { DatePicker } from 'antd';
|
||||
|
||||
ReactDOM.render(<DatePicker />, mountNode);
|
||||
```
|
||||
|
||||
@ -124,7 +125,7 @@ import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
|
||||
## 链接
|
||||
|
||||
- [首页](https://ant.design/)
|
||||
- [组件库](/docs/react/introduce)
|
||||
- [组件库](https://ant.design/components/button-cn/)
|
||||
- [Ant Design Pro](https://pro.ant.design/)
|
||||
- [更新日志](/changelog)
|
||||
- [React 底层基础组件](http://react-component.github.io/)
|
||||
|
@ -123,6 +123,7 @@ const Demo = () => (
|
||||
- AutoComplete no longer support `optionLabelProp`. Please set Option `value` directly.
|
||||
- Select remove `dropdownMenuStyle` prop.
|
||||
- Use `listHeight` to config popup height instead of `dropdownStyle`.
|
||||
- `filterOption` return origin data with second params instead. No need to use `option.props.children` for matching.
|
||||
- The Grid component uses flex layout.
|
||||
- Button's `danger` is now treated as a property instead of a button type.
|
||||
- Input, Select set `value` to `undefined` is uncontrolled mode now.
|
||||
|
@ -123,6 +123,7 @@ const Demo = () => (
|
||||
- AutoComplete 不再支持 `optionLabelProp`,请直接设置 Option `value` 属性。
|
||||
- Select 移除 `dropdownMenuStyle` 属性。
|
||||
- 如果你需要设置弹窗高度请使用 `listHeight` 来代替 `dropdownStyle` 的高度样式。
|
||||
- `filterOption` 第二个参数直接返回原数据,不在需要通过 `option.props.children` 来进行匹配。
|
||||
- Grid 组件使用 flex 布局。
|
||||
- Button 的 `danger` 现在作为一个属性而不是按钮类型。
|
||||
- Input、Select 的 `value` 为 `undefined` 时改为非受控状态。
|
||||
|
@ -191,8 +191,10 @@ module.exports = override(
|
||||
+ style: true,
|
||||
}),
|
||||
+ addLessLoader({
|
||||
+ javascriptEnabled: true,
|
||||
+ modifyVars: { '@primary-color': '#1DA57A' },
|
||||
+ lessOptions: { // If you are using less-loader@5 please spread the lessOptions to options directly
|
||||
+ javascriptEnabled: true,
|
||||
+ modifyVars: { '@primary-color': '#1DA57A' },
|
||||
+ },
|
||||
+ }),
|
||||
);
|
||||
```
|
||||
|
@ -191,8 +191,10 @@ module.exports = override(
|
||||
+ style: true,
|
||||
}),
|
||||
+ addLessLoader({
|
||||
+ javascriptEnabled: true,
|
||||
+ modifyVars: { '@primary-color': '#1DA57A' },
|
||||
+ lessOptions: { // 如果使用less-loader@5,请移除 lessOptions 这一级直接配置选项。
|
||||
+ javascriptEnabled: true,
|
||||
+ modifyVars: { '@primary-color': '#1DA57A' },
|
||||
+ },
|
||||
+ }),
|
||||
);
|
||||
```
|
||||
|
@ -21,7 +21,7 @@ The table is recognized as one of the clearest and most efficient forms of prese
|
||||
> Note:
|
||||
>
|
||||
> 1. The time, status, and action bar in the table need to keep the words intact without occupying multiple lines.
|
||||
> 2. When the data is empty, use "- -" to indicate that there is no data.
|
||||
> 2. When table cell is empty, use `-` to indicate that there is no data.
|
||||
|
||||
## Collapse
|
||||
|
||||
|
@ -21,7 +21,7 @@ title: 数据展示
|
||||
> 注:
|
||||
>
|
||||
> 1. 表格中的时间、状态、操作栏需保持词语完整不过行。
|
||||
> 2. 当数据为空时,可使用『- -』来表示暂无数据。
|
||||
> 2. 当单元格数据为空时,可使用 `-` 来表示暂无数据。
|
||||
|
||||
## 折叠面板(Collapse)
|
||||
|
||||
|
@ -83,6 +83,11 @@ const LinksList = () => (
|
||||
antue (vue)<LinkIcon />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://append-it.github.io/ant-design-blazor/" target="_blank">
|
||||
Ant Design of Blazor<LinkIcon />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
|
||||
|
@ -83,6 +83,9 @@ const LinksList = () => (
|
||||
antue (vue)<LinkIcon />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://append-it.github.io/ant-design-blazor/" target="_blank">Ant Design of Blazor<LinkIcon /></a>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
|
||||
|
@ -100,7 +100,7 @@ title: 结果页
|
||||
|
||||
#### 补充信息类型
|
||||
|
||||
<img class="preview-img no-padding" align="right" src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*QjpBRpyx5ecAAAAAAAAAAABkARQnAQ">
|
||||
<img class="preview-img no-padding" align="right" src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*avS5TZcMawwAAAAAAAAAAABkARQnAQ">
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
|
11
package.json
11
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "antd",
|
||||
"version": "4.1.4",
|
||||
"version": "4.1.5",
|
||||
"description": "An enterprise-class UI design language and React components implementation",
|
||||
"keywords": [
|
||||
"ant",
|
||||
@ -107,7 +107,7 @@
|
||||
"omit.js": "^1.0.2",
|
||||
"prop-types": "^15.7.2",
|
||||
"raf": "^3.4.1",
|
||||
"rc-animate": "~2.10.2",
|
||||
"rc-animate": "~2.11.0",
|
||||
"rc-cascader": "~1.0.0",
|
||||
"rc-checkbox": "~2.2.0",
|
||||
"rc-collapse": "~1.11.3",
|
||||
@ -191,7 +191,7 @@
|
||||
"eslint-plugin-markdown": "^1.0.0",
|
||||
"eslint-plugin-react": "^7.14.2",
|
||||
"eslint-plugin-react-hooks": "^3.0.0",
|
||||
"eslint-plugin-unicorn": "^18.0.1",
|
||||
"eslint-plugin-unicorn": "^19.0.0",
|
||||
"eslint-tinker": "^0.5.0",
|
||||
"fetch-jsonp": "^1.1.3",
|
||||
"fs-extra": "^9.0.0",
|
||||
@ -202,7 +202,7 @@
|
||||
"ignore-emit-webpack-plugin": "^2.0.2",
|
||||
"immutability-helper": "^3.0.0",
|
||||
"inquirer": "^7.1.0",
|
||||
"intersection-observer": "^0.9.0",
|
||||
"intersection-observer": "^0.10.0",
|
||||
"jest": "^25.1.0",
|
||||
"jquery": "^3.4.1",
|
||||
"jsdom": "^16.0.0",
|
||||
@ -287,5 +287,8 @@
|
||||
"maxSize": "65 kB"
|
||||
}
|
||||
],
|
||||
"tnpm": {
|
||||
"mode": "npm"
|
||||
},
|
||||
"title": "Ant Design"
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ const cheerio = require('cheerio');
|
||||
const glob = require('glob');
|
||||
const uniq = require('lodash/uniq');
|
||||
const { createServer } = require('http-server');
|
||||
const zhCN = require('../site/theme/zh-CN');
|
||||
const enUS = require('../site/theme/en-US');
|
||||
|
||||
const components = uniq(
|
||||
glob
|
||||
@ -43,11 +41,7 @@ describe('site test', () => {
|
||||
const expectComponent = async component => {
|
||||
const { status, $ } = await render(`/${component}/`);
|
||||
expect(status).toBe(200);
|
||||
expect(
|
||||
$('.markdown > h1')
|
||||
.text()
|
||||
.toLowerCase(),
|
||||
).toMatch(handleComponentName(component));
|
||||
expect($('.markdown > h1').text().toLowerCase()).toMatch(handleComponentName(component));
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
@ -67,13 +61,15 @@ describe('site test', () => {
|
||||
|
||||
it('Basic Pages en', async () => {
|
||||
const { status, $ } = await render('/');
|
||||
expect($('title').text()).toEqual(`Ant Design - ${enUS.messages['app.home.slogan']}`);
|
||||
expect($('title').text()).toEqual(
|
||||
`Ant Design - The world's second most popular React UI framework`,
|
||||
);
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('Basic Pages zh', async () => {
|
||||
const { status, $ } = await render('/index-cn');
|
||||
expect($('title').text()).toEqual(`Ant Design - ${zhCN.messages['app.home.slogan']}`);
|
||||
expect($('title').text()).toEqual(`Ant Design - 一套企业级 UI 设计语言和 React 组件库`);
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
|
@ -31,7 +31,6 @@ module.exports = {
|
||||
'app.demo.codesandbox': 'Open in CodeSandbox',
|
||||
'app.demo.stackblitz': 'Open in Stackblitz',
|
||||
'app.demo.riddle': 'Open in Riddle',
|
||||
'app.home.slogan': 'A UI Design Language and React UI library',
|
||||
'app.home.introduce':
|
||||
'A design system for enterprise-level products. Create an efficient and enjoyable work experience.',
|
||||
'app.home.recommend': 'Recommend',
|
||||
|
@ -1,3 +1,7 @@
|
||||
html.rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
body {
|
||||
color: @site-text-color;
|
||||
font-size: 14px;
|
||||
@ -171,15 +175,6 @@ a {
|
||||
height: 100%;
|
||||
transition: transform 0.3s @ease-in-out-circ;
|
||||
}
|
||||
.page-wrapper {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-content {
|
||||
padding: 40px 0;
|
||||
@ -224,7 +219,7 @@ a {
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
box-shadow: @shadow-2;
|
||||
transition: color .3s;
|
||||
transition: color 0.3s;
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
@ -17,14 +17,14 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
&-item {
|
||||
.page-wrapper-rtl & {
|
||||
html.rtl & {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
.page-wrapper-rtl & {
|
||||
html.rtl & {
|
||||
margin-right: 0;
|
||||
margin-left: 0.4em;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
padding-right: 16px;
|
||||
padding-left: 40px;
|
||||
|
||||
.page-wrapper-rtl & {
|
||||
html.rtl & {
|
||||
padding-right: 40px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
@ -251,6 +251,16 @@ export default function DesignPage() {
|
||||
<RightOutlined className="home-link-arrow" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://append-it.github.io/ant-design-blazor/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Ant Design of Blazor
|
||||
<RightOutlined className="home-link-arrow" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</Col>
|
||||
<Col xs={24} sm={15} style={{ alignSelf: 'flex-end', textAlign: 'right' }}>
|
||||
|
@ -27,7 +27,7 @@
|
||||
font-weight: 200;
|
||||
font-size: 16px;
|
||||
|
||||
.page-wrapper-rtl & {
|
||||
html.rtl & {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,11 @@ class Footer extends React.Component<WrappedComponentProps> {
|
||||
url: 'https://vue.ant.design',
|
||||
openExternal: true,
|
||||
},
|
||||
{
|
||||
title: 'Ant Design Blazor',
|
||||
url: 'https://append-it.github.io/ant-design-blazor/',
|
||||
openExternal: true,
|
||||
},
|
||||
{
|
||||
title: 'Ant Design Landing',
|
||||
description: <FormattedMessage id="app.footer.landing" />,
|
||||
|
@ -37,6 +37,16 @@ export function getEcosystemGroup({ isZhCN }: SharedProps): React.ReactElement {
|
||||
Ant Design of Vue
|
||||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="blazor">
|
||||
<a
|
||||
href="https://append-it.github.io/ant-design-blazor/"
|
||||
className="header-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Ant Design of Blazor
|
||||
</a>
|
||||
</Menu.Item>
|
||||
{isZhCN ? (
|
||||
<Menu.Item key="course" className="hide-in-home-page">
|
||||
<a
|
||||
|
@ -95,6 +95,11 @@ export default ({
|
||||
<FormattedMessage id="app.header.menu.resource" />
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
{isZhCN && (
|
||||
<Menu.Item key="mirror">
|
||||
<a href="https://ant-design.gitee.io">国内镜像</a>
|
||||
</Menu.Item>
|
||||
)}
|
||||
{additional}
|
||||
</Menu>
|
||||
);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user