mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-04 13:08:41 +08:00
commit
92fbfd1b93
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,9 +1,14 @@
|
||||
<!--
|
||||
First of all, thank you for your contribution! 😄
|
||||
|
||||
New feature please send a pull request to feature branch, and rest to master branch.
|
||||
Pull requests will be merged after one of the collaborators approve.
|
||||
Please makes sure that these forms are filled before submitting your pull request, thank you!
|
||||
For requesting to pull a new feature or bugfix, please send it from a feature/bugfix branch based on the `master` branch.
|
||||
|
||||
Before submitting your pull request, please make sure the checklist below is confirmed.
|
||||
|
||||
Your pull requests will be merged after one of the collaborators approve.
|
||||
|
||||
Thank you!
|
||||
|
||||
-->
|
||||
|
||||
[[中文版模板 / Chinese template](https://github.com/ant-design/ant-design/blob/master/.github/PULL_REQUEST_TEMPLATE/pr_cn.md)]
|
||||
@ -29,7 +34,7 @@ Please makes sure that these forms are filled before submitting your pull reques
|
||||
### 🔗 Related issue link
|
||||
|
||||
<!--
|
||||
1. Describe the source of requirement, like related issue link.
|
||||
1. Put the related issue or discussion links here.
|
||||
-->
|
||||
|
||||
### 💡 Background and solution
|
||||
@ -37,7 +42,7 @@ Please makes sure that these forms are filled before submitting your pull reques
|
||||
<!--
|
||||
1. Describe the problem and the scenario.
|
||||
2. GIF or snapshot should be provided if includes UI/interactive modification.
|
||||
3. How to fix the problem, and list final API implementation and usage sample if that is a new feature.
|
||||
3. How to fix the problem, and list the final API implementation and usage sample if that is a new feature.
|
||||
-->
|
||||
|
||||
### 📝 Changelog
|
||||
@ -51,7 +56,7 @@ Describe changes from the user side, and list all potential break changes or oth
|
||||
| 🇺🇸 English | |
|
||||
| 🇨🇳 Chinese | |
|
||||
|
||||
### ☑️ Self Check before Merge
|
||||
### ☑️ Self-Check before Merge
|
||||
|
||||
⚠️ Please check all items below before review. ⚠️
|
||||
|
||||
|
2
.github/workflows/release-helper.yml
vendored
2
.github/workflows/release-helper.yml
vendored
@ -28,7 +28,6 @@ jobs:
|
||||
branch: 'master'
|
||||
dingding-token: ${{ secrets.DINGDING_BOT_TOKEN }}
|
||||
dingding-msg: 'CHANGELOG.zh-CN.md'
|
||||
dingding-delay-minute: 10
|
||||
msg-title: '# Ant Design {{v}} 发布日志'
|
||||
msg-poster: 'https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*zx7LTI_ECSAAAAAAAAAAAABkARQnAQ'
|
||||
msg-footer: '💬 前往 [**Ant Design Releases**]({{url}}) 查看更新日志'
|
||||
@ -44,6 +43,7 @@ jobs:
|
||||
dingding-token: ${{ secrets.DINGDING_BOT_BIGFISH_TOKEN }}
|
||||
dingding-msg: 'CHANGELOG.zh-CN.md'
|
||||
dingding-delay-minute: 10
|
||||
release: false
|
||||
antd-conch-msg: '🐟 当前 Bigfish 内嵌 antd 版本:'
|
||||
msg-title: '# Ant Design {{v}} 发布日志'
|
||||
msg-poster: 'https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*zx7LTI_ECSAAAAAAAAAAAABkARQnAQ'
|
||||
|
31
.github/workflows/size-limit.yml
vendored
Normal file
31
.github/workflows/size-limit.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: 📦 Compressed Size(size-limit)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
# Cancel prev CI if new commit come
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
compressed-size:
|
||||
permissions:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
pull-requests: write # for preactjs/compressed-size-action to create PR comments
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_JOB_NUMBER: 1
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: andresz1/size-limit-action@v1
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
pattern: "./dist/**/*.min.{js,css}"
|
||||
build-script: "dist:esbuild-no-dup-check"
|
||||
clean-script: "clean-lockfiles"
|
@ -15,6 +15,45 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.22.2
|
||||
|
||||
`2022-07-28`
|
||||
|
||||
- 💄 Adjust Collapse title click region which will be fully width when `collapsible=default` now. [#36761](https://github.com/ant-design/ant-design/pull/36761)
|
||||
- Drawer
|
||||
- 🐞 Fix Drawer not work in 360 browser. [#36748](https://github.com/ant-design/ant-design/pull/36748)
|
||||
- 🐞 Revert back panel style into wrapper dom node in case developer use `contentWrapperStyle` for overwrite. [#36748](https://github.com/ant-design/ant-design/pull/36748)
|
||||
- 🐞 Fix for the string type as `width/height` value and warning which should use number type instead. [#284](https://github.com/react-component/drawer/pull/284)
|
||||
|
||||
## 4.22.1
|
||||
|
||||
`2022-07-27`
|
||||
|
||||
- 🐞 Fix Drawer with percentage width display issue. [#36729](https://github.com/ant-design/ant-design/pull/36729)
|
||||
|
||||
## 4.22.0
|
||||
|
||||
`2022-07-26`
|
||||
|
||||
- Form
|
||||
- 🔥 Form support `Form.Item.useStatus` for custom components to get Form.Item validate status. [#36486](https://github.com/ant-design/ant-design/pull/36486)
|
||||
- 🆕 Form support `setFieldValue` to simplify config array index value. [#36058](https://github.com/ant-design/ant-design/pull/36058)
|
||||
- 🐞 Fix Form.Item shaking when trigger validate so fast. [#36575](https://github.com/ant-design/ant-design/pull/36575)
|
||||
- 🆕 Radio.Group support `onBlur` and `onFocus` props. [#36041](https://github.com/ant-design/ant-design/pull/36041)
|
||||
- 🆕 Typography `ellipsis.tooltip` supports a object for Tooltip props. [#36099](https://github.com/ant-design/ant-design/pull/36099)
|
||||
- 🛠 Refactor Drawer to remove directly style control which helps more React way. [#36672](https://github.com/ant-design/ant-design/pull/36672)
|
||||
- 🛠 Refactor Sketelon.Button square shape style that its width is equal to height, and old become the default. [#36123](https://github.com/ant-design/ant-design/pull/36123) [@alanhaledc](https://github.com/alanhaledc)
|
||||
- 🐞 Fix Modal.confirm `onCancel` argument close is not a function sometimes. [#36600](https://github.com/ant-design/ant-design/pull/36600) [@Wxh16144](https://github.com/Wxh16144)
|
||||
- 🐞 Revert [#36439](https://github.com/ant-design/ant-design/pull/36439) to fix the problem of incorrect status when uploading and deleting files, and fix the status color change when Upload removes files again change problem. [#36706](https://github.com/ant-design/ant-design/pull/36706)
|
||||
- Tree
|
||||
- 🛠 Tree/TreeSelect `switcherIcon` argument support more parameters from `{ expanded: boolean }` to `AntTreeNodeProps`. [#36651](https://github.com/ant-design/ant-design/pull/36651) [@alanhaledc](https://github.com/alanhaledc)
|
||||
- 🐞 Fix Tree `draggable` Fn params type from AntTreeNode to DataNode. [#36648](https://github.com/ant-design/ant-design/pull/36648) [@tianyuan233](https://github.com/tianyuan233)
|
||||
- Table
|
||||
- 💄 Fix Table extra shadow and scrollbar when all columns are fixed. [#36606](https://github.com/ant-design/ant-design/pull/36606) [@dashaowang](https://github.com/dashaowang)
|
||||
- 💄 Fix Table tree data ellipsis style problem. [#36608](https://github.com/ant-design/ant-design/pull/36608)
|
||||
- 🌐 Localization
|
||||
- 🇱🇰 Add Sri Lanka locale. [#36149](https://github.com/ant-design/ant-design/pull/36149) [@sayuri-gi](https://github.com/sayuri-gi)
|
||||
|
||||
## 4.21.7
|
||||
|
||||
`2022-07-18`
|
||||
|
@ -15,6 +15,45 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.22.2
|
||||
|
||||
`2022-07-28`
|
||||
|
||||
- 💄 调整 Collapse 标题文本在 `collapsible=default` 时为完整宽度点击区域。[#36761](https://github.com/ant-design/ant-design/pull/36761)
|
||||
- Drawer
|
||||
- 🐞 修复 Drawer 在 360 浏览器不生效的问题。[#36748](https://github.com/ant-design/ant-design/pull/36748)
|
||||
- 🐞 回滚将样式恢复至包裹层以防止原本通过 `contentWrapperStyle` 覆盖样式的用法。[#36748](https://github.com/ant-design/ant-design/pull/36748)
|
||||
- 🐞 修复兼容以 string 类型作为 `width/height` 的用法,并且警告用户应当使用 number 类型。[#284](https://github.com/react-component/drawer/pull/284)
|
||||
|
||||
## 4.22.1
|
||||
|
||||
`2022-07-27`
|
||||
|
||||
- 🐞 修复 Drawer 使用百分比宽度时的展示问题。[#36729](https://github.com/ant-design/ant-design/pull/36729)
|
||||
|
||||
## 4.22.0
|
||||
|
||||
`2022-07-26`
|
||||
|
||||
- Form
|
||||
- 🔥 Form 新增 `Form.Item.useStatus` 用于获取 Form.Item 的校验状态。[#36486](https://github.com/ant-design/ant-design/pull/36486)
|
||||
- 🆕 Form 支持 `setFieldValue` 以简化设置数字单个值的操作流程。[#36058](https://github.com/ant-design/ant-design/pull/36058)
|
||||
- 🐞 修复 Form.Item 在快速切换校验状态时高度抖动的问题。[#36575](https://github.com/ant-design/ant-design/pull/36575)
|
||||
- 🆕 Radio.Group 支持 `onBlur` 和 `onFocus` 属性。[#36041](https://github.com/ant-design/ant-design/pull/36041)
|
||||
- 🆕 Typography `ellipsis.tooltip` 属性支持传入一个对象。[#36099](https://github.com/ant-design/ant-design/pull/36099)
|
||||
- 🛠 重构 Drawer 移除直接的 dom 操作以使其更符合 React 运作方式。[#36672](https://github.com/ant-design/ant-design/pull/36672)
|
||||
- 🛠 重构 Sketelon.Button square shape 样式为宽高相等,之前的 square 改为默认样式。[#36123](https://github.com/ant-design/ant-design/pull/36123) [@alanhaledc](https://github.com/alanhaledc)
|
||||
- 🐞 修复 Modal.confirm 中 `onCancel(close)` 参数有时候不是 function 的问题。[#36600](https://github.com/ant-design/ant-design/pull/36600) [@Wxh16144](https://github.com/Wxh16144)
|
||||
- 🐞 回滚 [#36439](https://github.com/ant-design/ant-design/pull/36439) 以修复上传和删除文件时状态不对的问题,并再次修复 Upload 移除文件时状态色会变化的问题。[#36706](https://github.com/ant-design/ant-design/pull/36706)
|
||||
- Tree
|
||||
- 🛠 Tree/TreeSelect `switcherIcon` 参数现在支持完整 TreeNode 属性,从 `{ expanded: boolean }` 变为 `AntTreeNodeProps`。[#36651](https://github.com/ant-design/ant-design/pull/36651) [@alanhaledc](https://github.com/alanhaledc)
|
||||
- 🐞 修改 Tree `draggable` 函数的参数类型由 AntTreeNode 改为 DataNode。[#36648](https://github.com/ant-design/ant-design/pull/36648) [@tianyuan233](https://github.com/tianyuan233)
|
||||
- Table
|
||||
- 💄 修复 Table 固定列额外阴影和滚动条样式的问题。[#36606](https://github.com/ant-design/ant-design/pull/36606) [@dashaowang](https://github.com/dashaowang)
|
||||
- 💄 修复 Table 树形数据固定列的省略样式错位的问题。[#36608](https://github.com/ant-design/ant-design/pull/36608)
|
||||
- 🌐 国际化
|
||||
- 🇱🇰 添加斯里兰卡语言。[#36149](https://github.com/ant-design/ant-design/pull/36149) [@sayuri-gi](https://github.com/sayuri-gi)
|
||||
|
||||
## 4.21.7
|
||||
|
||||
`2022-07-18`
|
||||
|
@ -1,4 +1,4 @@
|
||||
export function isWindow(obj: any) {
|
||||
export function isWindow(obj: any): obj is Window {
|
||||
return obj !== null && obj !== undefined && obj === obj.window;
|
||||
}
|
||||
|
||||
@ -12,16 +12,22 @@ export default function getScroll(
|
||||
const method = top ? 'scrollTop' : 'scrollLeft';
|
||||
let result = 0;
|
||||
if (isWindow(target)) {
|
||||
result = (target as Window)[top ? 'pageYOffset' : 'pageXOffset'];
|
||||
result = target[top ? 'pageYOffset' : 'pageXOffset'];
|
||||
} else if (target instanceof Document) {
|
||||
result = target.documentElement[method];
|
||||
} else if (target instanceof HTMLElement) {
|
||||
result = target[method];
|
||||
} else if (target) {
|
||||
result = (target as HTMLElement)[method];
|
||||
// According to the type inference, the `target` is `never` type.
|
||||
// Since we configured the loose mode type checking, and supports mocking the target with such shape below::
|
||||
// `{ documentElement: { scrollLeft: 200, scrollTop: 400 } }`,
|
||||
// the program may falls into this branch.
|
||||
// Check the corresponding tests for details. Don't sure what is the real scenario this happens.
|
||||
result = target[method];
|
||||
}
|
||||
|
||||
if (target && !isWindow(target) && typeof result !== 'number') {
|
||||
result = ((target as HTMLElement).ownerDocument || (target as Document)).documentElement?.[
|
||||
method
|
||||
];
|
||||
result = (target.ownerDocument ?? target).documentElement?.[method];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -1619,130 +1619,9 @@ exports[`Cascader rtl render component should be rendered correctly in RTL direc
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Cascader should highlight keyword and filter when search in Cascader 1`] = `
|
||||
<div
|
||||
class="ant-select-dropdown ant-cascader-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
|
||||
style="opacity: 0; min-width: 0;"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-cascader-menus"
|
||||
>
|
||||
<ul
|
||||
class="ant-cascader-menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item"
|
||||
data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou__RC_CASCADER_SPLIT__xihu"
|
||||
role="menuitemcheckbox"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
<span
|
||||
class="ant-cascader-menu-item-keyword"
|
||||
>
|
||||
Z
|
||||
</span>
|
||||
hejiang / Hang
|
||||
<span
|
||||
class="ant-cascader-menu-item-keyword"
|
||||
>
|
||||
z
|
||||
</span>
|
||||
hou / West Lake
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item"
|
||||
data-path-key="jiangsu__RC_CASCADER_SPLIT__nanjing__RC_CASCADER_SPLIT__zhonghuamen"
|
||||
role="menuitemcheckbox"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
Jiangsu / Nanjing /
|
||||
<span
|
||||
class="ant-cascader-menu-item-keyword"
|
||||
>
|
||||
Z
|
||||
</span>
|
||||
hong Hua Men
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
exports[`Cascader should highlight keyword and filter when search in Cascader 1`] = `"<div><div class=\\"ant-cascader-menus\\"><ul class=\\"ant-cascader-menu\\" role=\\"menu\\"><li class=\\"ant-cascader-menu-item\\" role=\\"menuitemcheckbox\\" aria-checked=\\"false\\" data-path-key=\\"zhejiang__RC_CASCADER_SPLIT__hangzhou__RC_CASCADER_SPLIT__xihu\\"><div class=\\"ant-cascader-menu-item-content\\"><span class=\\"ant-cascader-menu-item-keyword\\">Z</span>hejiang / Hang<span class=\\"ant-cascader-menu-item-keyword\\">z</span>hou / West Lake</div></li><li class=\\"ant-cascader-menu-item\\" role=\\"menuitemcheckbox\\" aria-checked=\\"false\\" data-path-key=\\"jiangsu__RC_CASCADER_SPLIT__nanjing__RC_CASCADER_SPLIT__zhonghuamen\\"><div class=\\"ant-cascader-menu-item-content\\">Jiangsu / Nanjing / <span class=\\"ant-cascader-menu-item-keyword\\">Z</span>hong Hua Men</div></li></ul></div></div>"`;
|
||||
|
||||
exports[`Cascader should highlight keyword and filter when search in Cascader with same field name of label and value 1`] = `
|
||||
<div
|
||||
class="ant-select-dropdown ant-cascader-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
|
||||
style="opacity: 0; min-width: 0;"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-cascader-menus"
|
||||
>
|
||||
<ul
|
||||
class="ant-cascader-menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item"
|
||||
data-path-key="Zhejiang__RC_CASCADER_SPLIT__Hangzhou__RC_CASCADER_SPLIT__West Lake"
|
||||
role="menuitemcheckbox"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
<span
|
||||
class="ant-cascader-menu-item-keyword"
|
||||
>
|
||||
Z
|
||||
</span>
|
||||
hejiang / Hang
|
||||
<span
|
||||
class="ant-cascader-menu-item-keyword"
|
||||
>
|
||||
z
|
||||
</span>
|
||||
hou / West Lake
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item ant-cascader-menu-item-disabled"
|
||||
data-path-key="Zhejiang__RC_CASCADER_SPLIT__Hangzhou__RC_CASCADER_SPLIT__Xia Sha"
|
||||
role="menuitemcheckbox"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
<span
|
||||
class="ant-cascader-menu-item-keyword"
|
||||
>
|
||||
Z
|
||||
</span>
|
||||
hejiang / Hang
|
||||
<span
|
||||
class="ant-cascader-menu-item-keyword"
|
||||
>
|
||||
z
|
||||
</span>
|
||||
hou / Xia Sha
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
exports[`Cascader should highlight keyword and filter when search in Cascader with same field name of label and value 1`] = `"<div><div class=\\"ant-cascader-menus\\"><ul class=\\"ant-cascader-menu\\" role=\\"menu\\"><li class=\\"ant-cascader-menu-item\\" role=\\"menuitemcheckbox\\" aria-checked=\\"false\\" data-path-key=\\"Zhejiang__RC_CASCADER_SPLIT__Hangzhou__RC_CASCADER_SPLIT__West Lake\\"><div class=\\"ant-cascader-menu-item-content\\"><span class=\\"ant-cascader-menu-item-keyword\\">Z</span>hejiang / Hang<span class=\\"ant-cascader-menu-item-keyword\\">z</span>hou / West Lake</div></li><li class=\\"ant-cascader-menu-item ant-cascader-menu-item-disabled\\" role=\\"menuitemcheckbox\\" aria-checked=\\"false\\" data-path-key=\\"Zhejiang__RC_CASCADER_SPLIT__Hangzhou__RC_CASCADER_SPLIT__Xia Sha\\"><div class=\\"ant-cascader-menu-item-content\\"><span class=\\"ant-cascader-menu-item-keyword\\">Z</span>hejiang / Hang<span class=\\"ant-cascader-menu-item-keyword\\">z</span>hou / Xia Sha</div></li></ul></div></div>"`;
|
||||
|
||||
exports[`Cascader should render not found content 1`] = `
|
||||
<div
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { mount } from 'enzyme';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import React from 'react';
|
||||
import Cascader from '..';
|
||||
import excludeAllWarning from '../../../tests/shared/excludeWarning';
|
||||
@ -7,26 +5,26 @@ import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
|
||||
const { SHOW_CHILD, SHOW_PARENT } = Cascader;
|
||||
|
||||
function toggleOpen(wrapper) {
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
function toggleOpen(container) {
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
|
||||
}
|
||||
|
||||
function isOpen(wrapper) {
|
||||
return !!wrapper.find('Trigger').props().popupVisible;
|
||||
function isOpen(container) {
|
||||
return container.querySelector('.ant-cascader').className.includes('ant-select-open');
|
||||
}
|
||||
|
||||
function getDropdown(wrapper) {
|
||||
return wrapper.find('.ant-select-dropdown');
|
||||
function getDropdown(container) {
|
||||
return container.querySelector('.ant-select-dropdown');
|
||||
}
|
||||
|
||||
function clickOption(wrapper, menuIndex, itemIndex, type = 'click') {
|
||||
const menu = wrapper.find('ul.ant-cascader-menu').at(menuIndex);
|
||||
const itemList = menu.find('li.ant-cascader-menu-item');
|
||||
|
||||
itemList.at(itemIndex).simulate(type);
|
||||
function clickOption(container, menuIndex, itemIndex, type = 'click') {
|
||||
const menu = container.querySelectorAll('ul.ant-cascader-menu')[menuIndex];
|
||||
const itemList = menu.querySelectorAll('li.ant-cascader-menu-item');
|
||||
fireEvent[type](itemList[itemIndex]);
|
||||
}
|
||||
|
||||
const options = [
|
||||
@ -76,80 +74,90 @@ describe('Cascader', () => {
|
||||
rtlTest(Cascader);
|
||||
|
||||
it('popup correctly when panel is hidden', () => {
|
||||
const wrapper = mount(<Cascader options={options} />);
|
||||
expect(isOpen(wrapper)).toBeFalsy();
|
||||
const { container } = render(<Cascader options={options} />);
|
||||
expect(isOpen(container)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('popup correctly when panel is open', () => {
|
||||
const onPopupVisibleChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Cascader options={options} onPopupVisibleChange={onPopupVisibleChange} />,
|
||||
);
|
||||
toggleOpen(wrapper);
|
||||
expect(isOpen(wrapper)).toBeTruthy();
|
||||
toggleOpen(container);
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
expect(onPopupVisibleChange).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('support controlled mode', () => {
|
||||
const wrapper = mount(<Cascader options={options} />);
|
||||
wrapper.setProps({
|
||||
value: ['zhejiang', 'hangzhou', 'xihu'],
|
||||
});
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { rerender, asFragment } = render(<Cascader options={options} />);
|
||||
rerender(<Cascader options={options} value={['zhejiang', 'hangzhou', 'xihu']} />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('popup correctly with defaultValue', () => {
|
||||
const wrapper = mount(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />);
|
||||
toggleOpen(wrapper);
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
const { container } = render(
|
||||
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />,
|
||||
);
|
||||
toggleOpen(container);
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support popupVisible', () => {
|
||||
const wrapper = mount(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />);
|
||||
expect(isOpen(wrapper)).toBeFalsy();
|
||||
wrapper.setProps({ popupVisible: true });
|
||||
expect(isOpen(wrapper)).toBeTruthy();
|
||||
const { container, rerender } = render(
|
||||
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />,
|
||||
);
|
||||
expect(isOpen(container)).toBeFalsy();
|
||||
rerender(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} popupVisible />);
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can be selected', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(<Cascader options={options} onChange={onChange} />);
|
||||
const { container } = render(<Cascader options={options} onChange={onChange} />);
|
||||
|
||||
toggleOpen(wrapper);
|
||||
expect(isOpen(wrapper)).toBeTruthy();
|
||||
toggleOpen(container);
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
|
||||
clickOption(wrapper, 0, 0);
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
clickOption(container, 0, 0);
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
|
||||
clickOption(wrapper, 1, 0);
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
clickOption(container, 1, 0);
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
|
||||
clickOption(wrapper, 2, 0);
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
clickOption(container, 2, 0);
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
expect(onChange).toHaveBeenCalledWith(['zhejiang', 'hangzhou', 'xihu'], expect.anything());
|
||||
});
|
||||
|
||||
it('backspace should work with `Cascader[showSearch]`', () => {
|
||||
const wrapper = mount(<Cascader options={options} showSearch />);
|
||||
wrapper.find('input').simulate('change', { target: { value: '123' } });
|
||||
expect(isOpen(wrapper)).toBeTruthy();
|
||||
const { container } = render(<Cascader options={options} showSearch />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: '123' } });
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
|
||||
wrapper.find('input').simulate('keydown', { which: KeyCode.BACKSPACE });
|
||||
expect(isOpen(wrapper)).toBeTruthy();
|
||||
fireEvent.keyDown(container.querySelector('input'), {
|
||||
key: 'Backspace',
|
||||
keyCode: 8,
|
||||
});
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
|
||||
wrapper.find('input').simulate('change', { target: { value: '' } });
|
||||
expect(isOpen(wrapper)).toBeTruthy();
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: '' } });
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
|
||||
wrapper.find('input').simulate('keydown', { which: KeyCode.BACKSPACE });
|
||||
expect(isOpen(wrapper)).toBeFalsy();
|
||||
fireEvent.keyDown(container.querySelector('input'), {
|
||||
key: 'Backspace',
|
||||
keyCode: 8,
|
||||
});
|
||||
expect(isOpen(container)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should highlight keyword and filter when search in Cascader', () => {
|
||||
const wrapper = mount(<Cascader options={options} showSearch={{ filter }} />);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'z' } });
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
const { container } = render(<Cascader options={options} showSearch={{ filter }} />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'z' } });
|
||||
|
||||
// React 18 with testing lib will have additional space. We have to compare innerHTML. Sad.
|
||||
expect(getDropdown(container).innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should highlight keyword and filter when search in Cascader with same field name of label and value', () => {
|
||||
@ -175,58 +183,67 @@ describe('Cascader', () => {
|
||||
function customFilter(inputValue, path) {
|
||||
return path.some(option => option.name.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
|
||||
}
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Cascader
|
||||
options={customOptions}
|
||||
fieldNames={{ label: 'name', value: 'name' }}
|
||||
showSearch={{ filter: customFilter }}
|
||||
/>,
|
||||
);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'z' } });
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'z' } });
|
||||
|
||||
// React 18 with testing lib will have additional space. We have to compare innerHTML. Sad.
|
||||
expect(getDropdown(container).innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render not found content', () => {
|
||||
const wrapper = mount(<Cascader options={options} showSearch={{ filter }} />);
|
||||
wrapper.find('input').simulate('change', { target: { value: '__notfoundkeyword__' } });
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
const { container } = render(<Cascader options={options} showSearch={{ filter }} />);
|
||||
fireEvent.change(container.querySelector('input'), {
|
||||
target: { value: '__notfoundkeyword__' },
|
||||
});
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support to clear selection', () => {
|
||||
const wrapper = mount(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />);
|
||||
expect(wrapper.find('.ant-select-selection-item').text()).toEqual('Zhejiang / Hangzhou');
|
||||
wrapper.find('.ant-select-clear').at(0).simulate('mouseDown');
|
||||
expect(wrapper.exists('.ant-select-selection-item')).toBeFalsy();
|
||||
const { container } = render(
|
||||
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />,
|
||||
);
|
||||
expect(container.querySelector('.ant-select-selection-item').textContent).toEqual(
|
||||
'Zhejiang / Hangzhou',
|
||||
);
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-clear'));
|
||||
expect(container.querySelector('.ant-select-selection-item')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should clear search input when clear selection', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} showSearch />,
|
||||
);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'xxx' } });
|
||||
|
||||
wrapper.find('.ant-select-clear').at(0).simulate('mouseDown');
|
||||
expect(wrapper.find('input').props().value).toEqual('');
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'xxx' } });
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-clear'));
|
||||
expect(container.querySelector('input').value).toEqual('');
|
||||
});
|
||||
|
||||
it('should change filtered item when options are changed', () => {
|
||||
const wrapper = mount(<Cascader options={options} showSearch={{ filter }} />);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'a' } });
|
||||
expect(wrapper.find('.ant-cascader-menu-item').length).toBe(2);
|
||||
wrapper.setProps({ options: [options[0]] });
|
||||
expect(wrapper.find('.ant-cascader-menu-item').length).toBe(1);
|
||||
const { container, rerender } = render(<Cascader options={options} showSearch={{ filter }} />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item').length).toBe(2);
|
||||
|
||||
rerender(<Cascader options={[options[0]]} showSearch={{ filter }} />);
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should select item immediately when searching and pressing down arrow key', () => {
|
||||
const wrapper = mount(<Cascader options={options} showSearch={{ filter }} />);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'a' } });
|
||||
expect(wrapper.find('.ant-cascader-menu-item').length).toBe(2);
|
||||
expect(wrapper.find('.ant-cascader-menu-item-active').length).toBe(0);
|
||||
const { container } = render(<Cascader options={options} showSearch={{ filter }} />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
|
||||
|
||||
wrapper.find('input').simulate('keyDown', {
|
||||
which: KeyCode.DOWN,
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item').length).toBe(2);
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item-active').length).toBe(0);
|
||||
fireEvent.keyDown(container.querySelector('input'), {
|
||||
key: 'Down',
|
||||
keyCode: 40,
|
||||
});
|
||||
expect(wrapper.find('.ant-cascader-menu-item-active').length).toBe(1);
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item-active').length).toBe(1);
|
||||
});
|
||||
|
||||
it('can use fieldNames', () => {
|
||||
@ -267,7 +284,7 @@ describe('Cascader', () => {
|
||||
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Cascader
|
||||
options={customerOptions}
|
||||
onChange={onChange}
|
||||
@ -280,10 +297,10 @@ describe('Cascader', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
clickOption(wrapper, 0, 0);
|
||||
clickOption(wrapper, 1, 0);
|
||||
clickOption(wrapper, 2, 0);
|
||||
expect(wrapper.find('.ant-select-selection-item').text()).toEqual(
|
||||
clickOption(container, 0, 0);
|
||||
clickOption(container, 1, 0);
|
||||
clickOption(container, 2, 0);
|
||||
expect(container.querySelector('.ant-select-selection-item').textContent).toEqual(
|
||||
'Zhejiang / Hangzhou / West Lake',
|
||||
);
|
||||
expect(onChange).toHaveBeenCalledWith(['zhejiang', 'hangzhou', 'xihu'], expect.anything());
|
||||
@ -291,14 +308,14 @@ describe('Cascader', () => {
|
||||
|
||||
it('should show not found content when options.length is 0', () => {
|
||||
const customerOptions = [];
|
||||
const wrapper = mount(<Cascader options={customerOptions} />);
|
||||
toggleOpen(wrapper);
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
const { container } = render(<Cascader options={customerOptions} />);
|
||||
toggleOpen(container);
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('not found content should be disabled', () => {
|
||||
const wrapper = mount(<Cascader options={[]} open />);
|
||||
expect(wrapper.find('.ant-cascader-menu-item-disabled').length).toBe(1);
|
||||
const { container } = render(<Cascader options={[]} open />);
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item-disabled').length).toBe(1);
|
||||
});
|
||||
|
||||
describe('limit filtered item count', () => {
|
||||
@ -309,22 +326,28 @@ describe('Cascader', () => {
|
||||
});
|
||||
|
||||
it('limit with positive number', () => {
|
||||
const wrapper = mount(<Cascader options={options} showSearch={{ filter, limit: 1 }} />);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'a' } });
|
||||
expect(wrapper.find('.ant-cascader-menu-item')).toHaveLength(1);
|
||||
const { container } = render(
|
||||
<Cascader options={options} showSearch={{ filter, limit: 1 }} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('not limit', () => {
|
||||
const wrapper = mount(<Cascader options={options} showSearch={{ filter, limit: false }} />);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'a' } });
|
||||
expect(wrapper.find('.ant-cascader-menu-item')).toHaveLength(2);
|
||||
const { container } = render(
|
||||
<Cascader options={options} showSearch={{ filter, limit: false }} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('negative limit', () => {
|
||||
const wrapper = mount(<Cascader options={options} showSearch={{ filter, limit: -1 }} />);
|
||||
wrapper.find('input').simulate('click');
|
||||
wrapper.find('input').simulate('change', { target: { value: 'a' } });
|
||||
expect(wrapper.find('.ant-cascader-menu-item')).toHaveLength(2);
|
||||
const { container } = render(
|
||||
<Cascader options={options} showSearch={{ filter, limit: -1 }} />,
|
||||
);
|
||||
fireEvent.click(container.querySelector('input'));
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
@ -332,7 +355,7 @@ describe('Cascader', () => {
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('should warning if not find `value` in `options`', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
mount(<Cascader options={[{ label: 'a', value: 'a', children: [{ label: 'b' }] }]} />);
|
||||
render(<Cascader options={[{ label: 'a', value: 'a', children: [{ label: 'b' }] }]} />);
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Cascader] Not found `value` in `options`.',
|
||||
);
|
||||
@ -355,22 +378,22 @@ describe('Cascader', () => {
|
||||
},
|
||||
];
|
||||
expect(() => {
|
||||
mount(<Cascader options={optionsWithChildrenNull} />);
|
||||
render(<Cascader options={optionsWithChildrenNull} />);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('placeholder works correctly', () => {
|
||||
const wrapper = mount(<Cascader options={[]} />);
|
||||
expect(wrapper.find('.ant-select-selection-placeholder').text()).toEqual('');
|
||||
const { container, rerender } = render(<Cascader options={[]} />);
|
||||
expect(container.querySelector('.ant-select-selection-placeholder').textContent).toEqual('');
|
||||
|
||||
const customPlaceholder = 'Custom placeholder';
|
||||
wrapper.setProps({
|
||||
placeholder: customPlaceholder,
|
||||
});
|
||||
expect(wrapper.find('.ant-select-selection-placeholder').text()).toEqual(customPlaceholder);
|
||||
rerender(<Cascader options={[]} placeholder={customPlaceholder} />);
|
||||
expect(container.querySelector('.ant-select-selection-placeholder').textContent).toEqual(
|
||||
customPlaceholder,
|
||||
);
|
||||
});
|
||||
|
||||
it('placement work correctly', () => {
|
||||
it('placement work correctly', async () => {
|
||||
const customerOptions = [
|
||||
{
|
||||
value: 'zhejiang',
|
||||
@ -383,17 +406,20 @@ describe('Cascader', () => {
|
||||
],
|
||||
},
|
||||
];
|
||||
const wrapper = mount(<Cascader options={customerOptions} placement="topRight" />);
|
||||
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('topRight');
|
||||
const { container } = render(<Cascader options={customerOptions} placement="topRight" />);
|
||||
toggleOpen(container);
|
||||
|
||||
// Inject in tests/__mocks__/rc-trigger.js
|
||||
expect(global.triggerProps.popupPlacement).toEqual('topRight');
|
||||
});
|
||||
|
||||
it('popup correctly with defaultValue RTL', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<ConfigProvider direction="rtl">
|
||||
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} open />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('can be selected in RTL direction', () => {
|
||||
@ -432,7 +458,7 @@ describe('Cascader', () => {
|
||||
},
|
||||
];
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<ConfigProvider direction="rtl">
|
||||
<Cascader
|
||||
options={options2}
|
||||
@ -443,72 +469,84 @@ describe('Cascader', () => {
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
toggleOpen(wrapper);
|
||||
clickOption(wrapper, 0, 0);
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
toggleOpen(container);
|
||||
clickOption(container, 0, 0);
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
|
||||
toggleOpen(wrapper);
|
||||
clickOption(wrapper, 1, 0);
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
toggleOpen(container);
|
||||
clickOption(container, 1, 0);
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
|
||||
toggleOpen(wrapper);
|
||||
clickOption(wrapper, 2, 0);
|
||||
expect(getDropdown(wrapper).render()).toMatchSnapshot();
|
||||
toggleOpen(container);
|
||||
clickOption(container, 2, 0);
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
expect(onChange).toHaveBeenCalledWith(['zhejiang', 'hangzhou', 'xihu'], expect.anything());
|
||||
});
|
||||
|
||||
it('defaultValue works correctly when no match options', () => {
|
||||
const wrapper = mount(<Cascader options={options} defaultValue={['options1', 'options2']} />);
|
||||
expect(wrapper.find('.ant-select-selection-item').text()).toEqual('options1 / options2');
|
||||
const { container } = render(
|
||||
<Cascader options={options} defaultValue={['options1', 'options2']} />,
|
||||
);
|
||||
expect(container.querySelector('.ant-select-selection-item').textContent).toEqual(
|
||||
'options1 / options2',
|
||||
);
|
||||
});
|
||||
|
||||
it('can be selected when showSearch', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(<Cascader options={options} onChange={onChange} showSearch />);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'Zh' } });
|
||||
expect(wrapper.find('.ant-cascader-menu').length).toBe(1);
|
||||
clickOption(wrapper, 0, 0);
|
||||
const { container } = render(<Cascader options={options} onChange={onChange} showSearch />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'Zh' } });
|
||||
|
||||
expect(container.querySelectorAll('.ant-cascader-menu').length).toBe(1);
|
||||
clickOption(container, 0, 0);
|
||||
expect(onChange).toHaveBeenCalledWith(['zhejiang', 'hangzhou', 'xihu'], expect.anything());
|
||||
});
|
||||
|
||||
it('options should open after press esc and then search', () => {
|
||||
const wrapper = mount(<Cascader options={options} showSearch />);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'jin' } });
|
||||
expect(isOpen(wrapper)).toBeTruthy();
|
||||
wrapper.find('input').simulate('keydown', { which: KeyCode.ESC });
|
||||
expect(isOpen(wrapper)).toBeFalsy();
|
||||
wrapper.find('input').simulate('change', { target: { value: 'jin' } });
|
||||
expect(isOpen(wrapper)).toBeTruthy();
|
||||
const { container } = render(<Cascader options={options} showSearch />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'jin' } });
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
fireEvent.keyDown(container.querySelector('input'), {
|
||||
key: 'Esc',
|
||||
keyCode: 27,
|
||||
});
|
||||
expect(isOpen(container)).toBeFalsy();
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'jin' } });
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('onChange works correctly when the label of fieldNames is the same as value', () => {
|
||||
const onChange = jest.fn();
|
||||
const sameNames = { label: 'label', value: 'label' };
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Cascader options={options} onChange={onChange} showSearch fieldNames={sameNames} />,
|
||||
);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'est' } });
|
||||
clickOption(wrapper, 0, 0);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'est' } });
|
||||
clickOption(container, 0, 0);
|
||||
expect(onChange).toHaveBeenCalledWith(['Zhejiang', 'Hangzhou', 'West Lake'], expect.anything());
|
||||
});
|
||||
|
||||
it('rtl should work well with placement', () => {
|
||||
const wrapper = mount(<Cascader options={options} direction="rtl" />);
|
||||
const { container } = render(<Cascader options={options} direction="rtl" />);
|
||||
toggleOpen(container);
|
||||
|
||||
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('bottomRight');
|
||||
// Inject in tests/__mocks__/rc-trigger.js
|
||||
expect(global.triggerProps.popupPlacement).toEqual('bottomRight');
|
||||
});
|
||||
|
||||
describe('legacy props', () => {
|
||||
it('popupClassName', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Cascader open popupPlacement="bottomLeft" popupClassName="mock-cls" />,
|
||||
);
|
||||
|
||||
expect(wrapper.exists('.mock-cls')).toBeTruthy();
|
||||
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('bottomLeft');
|
||||
expect(container.querySelector('.mock-cls')).toBeTruthy();
|
||||
|
||||
// Inject in tests/__mocks__/rc-trigger.js
|
||||
expect(global.triggerProps.popupPlacement).toEqual('bottomLeft');
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Cascader] `popupClassName` is deprecated. Please use `dropdownClassName` instead.',
|
||||
@ -562,7 +600,7 @@ describe('Cascader', () => {
|
||||
selectedValue = value;
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
const { container, asFragment } = render(
|
||||
<Cascader
|
||||
options={multipleOptions}
|
||||
onChange={onChange}
|
||||
@ -570,13 +608,13 @@ describe('Cascader', () => {
|
||||
showCheckedStrategy={SHOW_CHILD}
|
||||
/>,
|
||||
);
|
||||
toggleOpen(wrapper);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
toggleOpen(container);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
|
||||
clickOption(wrapper, 0, 0);
|
||||
clickOption(wrapper, 1, 0);
|
||||
clickOption(wrapper, 2, 0);
|
||||
clickOption(wrapper, 2, 1);
|
||||
clickOption(container, 0, 0);
|
||||
clickOption(container, 1, 0);
|
||||
clickOption(container, 2, 0);
|
||||
clickOption(container, 2, 1);
|
||||
expect(selectedValue[0].join(',')).toBe('zhejiang,hangzhou,xihu');
|
||||
expect(selectedValue[1].join(',')).toBe('zhejiang,hangzhou,donghu');
|
||||
expect(selectedValue.join(',')).toBe('zhejiang,hangzhou,xihu,zhejiang,hangzhou,donghu');
|
||||
@ -627,7 +665,7 @@ describe('Cascader', () => {
|
||||
selectedValue = value;
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
const { container, asFragment } = render(
|
||||
<Cascader
|
||||
options={multipleOptions}
|
||||
onChange={onChange}
|
||||
@ -635,12 +673,12 @@ describe('Cascader', () => {
|
||||
showCheckedStrategy={SHOW_PARENT}
|
||||
/>,
|
||||
);
|
||||
toggleOpen(wrapper);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
clickOption(wrapper, 0, 0);
|
||||
clickOption(wrapper, 1, 0);
|
||||
clickOption(wrapper, 2, 0);
|
||||
clickOption(wrapper, 2, 1);
|
||||
toggleOpen(container);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
clickOption(container, 0, 0);
|
||||
clickOption(container, 1, 0);
|
||||
clickOption(container, 2, 0);
|
||||
clickOption(container, 2, 1);
|
||||
|
||||
expect(selectedValue.length).toBe(1);
|
||||
expect(selectedValue.join(',')).toBe('zhejiang');
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { mount } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
import type { BaseOptionType } from '..';
|
||||
import Cascader from '..';
|
||||
import { render } from '../../../tests/utils';
|
||||
|
||||
describe('Cascader.typescript', () => {
|
||||
it('options value', () => {
|
||||
@ -46,8 +46,10 @@ describe('Cascader.typescript', () => {
|
||||
});
|
||||
|
||||
it('suffixIcon', () => {
|
||||
const wrapper = mount(<Cascader suffixIcon={<span />} />);
|
||||
expect(wrapper).toBeTruthy();
|
||||
const { container } = render(<Cascader suffixIcon={<span />} />);
|
||||
expect(
|
||||
container.querySelector('.ant-select-arrow')?.querySelector('span')?.className,
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('Generic', () => {
|
||||
@ -57,7 +59,7 @@ describe('Cascader.typescript', () => {
|
||||
customizeChildren?: MyOptionData[];
|
||||
}
|
||||
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Cascader<MyOptionData>
|
||||
options={[
|
||||
{
|
||||
@ -73,20 +75,20 @@ describe('Cascader.typescript', () => {
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper).toBeTruthy();
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
|
||||
it('single onChange', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Cascader multiple={false} onChange={(values: (string | number)[]) => values} />,
|
||||
);
|
||||
expect(wrapper).toBeTruthy();
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
|
||||
it('multiple onChange', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Cascader multiple onChange={(values: (string | number)[][]) => values} />,
|
||||
);
|
||||
expect(wrapper).toBeTruthy();
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { render, sleep } from '../../../tests/utils';
|
||||
import { sleep, render, fireEvent } from '../../../tests/utils';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
|
||||
describe('Collapse', () => {
|
||||
@ -10,6 +9,15 @@ describe('Collapse', () => {
|
||||
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
// fix React concurrent
|
||||
function triggerAllTimer() {
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
resetWarned();
|
||||
});
|
||||
@ -23,16 +31,16 @@ describe('Collapse', () => {
|
||||
});
|
||||
|
||||
it('should support remove expandIcon', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Collapse expandIcon={() => null}>
|
||||
<Collapse.Panel header="header" />
|
||||
</Collapse>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should keep the className of the expandIcon', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Collapse
|
||||
expandIcon={() => (
|
||||
<button type="button" className="custom-expandicon-classname">
|
||||
@ -44,49 +52,51 @@ describe('Collapse', () => {
|
||||
</Collapse>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.custom-expandicon-classname').exists()).toBe(true);
|
||||
expect(container.querySelectorAll('.custom-expandicon-classname').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should render extra node of panel', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Collapse>
|
||||
<Collapse.Panel header="header" extra={<button type="button">action</button>} />
|
||||
<Collapse.Panel header="header" extra={<button type="button">action</button>} />
|
||||
</Collapse>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('could be expand and collapse', async () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Collapse>
|
||||
<Collapse.Panel header="This is panel header 1" key="1">
|
||||
content
|
||||
</Collapse.Panel>
|
||||
</Collapse>,
|
||||
);
|
||||
expect(wrapper.find('.ant-collapse-item').hasClass('ant-collapse-item-active')).toBe(false);
|
||||
wrapper.find('.ant-collapse-header').at(0).simulate('click');
|
||||
wrapper.update();
|
||||
expect(
|
||||
container.querySelector('.ant-collapse-item')?.classList.contains('ant-collapse-item-active'),
|
||||
).toBe(false);
|
||||
fireEvent.click(container.querySelector('.ant-collapse-header')!);
|
||||
await sleep(400);
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-collapse-item').hasClass('ant-collapse-item-active')).toBe(true);
|
||||
expect(
|
||||
container.querySelector('.ant-collapse-item')?.classList.contains('ant-collapse-item-active'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('could override default openMotion', () => {
|
||||
const wrapper = mount(
|
||||
const { container, asFragment } = render(
|
||||
<Collapse openMotion={{}}>
|
||||
<Collapse.Panel header="This is panel header 1" key="1">
|
||||
content
|
||||
</Collapse.Panel>
|
||||
</Collapse>,
|
||||
);
|
||||
wrapper.find('.ant-collapse-header').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
fireEvent.click(container.querySelector('.ant-collapse-header')!);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should trigger warning and keep compatibility when using disabled in Panel', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Collapse>
|
||||
<Collapse.Panel disabled header="This is panel header 1" key="1">
|
||||
content
|
||||
@ -98,19 +108,19 @@ describe('Collapse', () => {
|
||||
'Warning: [antd: Collapse.Panel] `disabled` is deprecated. Please use `collapsible="disabled"` instead.',
|
||||
);
|
||||
|
||||
expect(wrapper.find('.ant-collapse-item-disabled').length).toBe(1);
|
||||
expect(container.querySelectorAll('.ant-collapse-item-disabled').length).toBe(1);
|
||||
|
||||
wrapper.find('.ant-collapse-header').simulate('click');
|
||||
expect(wrapper.find('.ant-collapse-item-active').length).toBe(0);
|
||||
fireEvent.click(container.querySelector('.ant-collapse-header')!);
|
||||
expect(container.querySelectorAll('.ant-collapse-item-active').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should end motion when set activeKey while hiding', async () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => {
|
||||
setTimeout(cb, 16.66);
|
||||
});
|
||||
const spiedRAF = jest
|
||||
.spyOn(window, 'requestAnimationFrame')
|
||||
.mockImplementation(cb => setTimeout(cb, 16.66));
|
||||
|
||||
let setActiveKeyOuter;
|
||||
let setActiveKeyOuter: React.Dispatch<React.SetStateAction<React.Key | undefined>>;
|
||||
const Test = () => {
|
||||
const [activeKey, setActiveKey] = React.useState();
|
||||
setActiveKeyOuter = setActiveKey;
|
||||
@ -125,17 +135,18 @@ describe('Collapse', () => {
|
||||
);
|
||||
};
|
||||
|
||||
const wrapper = mount(<Test />);
|
||||
const { container } = render(<Test />);
|
||||
|
||||
await act(async () => {
|
||||
setActiveKeyOuter('1');
|
||||
await Promise.resolve();
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(wrapper.render().find('.ant-motion-collapse').length).toBe(0);
|
||||
triggerAllTimer();
|
||||
|
||||
window.requestAnimationFrame.mockRestore();
|
||||
expect(container.querySelectorAll('.ant-motion-collapse').length).toBe(0);
|
||||
|
||||
spiedRAF.mockRestore();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
@ -69,6 +69,10 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = token => {
|
||||
cursor: 'pointer',
|
||||
transition: `all ${motionDurationSlow}, visibility 0s`,
|
||||
|
||||
[`> ${componentCls}-header-text`]: {
|
||||
flex: 'auto',
|
||||
},
|
||||
|
||||
'&:focus': {
|
||||
outline: 'none',
|
||||
},
|
||||
@ -100,6 +104,7 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = token => {
|
||||
cursor: 'default',
|
||||
|
||||
[`${componentCls}-header-text`]: {
|
||||
flex: 'none',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
},
|
||||
|
@ -14130,449 +14130,519 @@ exports[`ConfigProvider components Divider prefixCls 1`] = `
|
||||
|
||||
exports[`ConfigProvider components Drawer configProvider 1`] = `
|
||||
<div
|
||||
class=""
|
||||
class="config-drawer config-drawer-right config-drawer-open config-drawer-inline"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="config-drawer config-drawer-right"
|
||||
tabindex="-1"
|
||||
class="config-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="start"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="config-drawer-content-wrapper"
|
||||
style="width:378px"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="config-drawer-content-wrapper"
|
||||
style="transform:translateX(100%);-ms-transform:translateX(100%);width:378px"
|
||||
aria-modal="true"
|
||||
class="config-drawer-content"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-content"
|
||||
class="config-drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-wrapper-body"
|
||||
class="config-drawer-header config-drawer-header-close-only"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-header config-drawer-header-close-only"
|
||||
class="config-drawer-header-title"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-header-title"
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="config-drawer-close"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="config-drawer-close"
|
||||
type="button"
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
<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="config-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="end"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Drawer configProvider componentDisabled 1`] = `
|
||||
<div
|
||||
class=""
|
||||
class="config-drawer config-drawer-right config-drawer-open config-drawer-inline"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="config-drawer config-drawer-right"
|
||||
tabindex="-1"
|
||||
class="config-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="start"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="config-drawer-content-wrapper"
|
||||
style="width:378px"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="config-drawer-content-wrapper"
|
||||
style="transform:translateX(100%);-ms-transform:translateX(100%);width:378px"
|
||||
aria-modal="true"
|
||||
class="config-drawer-content"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-content"
|
||||
class="config-drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-wrapper-body"
|
||||
class="config-drawer-header config-drawer-header-close-only"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-header config-drawer-header-close-only"
|
||||
class="config-drawer-header-title"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-header-title"
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="config-drawer-close"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="config-drawer-close"
|
||||
type="button"
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
<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="config-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="end"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Drawer configProvider componentSize large 1`] = `
|
||||
<div
|
||||
class=""
|
||||
class="config-drawer config-drawer-right config-drawer-open config-drawer-inline"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="config-drawer config-drawer-right"
|
||||
tabindex="-1"
|
||||
class="config-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="start"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="config-drawer-content-wrapper"
|
||||
style="width:378px"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="config-drawer-content-wrapper"
|
||||
style="transform:translateX(100%);-ms-transform:translateX(100%);width:378px"
|
||||
aria-modal="true"
|
||||
class="config-drawer-content"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-content"
|
||||
class="config-drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-wrapper-body"
|
||||
class="config-drawer-header config-drawer-header-close-only"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-header config-drawer-header-close-only"
|
||||
class="config-drawer-header-title"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-header-title"
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="config-drawer-close"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="config-drawer-close"
|
||||
type="button"
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
<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="config-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="end"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Drawer configProvider componentSize middle 1`] = `
|
||||
<div
|
||||
class=""
|
||||
class="config-drawer config-drawer-right config-drawer-open config-drawer-inline"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="config-drawer config-drawer-right"
|
||||
tabindex="-1"
|
||||
class="config-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="start"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="config-drawer-content-wrapper"
|
||||
style="width:378px"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="config-drawer-content-wrapper"
|
||||
style="transform:translateX(100%);-ms-transform:translateX(100%);width:378px"
|
||||
aria-modal="true"
|
||||
class="config-drawer-content"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-content"
|
||||
class="config-drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-wrapper-body"
|
||||
class="config-drawer-header config-drawer-header-close-only"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-header config-drawer-header-close-only"
|
||||
class="config-drawer-header-title"
|
||||
>
|
||||
<div
|
||||
class="config-drawer-header-title"
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="config-drawer-close"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="config-drawer-close"
|
||||
type="button"
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
<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="config-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="end"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Drawer configProvider virtual and dropdownMatchSelectWidth 1`] = `
|
||||
<div
|
||||
class=""
|
||||
class="ant-drawer ant-drawer-right ant-drawer-open ant-drawer-inline"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer ant-drawer-right"
|
||||
tabindex="-1"
|
||||
class="ant-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="start"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-drawer-content-wrapper"
|
||||
style="width:378px"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="ant-drawer-content-wrapper"
|
||||
style="transform:translateX(100%);-ms-transform:translateX(100%);width:378px"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-content"
|
||||
class="ant-drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-wrapper-body"
|
||||
class="ant-drawer-header ant-drawer-header-close-only"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header ant-drawer-header-close-only"
|
||||
class="ant-drawer-header-title"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header-title"
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="ant-drawer-close"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="ant-drawer-close"
|
||||
type="button"
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
<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-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="end"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Drawer normal 1`] = `
|
||||
<div
|
||||
class=""
|
||||
class="ant-drawer ant-drawer-right ant-drawer-open ant-drawer-inline"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer ant-drawer-right"
|
||||
tabindex="-1"
|
||||
class="ant-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="start"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-drawer-content-wrapper"
|
||||
style="width:378px"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="ant-drawer-content-wrapper"
|
||||
style="transform:translateX(100%);-ms-transform:translateX(100%);width:378px"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-content"
|
||||
class="ant-drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-wrapper-body"
|
||||
class="ant-drawer-header ant-drawer-header-close-only"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header ant-drawer-header-close-only"
|
||||
class="ant-drawer-header-title"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header-title"
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="ant-drawer-close"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="ant-drawer-close"
|
||||
type="button"
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
<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-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-drawer-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="end"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Drawer prefixCls 1`] = `
|
||||
<div
|
||||
class=""
|
||||
class="prefix-Drawer prefix-Drawer-right prefix-Drawer-open prefix-Drawer-inline"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="prefix-Drawer prefix-Drawer-right"
|
||||
tabindex="-1"
|
||||
class="prefix-Drawer-mask"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="start"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="prefix-Drawer-content-wrapper"
|
||||
style="width:378px"
|
||||
>
|
||||
<div
|
||||
class="prefix-Drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="prefix-Drawer-content-wrapper"
|
||||
style="transform:translateX(100%);-ms-transform:translateX(100%);width:378px"
|
||||
aria-modal="true"
|
||||
class="prefix-Drawer-content"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="prefix-Drawer-content"
|
||||
class="prefix-Drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="prefix-Drawer-wrapper-body"
|
||||
class="prefix-Drawer-header prefix-Drawer-header-close-only"
|
||||
>
|
||||
<div
|
||||
class="prefix-Drawer-header prefix-Drawer-header-close-only"
|
||||
class="prefix-Drawer-header-title"
|
||||
>
|
||||
<div
|
||||
class="prefix-Drawer-header-title"
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="prefix-Drawer-close"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="prefix-Drawer-close"
|
||||
type="button"
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
<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="prefix-Drawer-body"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="prefix-Drawer-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="end"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Drawer from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { render } from '../../../tests/utils';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
|
||||
const DrawerTest = ({ getContainer }) => (
|
||||
@ -17,19 +18,53 @@ describe('Drawer', () => {
|
||||
mountTest(Drawer);
|
||||
rtlTest(Drawer);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
function triggerMotion() {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
const mask = document.querySelector('.ant-drawer-mask');
|
||||
if (mask) {
|
||||
fireEvent.animationEnd(mask);
|
||||
}
|
||||
|
||||
const panel = document.querySelector('.ant-drawer-content');
|
||||
if (panel) {
|
||||
fireEvent.animationEnd(panel);
|
||||
}
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
}
|
||||
|
||||
it('render correctly', () => {
|
||||
const { container: wrapper } = render(
|
||||
<Drawer visible width={400} getContainer={false}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('getContainer return undefined', () => {
|
||||
const { container: wrapper, rerender } = render(<DrawerTest getContainer={() => undefined} />);
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
|
||||
rerender(<DrawerTest getContainer={false} />);
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -39,6 +74,8 @@ describe('Drawer', () => {
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -48,6 +85,8 @@ describe('Drawer', () => {
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -57,6 +96,8 @@ describe('Drawer', () => {
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -66,15 +107,19 @@ describe('Drawer', () => {
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('className is test_drawer', () => {
|
||||
const { container: wrapper } = render(
|
||||
<Drawer destroyOnClose visible={false} className="test_drawer" getContainer={false}>
|
||||
<Drawer destroyOnClose visible className="test_drawer" getContainer={false}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -94,6 +139,8 @@ describe('Drawer', () => {
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -103,6 +150,8 @@ describe('Drawer', () => {
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -131,6 +180,8 @@ describe('Drawer', () => {
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -147,15 +198,4 @@ describe('Drawer', () => {
|
||||
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should support ref', () => {
|
||||
const ref = React.createRef();
|
||||
render(
|
||||
<Drawer visible ref={ref} width={400}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(typeof ref.current.push).toBe('function');
|
||||
expect(typeof ref.current.pull).toBe('function');
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import Drawer from '..';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import { act, fireEvent, render } from '../../../tests/utils';
|
||||
|
||||
describe('Drawer', () => {
|
||||
const getDrawer = props => (
|
||||
@ -9,6 +9,14 @@ describe('Drawer', () => {
|
||||
</Drawer>
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('render correctly', () => {
|
||||
const { container, asFragment, rerender } = render(getDrawer());
|
||||
expect(container.querySelector('.ant-drawer-body')).toBeTruthy();
|
||||
@ -48,54 +56,68 @@ describe('Drawer', () => {
|
||||
|
||||
it('dom should be removed after close when destroyOnClose is true', () => {
|
||||
const { container, rerender } = render(getDrawer({ destroyOnClose: true }));
|
||||
expect(container.querySelector('.ant-drawer')).toBeTruthy();
|
||||
|
||||
rerender(getDrawer({ destroyOnClose: true, visible: false }));
|
||||
const ev = new TransitionEvent('transitionend', { bubbles: true });
|
||||
ev.propertyName = 'transform';
|
||||
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(container.querySelector('.ant-drawer-wrapper-body')).toBeFalsy();
|
||||
expect(container.querySelector('.ant-drawer')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('dom should be existed after close when destroyOnClose is false', () => {
|
||||
const { container, rerender } = render(getDrawer());
|
||||
expect(container.querySelector('.ant-drawer-wrapper-body')).toBeTruthy();
|
||||
expect(container.querySelector('.ant-drawer')).toBeTruthy();
|
||||
|
||||
rerender(getDrawer({ visible: false }));
|
||||
const ev = new TransitionEvent('transitionend', { bubbles: true });
|
||||
ev.propertyName = 'transform';
|
||||
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content'));
|
||||
|
||||
expect(container.querySelector('.ant-drawer-wrapper-body')).toBeTruthy();
|
||||
expect(container.querySelector('.ant-drawer')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('dom should be existed after close twice when getContainer is false', () => {
|
||||
const { container, rerender } = render(getDrawer({ visible: true, getContainer: false }));
|
||||
rerender(getDrawer({ visible: false, getContainer: false }));
|
||||
const ev = new TransitionEvent('transitionend', { bubbles: true });
|
||||
ev.propertyName = 'transform';
|
||||
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev);
|
||||
expect(container.querySelector('.ant-drawer-content')).toBeTruthy();
|
||||
|
||||
// Hide
|
||||
rerender(getDrawer({ visible: false, getContainer: false }));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper'));
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper-hidden')).toBeTruthy();
|
||||
|
||||
// Show
|
||||
rerender(getDrawer({ visible: true, getContainer: false }));
|
||||
const ev2 = new TransitionEvent('transitionend', { bubbles: true });
|
||||
ev2.propertyName = 'transform';
|
||||
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev2);
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper')).toBeTruthy();
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper-hidden')).toBeFalsy();
|
||||
|
||||
// Hide
|
||||
rerender(getDrawer({ visible: false, getContainer: false }));
|
||||
const ev3 = new TransitionEvent('transitionend', { bubbles: true });
|
||||
ev3.propertyName = 'transform';
|
||||
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev3);
|
||||
|
||||
expect(container.querySelector('.ant-drawer-wrapper-body')).toBeTruthy();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper'));
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper-hidden')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('test afterVisibleChange', async () => {
|
||||
const afterVisibleChange = jest.fn();
|
||||
const { rerender } = render(getDrawer({ afterVisibleChange, visible: true }));
|
||||
const { container, rerender } = render(getDrawer({ afterVisibleChange, visible: true }));
|
||||
rerender(getDrawer({ afterVisibleChange, visible: false }));
|
||||
const ev = new TransitionEvent('transitionend', { bubbles: true });
|
||||
ev.propertyName = 'transform';
|
||||
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev);
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper'));
|
||||
|
||||
expect(afterVisibleChange).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should support children ref', () => {
|
||||
const fn = jest.fn();
|
||||
|
||||
|
@ -113,8 +113,10 @@ describe('Drawer', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer placement="right" />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
|
||||
expect(translateX).toEqual('translateX(-180px)');
|
||||
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(-180px)',
|
||||
});
|
||||
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
});
|
||||
|
||||
@ -122,8 +124,10 @@ describe('Drawer', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer placement="left" />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
|
||||
expect(translateX).toEqual('translateX(180px)');
|
||||
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(180px)',
|
||||
});
|
||||
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
fireEvent.click(wrapper.querySelector('.Two-level .ant-drawer-close'));
|
||||
expect(wrapper.querySelector('.childrenDrawer').innerHTML).toEqual('false');
|
||||
@ -133,8 +137,9 @@ describe('Drawer', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer placement="top" />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
|
||||
expect(translateX).toEqual('translateY(180px)');
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateY(180px)',
|
||||
});
|
||||
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
});
|
||||
|
||||
@ -143,11 +148,18 @@ describe('Drawer', () => {
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#remove_drawer'));
|
||||
let translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
|
||||
expect(translateX).toEqual('');
|
||||
|
||||
// Strange, testing-lib get wrong style in next branch.
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper').style).toEqual(
|
||||
expect.objectContaining({
|
||||
transform: '',
|
||||
}),
|
||||
);
|
||||
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
|
||||
expect(translateX).toEqual('translateY(180px)');
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateY(180px)',
|
||||
});
|
||||
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
});
|
||||
|
||||
@ -155,23 +167,28 @@ describe('Drawer', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer push={{ distance: 256 }} />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
|
||||
expect(translateX).toEqual('translateX(-256px)');
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(-256px)',
|
||||
});
|
||||
});
|
||||
|
||||
it('custom MultiDrawer push with true', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer push />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
|
||||
expect(translateX).toEqual('translateX(-180px)');
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(-180px)',
|
||||
});
|
||||
});
|
||||
|
||||
it('custom MultiDrawer push with false', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer push={false} />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
|
||||
expect(translateX).toEqual('');
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper').style).toEqual(
|
||||
expect.objectContaining({
|
||||
transform: '',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,66 +2,76 @@
|
||||
|
||||
exports[`Drawer render correctly 1`] = `
|
||||
<div
|
||||
class=""
|
||||
class="ant-drawer ant-drawer-right ant-drawer-inline"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer ant-drawer-right"
|
||||
tabindex="-1"
|
||||
class="ant-drawer-mask ant-drawer-mask-motion-leave ant-drawer-mask-motion-leave-start ant-drawer-mask-motion"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="start"
|
||||
style="width: 0px; height: 0px; overflow: hidden; outline: none; position: absolute;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-drawer-content-wrapper ant-drawer-panel-motion-right-leave ant-drawer-panel-motion-right-leave-start ant-drawer-panel-motion-right"
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="ant-drawer-content-wrapper"
|
||||
style="width: 378px; transform: translateX(100%);"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-content"
|
||||
class="ant-drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-wrapper-body"
|
||||
class="ant-drawer-header ant-drawer-header-close-only"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header ant-drawer-header-close-only"
|
||||
class="ant-drawer-header-title"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header-title"
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="ant-drawer-close"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="ant-drawer-close"
|
||||
type="button"
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
<div
|
||||
class="ant-drawer-body"
|
||||
>
|
||||
Here is content of Drawer
|
||||
<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>
|
||||
<div
|
||||
class="ant-drawer-body"
|
||||
>
|
||||
Here is content of Drawer
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="end"
|
||||
style="width: 0px; height: 0px; overflow: hidden; outline: none; position: absolute;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,502 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/drawer/demo/basic-right.md extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/drawer/demo/config-provider.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="site-drawer-render-in-current-wrapper"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/drawer/demo/extra.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="top"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
top
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper ant-radio-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-radio ant-radio-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="right"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
right
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="bottom"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottom
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="left"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
left
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/drawer/demo/form-in-drawer.md extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="plus"
|
||||
class="anticon anticon-plus"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="plus"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<defs />
|
||||
<path
|
||||
d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"
|
||||
/>
|
||||
<path
|
||||
d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span>
|
||||
New account
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/drawer/demo/multi-level-drawer.md extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open drawer
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/drawer/demo/no-mask.md extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/drawer/demo/placement.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="top"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
top
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="right"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
right
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="bottom"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottom
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper ant-radio-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-radio ant-radio-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="left"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
left
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/drawer/demo/render-in-current.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="site-drawer-render-in-current-wrapper"
|
||||
>
|
||||
Render in this
|
||||
<div
|
||||
style="margin-top:16px"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-drawer ant-drawer-right"
|
||||
style="position:absolute"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="ant-drawer-content-wrapper"
|
||||
style="transform:translateX(100%);-ms-transform:translateX(100%);width:378px"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-content"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header-title"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
>
|
||||
Basic Drawer
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-drawer-body"
|
||||
>
|
||||
<p>
|
||||
Some contents...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/drawer/demo/size.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open Default Size (378px)
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open Large Size (736px)
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/drawer/demo/user-profile.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-list ant-list-split ant-list-bordered"
|
||||
>
|
||||
<div
|
||||
class="ant-spin-nested-loading"
|
||||
>
|
||||
<div
|
||||
class="ant-spin-container"
|
||||
>
|
||||
<ul
|
||||
class="ant-list-items"
|
||||
>
|
||||
<li
|
||||
class="ant-list-item"
|
||||
>
|
||||
<div
|
||||
class="ant-list-item-meta"
|
||||
>
|
||||
<div
|
||||
class="ant-list-item-meta-avatar"
|
||||
>
|
||||
<span
|
||||
class="ant-avatar ant-avatar-circle ant-avatar-image"
|
||||
>
|
||||
<img
|
||||
src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-list-item-meta-content"
|
||||
>
|
||||
<h4
|
||||
class="ant-list-item-meta-title"
|
||||
>
|
||||
<a
|
||||
href="https://ant.design/index-cn"
|
||||
>
|
||||
Lily
|
||||
</a>
|
||||
</h4>
|
||||
<div
|
||||
class="ant-list-item-meta-description"
|
||||
>
|
||||
Progresser XTech
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
class="ant-list-item-action"
|
||||
>
|
||||
<li>
|
||||
<a>
|
||||
View Profile
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
class="ant-list-item"
|
||||
>
|
||||
<div
|
||||
class="ant-list-item-meta"
|
||||
>
|
||||
<div
|
||||
class="ant-list-item-meta-avatar"
|
||||
>
|
||||
<span
|
||||
class="ant-avatar ant-avatar-circle ant-avatar-image"
|
||||
>
|
||||
<img
|
||||
src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-list-item-meta-content"
|
||||
>
|
||||
<h4
|
||||
class="ant-list-item-meta-title"
|
||||
>
|
||||
<a
|
||||
href="https://ant.design/index-cn"
|
||||
>
|
||||
Lily
|
||||
</a>
|
||||
</h4>
|
||||
<div
|
||||
class="ant-list-item-meta-description"
|
||||
>
|
||||
Progresser XTech
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
class="ant-list-item-action"
|
||||
>
|
||||
<li>
|
||||
<a>
|
||||
View Profile
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
3565
components/drawer/__tests__/__snapshots__/demo-extend.test.tsx.snap
Normal file
3565
components/drawer/__tests__/__snapshots__/demo-extend.test.tsx.snap
Normal file
File diff suppressed because it is too large
Load Diff
@ -310,50 +310,22 @@ exports[`renders ./components/drawer/demo/render-in-current.md correctly 1`] = `
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class=""
|
||||
class="ant-drawer ant-drawer-right ant-drawer-inline"
|
||||
style="position:absolute"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer ant-drawer-right"
|
||||
style="position:absolute"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="ant-drawer-content-wrapper"
|
||||
style="transform:translateX(100%);-ms-transform:translateX(100%);width:378px"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-content"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header-title"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
>
|
||||
Basic Drawer
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-drawer-body"
|
||||
>
|
||||
<p>
|
||||
Some contents...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
aria-hidden="true"
|
||||
data-sentinel="start"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-sentinel="end"
|
||||
style="width:0;height:0;overflow:hidden;outline:none;position:absolute"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,3 +0,0 @@
|
||||
import { extendTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
extendTest('drawer');
|
19
components/drawer/__tests__/demo-extend.test.tsx
Normal file
19
components/drawer/__tests__/demo-extend.test.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import { extendTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
jest.mock('rc-drawer', () => {
|
||||
const Drawer = jest.requireActual('rc-drawer');
|
||||
const MockDrawer = Drawer.default;
|
||||
return (props: any) => {
|
||||
const newProps = {
|
||||
...props,
|
||||
open: true,
|
||||
getContainer: false,
|
||||
};
|
||||
return <MockDrawer {...newProps} />;
|
||||
};
|
||||
});
|
||||
|
||||
extendTest('drawer', {
|
||||
testingLib: true,
|
||||
});
|
@ -40,6 +40,13 @@ const App: React.FC = () => {
|
||||
mask={false}
|
||||
onClose={onClose}
|
||||
visible={visible}
|
||||
contentWrapperStyle={{
|
||||
width: 333,
|
||||
background: 'red',
|
||||
borderRadius: 20,
|
||||
boxShadow: '-5px 0 5px green',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
|
@ -1,20 +1,17 @@
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import classNames from 'classnames';
|
||||
import RcDrawer from 'rc-drawer';
|
||||
import type { DrawerProps as RcDrawerProps } from 'rc-drawer';
|
||||
import type { CSSMotionProps } from 'rc-motion';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { NoFormStyle } from '../form/context';
|
||||
import { getTransitionName } from '../_util/motion';
|
||||
import { tuple } from '../_util/type';
|
||||
|
||||
// CSSINJS
|
||||
import useStyle from './style';
|
||||
|
||||
type DrawerRef = {
|
||||
push(): void;
|
||||
pull(): void;
|
||||
};
|
||||
|
||||
const DrawerContext = React.createContext<DrawerRef | null>(null);
|
||||
|
||||
type EventType =
|
||||
| React.KeyboardEvent<HTMLDivElement>
|
||||
| React.MouseEvent<HTMLDivElement | HTMLButtonElement>;
|
||||
@ -72,212 +69,136 @@ export interface DrawerProps {
|
||||
|
||||
const defaultPushState: PushState = { distance: 180 };
|
||||
|
||||
const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
|
||||
(
|
||||
function Drawer({
|
||||
width,
|
||||
height,
|
||||
size = 'default',
|
||||
closable = true,
|
||||
mask = true,
|
||||
push = defaultPushState,
|
||||
closeIcon = <CloseOutlined />,
|
||||
bodyStyle,
|
||||
drawerStyle,
|
||||
className,
|
||||
visible,
|
||||
children,
|
||||
zIndex,
|
||||
style,
|
||||
title,
|
||||
headerStyle,
|
||||
onClose,
|
||||
footer,
|
||||
footerStyle,
|
||||
prefixCls: customizePrefixCls,
|
||||
getContainer: customizeGetContainer,
|
||||
extra,
|
||||
afterVisibleChange,
|
||||
...rest
|
||||
}: DrawerProps) {
|
||||
const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('drawer', customizePrefixCls);
|
||||
|
||||
// Style
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
const getContainer =
|
||||
// 有可能为 false,所以不能直接判断
|
||||
customizeGetContainer === undefined && getPopupContainer
|
||||
? () => getPopupContainer(document.body)
|
||||
: customizeGetContainer;
|
||||
|
||||
const closeIconNode = closable && (
|
||||
<button type="button" onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
|
||||
{closeIcon}
|
||||
</button>
|
||||
);
|
||||
|
||||
function renderHeader() {
|
||||
if (!title && !closable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(`${prefixCls}-header`, {
|
||||
[`${prefixCls}-header-close-only`]: closable && !title && !extra,
|
||||
})}
|
||||
style={headerStyle}
|
||||
>
|
||||
<div className={`${prefixCls}-header-title`}>
|
||||
{closeIconNode}
|
||||
{title && <div className={`${prefixCls}-title`}>{title}</div>}
|
||||
</div>
|
||||
{extra && <div className={`${prefixCls}-extra`}>{extra}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderFooter() {
|
||||
if (!footer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const footerClassName = `${prefixCls}-footer`;
|
||||
return (
|
||||
<div className={footerClassName} style={footerStyle}>
|
||||
{footer}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const drawerClassName = classNames(
|
||||
{
|
||||
width,
|
||||
height,
|
||||
size = 'default',
|
||||
closable = true,
|
||||
placement = 'right' as placementType,
|
||||
maskClosable = true,
|
||||
mask = true,
|
||||
level = null,
|
||||
keyboard = true,
|
||||
push = defaultPushState,
|
||||
closeIcon = <CloseOutlined />,
|
||||
bodyStyle,
|
||||
drawerStyle,
|
||||
className,
|
||||
visible: propsVisible,
|
||||
forceRender,
|
||||
children,
|
||||
zIndex,
|
||||
destroyOnClose,
|
||||
style,
|
||||
title,
|
||||
headerStyle,
|
||||
onClose,
|
||||
footer,
|
||||
footerStyle,
|
||||
prefixCls: customizePrefixCls,
|
||||
getContainer: customizeGetContainer,
|
||||
extra,
|
||||
afterVisibleChange,
|
||||
...rest
|
||||
'no-mask': !mask,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const [internalPush, setPush] = React.useState(false);
|
||||
const parentDrawer = React.useContext(DrawerContext);
|
||||
const destroyCloseRef = React.useRef<boolean>(false);
|
||||
className,
|
||||
hashId,
|
||||
);
|
||||
|
||||
const [load, setLoad] = React.useState(false);
|
||||
const [visible, setVisible] = React.useState(false);
|
||||
// ============================ Size ============================
|
||||
const mergedWidth = React.useMemo(() => width ?? (size === 'large' ? 736 : 378), [width, size]);
|
||||
const mergedHeight = React.useMemo(
|
||||
() => height ?? (size === 'large' ? 736 : 378),
|
||||
[height, size],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (propsVisible) {
|
||||
setLoad(true);
|
||||
} else {
|
||||
setVisible(false);
|
||||
}
|
||||
}, [propsVisible]);
|
||||
// =========================== Motion ===========================
|
||||
const maskMotion: CSSMotionProps = {
|
||||
motionName: getTransitionName(prefixCls, 'mask-motion'),
|
||||
motionAppear: true,
|
||||
motionEnter: true,
|
||||
motionLeave: true,
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (load && propsVisible) {
|
||||
setVisible(true);
|
||||
}
|
||||
}, [load, propsVisible]);
|
||||
const panelMotion: RcDrawerProps['motion'] = motionPlacement => ({
|
||||
motionName: getTransitionName(prefixCls, `panel-motion-${motionPlacement}`),
|
||||
motionAppear: true,
|
||||
motionEnter: true,
|
||||
motionLeave: true,
|
||||
});
|
||||
|
||||
const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('drawer', customizePrefixCls);
|
||||
|
||||
// Style
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
const getContainer =
|
||||
// 有可能为 false,所以不能直接判断
|
||||
customizeGetContainer === undefined && getPopupContainer
|
||||
? () => getPopupContainer(document.body)
|
||||
: customizeGetContainer;
|
||||
|
||||
React.useEffect(() => {
|
||||
// fix: delete drawer in child and re-render, no push started.
|
||||
// <Drawer>{show && <Drawer />}</Drawer>
|
||||
if (propsVisible && parentDrawer) {
|
||||
parentDrawer.push();
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (parentDrawer) {
|
||||
parentDrawer.pull();
|
||||
// parentDrawer = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (parentDrawer) {
|
||||
if (visible) {
|
||||
parentDrawer.push();
|
||||
} else {
|
||||
parentDrawer.pull();
|
||||
}
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
const operations = React.useMemo(
|
||||
() => ({
|
||||
push() {
|
||||
if (push) {
|
||||
setPush(true);
|
||||
}
|
||||
},
|
||||
pull() {
|
||||
if (push) {
|
||||
setPush(false);
|
||||
}
|
||||
},
|
||||
}),
|
||||
[push],
|
||||
);
|
||||
|
||||
React.useImperativeHandle(ref, () => operations, [operations]);
|
||||
|
||||
const getOffsetStyle = () => {
|
||||
// https://github.com/ant-design/ant-design/issues/24287
|
||||
if (!visible && !mask) {
|
||||
return {};
|
||||
}
|
||||
const offsetStyle: any = {};
|
||||
if (placement === 'left' || placement === 'right') {
|
||||
const defaultWidth = size === 'large' ? 736 : 378;
|
||||
offsetStyle.width = typeof width === 'undefined' ? defaultWidth : width;
|
||||
} else {
|
||||
const defaultHeight = size === 'large' ? 736 : 378;
|
||||
offsetStyle.height = typeof height === 'undefined' ? defaultHeight : height;
|
||||
}
|
||||
return offsetStyle;
|
||||
};
|
||||
|
||||
const getRcDrawerStyle = () => {
|
||||
// get drawer push width or height
|
||||
const getPushTransform = (_placement?: placementType) => {
|
||||
let distance: number | string;
|
||||
if (typeof push === 'boolean') {
|
||||
distance = push ? defaultPushState.distance : 0;
|
||||
} else {
|
||||
distance = push!.distance;
|
||||
}
|
||||
distance = parseFloat(String(distance || 0));
|
||||
|
||||
if (_placement === 'left' || _placement === 'right') {
|
||||
return `translateX(${_placement === 'left' ? distance : -distance}px)`;
|
||||
}
|
||||
if (_placement === 'top' || _placement === 'bottom') {
|
||||
return `translateY(${_placement === 'top' ? distance : -distance}px)`;
|
||||
}
|
||||
};
|
||||
|
||||
// 当无 mask 时,将 width 应用到外层容器上
|
||||
// 解决 https://github.com/ant-design/ant-design/issues/12401 的问题
|
||||
const offsetStyle = mask ? {} : getOffsetStyle();
|
||||
return {
|
||||
zIndex,
|
||||
transform: internalPush ? getPushTransform(placement) : undefined,
|
||||
...offsetStyle,
|
||||
...style,
|
||||
};
|
||||
};
|
||||
|
||||
const closeIconNode = closable && (
|
||||
<button type="button" onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
|
||||
{closeIcon}
|
||||
</button>
|
||||
);
|
||||
|
||||
function renderHeader() {
|
||||
if (!title && !closable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(`${prefixCls}-header`, {
|
||||
[`${prefixCls}-header-close-only`]: closable && !title && !extra,
|
||||
})}
|
||||
style={headerStyle}
|
||||
>
|
||||
<div className={`${prefixCls}-header-title`}>
|
||||
{closeIconNode}
|
||||
{title && <div className={`${prefixCls}-title`}>{title}</div>}
|
||||
</div>
|
||||
{extra && <div className={`${prefixCls}-extra`}>{extra}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderFooter() {
|
||||
if (!footer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const footerClassName = `${prefixCls}-footer`;
|
||||
return (
|
||||
<div className={footerClassName} style={footerStyle}>
|
||||
{footer}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// render drawer body dom
|
||||
const renderBody = () => {
|
||||
// destroyCloseRef.current =false Load the body only once by default
|
||||
if (destroyCloseRef.current && !forceRender && !propsVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
// =========================== Render ===========================
|
||||
return wrapSSR(
|
||||
<NoFormStyle status override>
|
||||
<RcDrawer
|
||||
prefixCls={prefixCls}
|
||||
onClose={onClose}
|
||||
{...rest}
|
||||
open={visible}
|
||||
mask={mask}
|
||||
push={push}
|
||||
width={mergedWidth}
|
||||
height={mergedHeight}
|
||||
rootClassName={drawerClassName}
|
||||
getContainer={getContainer}
|
||||
afterOpenChange={open => {
|
||||
afterVisibleChange?.(open);
|
||||
}}
|
||||
maskMotion={maskMotion}
|
||||
motion={panelMotion}
|
||||
rootStyle={style}
|
||||
>
|
||||
<div className={`${prefixCls}-wrapper-body`} style={{ ...drawerStyle }}>
|
||||
{renderHeader()}
|
||||
<div className={`${prefixCls}-body`} style={bodyStyle}>
|
||||
@ -285,58 +206,11 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
|
||||
</div>
|
||||
{renderFooter()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</RcDrawer>
|
||||
</NoFormStyle>,
|
||||
);
|
||||
}
|
||||
|
||||
const drawerClassName = classNames(
|
||||
{
|
||||
'no-mask': !mask,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
hashId,
|
||||
);
|
||||
const offsetStyle = mask ? getOffsetStyle() : {};
|
||||
|
||||
return wrapSSR(
|
||||
<DrawerContext.Provider value={operations}>
|
||||
<NoFormStyle status override>
|
||||
<RcDrawer
|
||||
handler={false}
|
||||
{...{
|
||||
placement,
|
||||
prefixCls,
|
||||
maskClosable,
|
||||
level,
|
||||
keyboard,
|
||||
children,
|
||||
onClose,
|
||||
forceRender,
|
||||
...rest,
|
||||
}}
|
||||
{...offsetStyle}
|
||||
open={visible || propsVisible}
|
||||
showMask={mask}
|
||||
style={getRcDrawerStyle()}
|
||||
className={drawerClassName}
|
||||
getContainer={getContainer}
|
||||
afterVisibleChange={open => {
|
||||
if (open) {
|
||||
destroyCloseRef.current = false;
|
||||
} else if (destroyOnClose) {
|
||||
destroyCloseRef.current = true;
|
||||
setLoad(false);
|
||||
}
|
||||
afterVisibleChange?.(open);
|
||||
}}
|
||||
>
|
||||
{renderBody()}
|
||||
</RcDrawer>
|
||||
</NoFormStyle>
|
||||
</DrawerContext.Provider>,
|
||||
);
|
||||
},
|
||||
);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Drawer.displayName = 'Drawer';
|
||||
}
|
||||
|
@ -1,254 +1,211 @@
|
||||
import type { CSSObject } from '@ant-design/cssinjs';
|
||||
import { Keyframes } from '@ant-design/cssinjs';
|
||||
import type { FullToken, GenerateStyle } from '../../theme';
|
||||
import { genComponentStyleHook, mergeToken } from '../../theme';
|
||||
import genMotionStyle from './motion';
|
||||
|
||||
export interface ComponentToken {
|
||||
zIndexPopup: number;
|
||||
}
|
||||
|
||||
export interface DrawerToken extends FullToken<'Drawer'> {
|
||||
drawerFooterPaddingVertical: number;
|
||||
drawerFooterPaddingHorizontal: number;
|
||||
}
|
||||
|
||||
const antdDrawerFadeIn = new Keyframes('antDrawerFadeIn', {
|
||||
'0%': { opacity: 0 },
|
||||
'100%': { opacity: 1 },
|
||||
});
|
||||
|
||||
// =============================== Base ===============================
|
||||
const genBaseStyle: GenerateStyle<DrawerToken> = (token: DrawerToken): CSSObject => {
|
||||
const genDrawerStyle: GenerateStyle<DrawerToken> = (token: DrawerToken) => {
|
||||
const {
|
||||
componentCls,
|
||||
motionEaseOut,
|
||||
zIndexPopup,
|
||||
colorBgMask,
|
||||
colorBgElevated,
|
||||
motionDurationSlow,
|
||||
fontSizeLG,
|
||||
paddingLG,
|
||||
lineWidth,
|
||||
radiusBase,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
drawerFooterPaddingVertical,
|
||||
drawerFooterPaddingHorizontal,
|
||||
zIndexPopupBase,
|
||||
colorText,
|
||||
padding,
|
||||
paddingLG,
|
||||
fontSizeLG,
|
||||
lineHeightLG,
|
||||
lineWidth,
|
||||
lineType,
|
||||
colorSplit,
|
||||
marginSM,
|
||||
colorIcon,
|
||||
colorIconHover,
|
||||
colorText,
|
||||
fontWeightStrong,
|
||||
drawerFooterPaddingVertical,
|
||||
drawerFooterPaddingHorizontal,
|
||||
} = token;
|
||||
|
||||
const wrapperCls = `${componentCls}-content-wrapper`;
|
||||
|
||||
return {
|
||||
[`${componentCls}`]: {
|
||||
[componentCls]: {
|
||||
position: 'fixed',
|
||||
zIndex: zIndexPopupBase,
|
||||
width: 0,
|
||||
height: '100%',
|
||||
transition: `width 0s ease ${motionDurationSlow}, height 0s ease ${motionDurationSlow}`,
|
||||
[`${componentCls}-content-wrapper`]: {
|
||||
inset: 0,
|
||||
zIndex: zIndexPopup,
|
||||
pointerEvents: 'none',
|
||||
|
||||
'&-inline': {
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
transition: `transform ${motionDurationSlow} ${motionEaseOut},box-shadow ${motionDurationSlow} ${motionEaseOut}`,
|
||||
[`${componentCls}-content`]: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
overflow: 'auto',
|
||||
backgroundColor: token.colorBgElevated,
|
||||
backgroundClip: `padding-box`,
|
||||
border: 0,
|
||||
[`${componentCls}-wrapper-body`]: {
|
||||
display: 'flex',
|
||||
flexFlow: 'column nowrap',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
[`${componentCls}-header`]: {
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: `${padding}px ${paddingLG}px`,
|
||||
color: colorText,
|
||||
background: token.colorBgElevated,
|
||||
borderBottom: `${lineWidth}px ${lineType} ${colorSplit}`,
|
||||
borderRadius: `${radiusBase}px ${radiusBase}px 0 0`,
|
||||
|
||||
[`${componentCls}-header-title`]: {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
[`${componentCls}-title`]: {
|
||||
flex: 1,
|
||||
margin: 0,
|
||||
color: colorText,
|
||||
fontWeight: token.fontWeightStrong,
|
||||
fontSize: fontSizeLG,
|
||||
lineHeight: token.lineHeightLG,
|
||||
},
|
||||
[`${componentCls}-close`]: {
|
||||
display: 'inline-block',
|
||||
marginInlineEnd: token.marginSM,
|
||||
color: token.colorIcon,
|
||||
fontWeight: token.fontWeightStrong,
|
||||
fontSize: fontSizeLG,
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1,
|
||||
textAlign: 'center',
|
||||
textTransform: 'none',
|
||||
textDecoration: 'none',
|
||||
background: 'transparent',
|
||||
border: 0,
|
||||
outline: 0,
|
||||
cursor: 'pointer',
|
||||
transition: `color ${motionDurationSlow}`,
|
||||
textRendering: 'auto',
|
||||
|
||||
[`&:focus, &:hover`]: {
|
||||
color: token.colorIconHover,
|
||||
textDecoration: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
[`${componentCls}-header-close-only`]: {
|
||||
paddingBottom: 0,
|
||||
border: 'none',
|
||||
},
|
||||
},
|
||||
[`${componentCls}-body`]: {
|
||||
flexGrow: 1,
|
||||
padding: paddingLG,
|
||||
overflow: 'auto',
|
||||
fontSize,
|
||||
lineHeight,
|
||||
wordWrap: 'break-word',
|
||||
},
|
||||
[`${componentCls}-footer`]: {
|
||||
flexShrink: 0,
|
||||
padding: `${drawerFooterPaddingVertical}px ${drawerFooterPaddingHorizontal}px`,
|
||||
borderTop: `${lineWidth}px ${lineType} ${colorSplit}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ====================== Mask ======================
|
||||
[`${componentCls}-mask`]: {
|
||||
position: 'absolute',
|
||||
insetBlockStart: 0,
|
||||
insetInlineStart: 0,
|
||||
width: '100%',
|
||||
height: 0,
|
||||
backgroundColor: token.colorBgMask,
|
||||
opacity: 0,
|
||||
transition: `opacity ${motionDurationSlow} linear, height 0s ease ${motionDurationSlow}`,
|
||||
pointerEvents: 'none',
|
||||
inset: 0,
|
||||
zIndex: zIndexPopup,
|
||||
background: colorBgMask,
|
||||
pointerEvents: 'auto',
|
||||
},
|
||||
},
|
||||
[`${componentCls}${componentCls}-open ${componentCls}-mask`]: {
|
||||
height: '100%',
|
||||
opacity: 1,
|
||||
transition: 'none',
|
||||
animationName: antdDrawerFadeIn,
|
||||
animationDuration: token.motionDurationSlow,
|
||||
animationTimingFunction: motionEaseOut,
|
||||
pointerEvents: 'auto',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genDrawerStyle: GenerateStyle<DrawerToken> = (token: DrawerToken) => {
|
||||
const { componentCls, motionDurationSlow, lineWidth, motionEaseOut } = token;
|
||||
// ==================== Content =====================
|
||||
[wrapperCls]: {
|
||||
position: 'absolute',
|
||||
zIndex: zIndexPopup,
|
||||
transition: `all ${motionDurationSlow}`,
|
||||
|
||||
return {
|
||||
// =================== left,right ===================
|
||||
[`${componentCls}-left`]: {
|
||||
insetInlineStart: 0,
|
||||
insetBlockStart: 0,
|
||||
width: 0,
|
||||
height: '100%',
|
||||
[`${componentCls}-content-wrapper`]: {
|
||||
height: '100%',
|
||||
insetInlineStart: 0,
|
||||
'&-hidden': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
[`${componentCls}-left${componentCls}-open`]: {
|
||||
width: '100%',
|
||||
transition: `transform ${motionDurationSlow} ${motionEaseOut}`,
|
||||
[`${componentCls}-content-wrapper`]: {
|
||||
|
||||
// Placement
|
||||
[`&-left ${wrapperCls}`]: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
boxShadow: token.boxShadowDrawerRight,
|
||||
},
|
||||
},
|
||||
[`${componentCls}-right`]: {
|
||||
insetInlineEnd: 0,
|
||||
insetBlockStart: 0,
|
||||
width: 0,
|
||||
height: '100%',
|
||||
[`${componentCls}-content-wrapper`]: {
|
||||
height: '100%',
|
||||
insetInlineEnd: 0,
|
||||
},
|
||||
},
|
||||
[`${componentCls}-right${componentCls}-open`]: {
|
||||
width: '100%',
|
||||
transition: `transform ${motionDurationSlow} ${motionEaseOut}`,
|
||||
[`${componentCls}-content-wrapper`]: {
|
||||
[`&-right ${wrapperCls}`]: {
|
||||
top: 0,
|
||||
right: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
bottom: 0,
|
||||
boxShadow: token.boxShadowDrawerLeft,
|
||||
},
|
||||
},
|
||||
// https://github.com/ant-design/ant-design/issues/18607, Avoid edge alignment bug.
|
||||
[`${componentCls}-right${componentCls}-open.no-mask`]: {
|
||||
insetInlineEnd: lineWidth,
|
||||
transform: `translateX(${lineWidth})`,
|
||||
},
|
||||
|
||||
// =================== top,bottom ===================
|
||||
[`${componentCls}-top,${componentCls}-bottom`]: {
|
||||
insetInlineStart: 0,
|
||||
width: '100%',
|
||||
height: 0,
|
||||
[`${componentCls}-content-wrapper`]: {
|
||||
width: '100%',
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-top${componentCls}-open,${componentCls}-bottom${componentCls}-open`]: {
|
||||
height: '100%',
|
||||
transition: `transform ${motionDurationSlow} ${motionEaseOut}`,
|
||||
},
|
||||
|
||||
[`${componentCls}-top`]: {
|
||||
insetBlockStart: 0,
|
||||
},
|
||||
|
||||
[`${componentCls}-top${componentCls}-open`]: {
|
||||
[`${componentCls}-content-wrapper`]: {
|
||||
[`&-top ${wrapperCls}`]: {
|
||||
top: 0,
|
||||
insetInline: 0,
|
||||
boxShadow: token.boxShadowDrawerDown,
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-bottom`]: {
|
||||
bottom: 0,
|
||||
[`${componentCls}-content-wrapper`]: {
|
||||
[`&-bottom ${wrapperCls}`]: {
|
||||
bottom: 0,
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-bottom${componentCls}-bottom-open`]: {
|
||||
[`${componentCls}-content-wrapper`]: {
|
||||
insetInline: 0,
|
||||
boxShadow: token.boxShadowDrawerUp,
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-bottom${componentCls}-bottom-open.no-mask`]: {
|
||||
insetBlockEnd: lineWidth,
|
||||
transform: `translateY(${lineWidth})`,
|
||||
[`${componentCls}-content`]: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
background: colorBgElevated,
|
||||
pointerEvents: 'auto',
|
||||
},
|
||||
|
||||
// ===================== Panel ======================
|
||||
[`${componentCls}-wrapper-body`]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
|
||||
// Header
|
||||
[`${componentCls}-header`]: {
|
||||
display: 'flex',
|
||||
flex: 0,
|
||||
alignItems: 'center',
|
||||
padding: `${padding}px ${paddingLG}px`,
|
||||
fontSize: fontSizeLG,
|
||||
lineHeight: lineHeightLG,
|
||||
borderBottom: `${lineWidth}px ${lineType} ${colorSplit}`,
|
||||
|
||||
'&-title': {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-extra`]: {
|
||||
flex: 0,
|
||||
},
|
||||
|
||||
[`${componentCls}-close`]: {
|
||||
display: 'inline-block',
|
||||
marginInlineEnd: marginSM,
|
||||
color: colorIcon,
|
||||
fontWeight: fontWeightStrong,
|
||||
fontSize: fontSizeLG,
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1,
|
||||
textAlign: 'center',
|
||||
textTransform: 'none',
|
||||
textDecoration: 'none',
|
||||
background: 'transparent',
|
||||
border: 0,
|
||||
outline: 0,
|
||||
cursor: 'pointer',
|
||||
transition: `color ${motionDurationSlow}`,
|
||||
textRendering: 'auto',
|
||||
|
||||
'&:focus, &:hover': {
|
||||
color: colorIconHover,
|
||||
textDecoration: 'none',
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-title`]: {
|
||||
flex: 1,
|
||||
margin: 0,
|
||||
color: colorText,
|
||||
fontWeight: token.fontWeightStrong,
|
||||
fontSize: fontSizeLG,
|
||||
lineHeight: lineHeightLG,
|
||||
},
|
||||
|
||||
// Body
|
||||
[`${componentCls}-body`]: {
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
padding: paddingLG,
|
||||
overflow: 'auto',
|
||||
},
|
||||
|
||||
// Footer
|
||||
[`${componentCls}-footer`]: {
|
||||
flexShrink: 0,
|
||||
padding: `${drawerFooterPaddingVertical}px ${drawerFooterPaddingHorizontal}px`,
|
||||
borderTop: `${lineWidth}px ${lineType} ${colorSplit}`,
|
||||
},
|
||||
|
||||
// ====================== RTL =======================
|
||||
'&-rtl': {
|
||||
direction: 'rtl',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default genComponentStyleHook('Drawer', token => {
|
||||
const drawerToken = mergeToken<DrawerToken>(token, {
|
||||
drawerFooterPaddingVertical: token.paddingXS,
|
||||
drawerFooterPaddingHorizontal: token.padding,
|
||||
});
|
||||
export default genComponentStyleHook(
|
||||
'Drawer',
|
||||
token => {
|
||||
const drawerToken = mergeToken<DrawerToken>(token, {
|
||||
drawerFooterPaddingVertical: token.paddingXS,
|
||||
drawerFooterPaddingHorizontal: token.padding,
|
||||
});
|
||||
|
||||
return [genBaseStyle(drawerToken), genDrawerStyle(drawerToken)];
|
||||
});
|
||||
return [genDrawerStyle(drawerToken), genMotionStyle(drawerToken)];
|
||||
},
|
||||
token => ({
|
||||
zIndexPopup: token.zIndexPopupBase,
|
||||
}),
|
||||
);
|
||||
|
126
components/drawer/style/motion.tsx
Normal file
126
components/drawer/style/motion.tsx
Normal file
@ -0,0 +1,126 @@
|
||||
import type { DrawerToken } from '.';
|
||||
import type { GenerateStyle } from '../../theme';
|
||||
|
||||
const genMotionStyle: GenerateStyle<DrawerToken> = (token: DrawerToken) => {
|
||||
const { componentCls, motionDurationSlow } = token;
|
||||
|
||||
const sharedPanelMotion = {
|
||||
'&-enter, &-appear, &-leave': {
|
||||
'&-start': {
|
||||
transition: 'none',
|
||||
},
|
||||
|
||||
'&-active': {
|
||||
transition: `all ${motionDurationSlow}`,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
[componentCls]: {
|
||||
// ======================== Mask ========================
|
||||
[`${componentCls}-mask-motion`]: {
|
||||
'&-enter, &-appear, &-leave': {
|
||||
'&-active': {
|
||||
transition: `all ${motionDurationSlow}`,
|
||||
},
|
||||
},
|
||||
|
||||
'&-enter, &-appear': {
|
||||
opacity: 0,
|
||||
'&-active': {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
|
||||
'&-leave': {
|
||||
opacity: 1,
|
||||
'&-active': {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ======================= Panel ========================
|
||||
[`${componentCls}-panel-motion`]: {
|
||||
// Left
|
||||
'&-left': [
|
||||
sharedPanelMotion,
|
||||
{
|
||||
'&-enter, &-appear': {
|
||||
transform: 'translateX(-100%)',
|
||||
'&-active': {
|
||||
transform: 'translateX(0)',
|
||||
},
|
||||
},
|
||||
'&-leave': {
|
||||
transform: 'translateX(0)',
|
||||
'&-active': {
|
||||
transform: 'translateX(-100%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
// Right
|
||||
'&-right': [
|
||||
sharedPanelMotion,
|
||||
{
|
||||
'&-enter, &-appear': {
|
||||
transform: 'translateX(100%)',
|
||||
'&-active': {
|
||||
transform: 'translateX(0)',
|
||||
},
|
||||
},
|
||||
'&-leave': {
|
||||
transform: 'translateX(0)',
|
||||
'&-active': {
|
||||
transform: 'translateX(100%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
// Top
|
||||
'&-top': [
|
||||
sharedPanelMotion,
|
||||
{
|
||||
'&-enter, &-appear': {
|
||||
transform: 'translateY(-100%)',
|
||||
'&-active': {
|
||||
transform: 'translateY(0)',
|
||||
},
|
||||
},
|
||||
'&-leave': {
|
||||
transform: 'translateY(0)',
|
||||
'&-active': {
|
||||
transform: 'translateY(-100%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
// Bottom
|
||||
'&-bottom': [
|
||||
sharedPanelMotion,
|
||||
{
|
||||
'&-enter, &-appear': {
|
||||
transform: 'translateY(100%)',
|
||||
'&-active': {
|
||||
transform: 'translateY(0)',
|
||||
},
|
||||
},
|
||||
'&-leave': {
|
||||
transform: 'translateY(0)',
|
||||
'&-active': {
|
||||
transform: 'translateY(100%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default genMotionStyle;
|
@ -17,6 +17,7 @@ const genGridRowStyle: GenerateStyle<GridRowToken> = (token): CSSObject => {
|
||||
[componentCls]: {
|
||||
display: 'flex',
|
||||
flexFlow: 'row wrap',
|
||||
minWidth: 0,
|
||||
|
||||
'&::before, &::after': {
|
||||
display: 'flex',
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
|
||||
import InputNumber from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
|
||||
describe('InputNumber', () => {
|
||||
focusTest(InputNumber, { refFocus: true });
|
||||
@ -14,32 +14,35 @@ describe('InputNumber', () => {
|
||||
// https://github.com/ant-design/ant-design/issues/13896
|
||||
it('should return null when blur a empty input number', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(<InputNumber defaultValue="1" onChange={onChange} />);
|
||||
wrapper.find('input').simulate('change', { target: { value: '' } });
|
||||
const { container } = render(<InputNumber defaultValue="1" onChange={onChange} />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: '' } });
|
||||
expect(onChange).toHaveBeenLastCalledWith(null);
|
||||
});
|
||||
|
||||
it('should call onStep when press up or down button', () => {
|
||||
const onStep = jest.fn();
|
||||
const wrapper = mount(<InputNumber defaultValue={1} onStep={onStep} />);
|
||||
wrapper.find('.ant-input-number-handler-up').simulate('mousedown');
|
||||
const { container } = render(<InputNumber defaultValue={1} onStep={onStep} />);
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-up'));
|
||||
expect(onStep).toBeCalledTimes(1);
|
||||
expect(onStep).toHaveBeenLastCalledWith(2, { offset: 1, type: 'up' });
|
||||
wrapper.find('.ant-input-number-handler-down').simulate('mousedown');
|
||||
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-down'));
|
||||
expect(onStep).toBeCalledTimes(2);
|
||||
expect(onStep).toHaveBeenLastCalledWith(1, { offset: 1, type: 'down' });
|
||||
});
|
||||
|
||||
it('renders correctly when controls is boolean', () => {
|
||||
expect(mount(<InputNumber controls={false} />).render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<InputNumber controls={false} />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders correctly when controls is {}', () => {
|
||||
expect(mount(<InputNumber controls={{}} />).render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<InputNumber controls={{}} />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders correctly when controls has custom upIcon and downIcon', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<InputNumber
|
||||
controls={{
|
||||
upIcon: <ArrowUpOutlined />,
|
||||
@ -47,11 +50,11 @@ describe('InputNumber', () => {
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support className', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<InputNumber
|
||||
controls={{
|
||||
upIcon: <ArrowUpOutlined className="my-class-name" />,
|
||||
@ -59,11 +62,11 @@ describe('InputNumber', () => {
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find('.anticon-arrow-up').getDOMNode().className.includes('my-class-name')).toBe(
|
||||
expect(container.querySelector('.anticon-arrow-up')?.className.includes('my-class-name')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
wrapper.find('.anticon-arrow-down').getDOMNode().className.includes('my-class-name'),
|
||||
container.querySelector('.anticon-arrow-down')?.className.includes('my-class-name'),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@ -1,25 +1,24 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import React, { forwardRef } from 'react';
|
||||
import InputNumber from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
|
||||
describe('prefix', () => {
|
||||
focusTest(
|
||||
React.forwardRef((props, ref) => <InputNumber {...props} prefix="A" ref={ref} />),
|
||||
forwardRef((props, ref) => <InputNumber {...props} prefix="A" ref={ref} />),
|
||||
{ refFocus: true },
|
||||
);
|
||||
it('should support className when has prefix', () => {
|
||||
const { container } = render(<InputNumber prefix="suffix" className="my-class-name" />);
|
||||
expect(container.firstChild.className.includes('my-class-name')).toBe(true);
|
||||
expect(container.firstChild?.className.includes('my-class-name')).toBe(true);
|
||||
expect(container.querySelector('input')?.className.includes('my-class-name')).toBe(false);
|
||||
});
|
||||
|
||||
it('should trigger focus when prefix is clicked', () => {
|
||||
const wrapper = mount(<InputNumber prefix={<i>123</i>} />);
|
||||
const { container } = render(<InputNumber prefix={<i>123</i>} />);
|
||||
|
||||
const mockFocus = jest.spyOn(wrapper.find('input').getDOMNode(), 'focus');
|
||||
wrapper.find('i').simulate('mouseUp');
|
||||
const mockFocus = jest.spyOn(container.querySelector('input'), 'focus');
|
||||
fireEvent.mouseUp(container.querySelector('i'));
|
||||
expect(mockFocus).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
@ -42,7 +42,7 @@ export function resolveOnChange<E extends HTMLInputElement | HTMLTextAreaElement
|
||||
if (!onChange) {
|
||||
return;
|
||||
}
|
||||
let event = e;
|
||||
let event = e as React.ChangeEvent<E>;
|
||||
|
||||
if (e.type === 'click') {
|
||||
// Clone a new target for event.
|
||||
@ -66,7 +66,7 @@ export function resolveOnChange<E extends HTMLInputElement | HTMLTextAreaElement
|
||||
});
|
||||
|
||||
currentTarget.value = '';
|
||||
onChange(event as React.ChangeEvent<E>);
|
||||
onChange(event);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -78,10 +78,10 @@ export function resolveOnChange<E extends HTMLInputElement | HTMLTextAreaElement
|
||||
});
|
||||
|
||||
target.value = targetValue;
|
||||
onChange(event as React.ChangeEvent<E>);
|
||||
onChange(event);
|
||||
return;
|
||||
}
|
||||
onChange(event as React.ChangeEvent<E>);
|
||||
onChange(event);
|
||||
}
|
||||
|
||||
export function triggerFocus(
|
||||
|
@ -74,13 +74,13 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
|
||||
[`${prefixCls}-${size}`]: !!size,
|
||||
});
|
||||
|
||||
const omittedProps = {
|
||||
const omittedProps: InputProps = {
|
||||
...omit(restProps, ['suffix', 'iconRender']),
|
||||
type: visible ? 'text' : 'password',
|
||||
className: inputClassName,
|
||||
prefixCls: inputPrefixCls,
|
||||
suffix: suffixIcon,
|
||||
} as InputProps;
|
||||
};
|
||||
|
||||
if (size) {
|
||||
omittedProps.size = size;
|
||||
|
@ -166,3 +166,14 @@ const dividerItem = {
|
||||
### Why will Menu's children be rendered twice?
|
||||
|
||||
Menu collects structure info with [twice-render](https://github.com/react-component/menu/blob/f4684514096d6b7123339cbe72e7b0f68db0bce2/src/Menu.tsx#L543) to support HOC usage. Merging into one render may cause the logic to become much more complex. Contributions to help improve the collection logic are welcomed.
|
||||
|
||||
### Why Menu do not responsive collapse in Flex layout?
|
||||
|
||||
Menu will render fully item in flex layout and then collapse it. You need tell flex not consider Menu width to enable responsive ([online demo](https://codesandbox.io/s/ding-bu-dao-hang-antd-4-21-7-forked-5e3imy?file=/demo.js)):
|
||||
|
||||
```jsx
|
||||
<div style={{ flex }}>
|
||||
<div style={{ ... }}>Some Content</div>
|
||||
<Menu style={{ minWidth: 0, flex: "auto" }} />
|
||||
</div>
|
||||
```
|
||||
|
@ -120,7 +120,7 @@ return <Menu items={items} />;
|
||||
#### SubMenuType
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| children | 子菜单的菜单项 | [ItemType\[\]](#ItemType) | - | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| icon | 菜单图标 | ReactNode | - | |
|
||||
@ -129,7 +129,7 @@ return <Menu items={items} />;
|
||||
| popupClassName | 子菜单样式,`mode="inline"` 时无效 | string | - | |
|
||||
| popupOffset | 子菜单偏移量,`mode="inline"` 时无效 | \[number, number] | - | |
|
||||
| onTitleClick | 点击子菜单标题 | function({ key, domEvent }) | - | |
|
||||
| theme | 设置子菜单的主题,默认从 Menu 上继承 | | `light` \| `dark` | - | |
|
||||
| theme | 设置子菜单的主题,默认从 Menu 上继承 | `light` \| `dark` | - | |
|
||||
|
||||
#### MenuItemGroupType
|
||||
|
||||
@ -167,3 +167,14 @@ const dividerItem = {
|
||||
### 为何 Menu 的子元素会渲染两次?
|
||||
|
||||
Menu 通过[二次渲染](https://github.com/react-component/menu/blob/f4684514096d6b7123339cbe72e7b0f68db0bce2/src/Menu.tsx#L543)收集嵌套结构信息以支持 HOC 的结构。合并成一个推导结构会使得逻辑变得十分复杂,欢迎 PR 以协助改进该设计。
|
||||
|
||||
### 在 Flex 布局中,Menu 没有按照预期响应式省略菜单?
|
||||
|
||||
Menu 初始化时会先全部渲染,然后根据宽度裁剪内容。当处于 Flex 布局中,你需要告知其预期宽度为响应式宽度([在线 Demo](https://codesandbox.io/s/ding-bu-dao-hang-antd-4-21-7-forked-5e3imy?file=/demo.js)):
|
||||
|
||||
```jsx
|
||||
<div style={{ flex }}>
|
||||
<div style={{ ... }}>Some Content</div>
|
||||
<Menu style={{ minWidth: 0, flex: "auto" }} />
|
||||
</div>
|
||||
```
|
||||
|
@ -305,7 +305,7 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
});
|
||||
|
||||
describe('should not close modals when click confirm button when onOk has argument', () => {
|
||||
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
|
||||
it(type, async () => {
|
||||
jest.useFakeTimers();
|
||||
Modal[type]({
|
||||
@ -318,7 +318,7 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
await sleep();
|
||||
});
|
||||
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
|
||||
$$('.ant-btn')[0].click();
|
||||
$$('.ant-btn-primary')[0].click();
|
||||
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
@ -674,4 +674,149 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
const { width } = $$('.ant-modal-body')[0].style;
|
||||
expect(width).toBe('500px');
|
||||
});
|
||||
|
||||
describe('the callback close should be a method when onCancel has a close parameter', () => {
|
||||
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
|
||||
it(`click the close icon to trigger ${type} onCancel`, async () => {
|
||||
jest.useFakeTimers();
|
||||
const mock = jest.fn();
|
||||
|
||||
Modal[type]({
|
||||
closable: true,
|
||||
onCancel: close => mock(close),
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
});
|
||||
|
||||
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
|
||||
$$('.ant-modal-close')[0].click();
|
||||
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
});
|
||||
|
||||
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0);
|
||||
expect(mock).toBeCalledWith(expect.any(Function));
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
|
||||
it(`press ESC to trigger ${type} onCancel`, async () => {
|
||||
jest.useFakeTimers();
|
||||
const mock = jest.fn();
|
||||
|
||||
Modal[type]({
|
||||
keyboard: true,
|
||||
onCancel: close => mock(close),
|
||||
});
|
||||
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
jest.runAllTimers();
|
||||
|
||||
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
|
||||
TestUtils.Simulate.keyDown($$('.ant-modal')[0], {
|
||||
keyCode: KeyCode.ESC,
|
||||
});
|
||||
|
||||
jest.runAllTimers();
|
||||
await sleep(0);
|
||||
jest.runAllTimers();
|
||||
|
||||
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0);
|
||||
expect(mock).toBeCalledWith(expect.any(Function));
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
|
||||
it(`click the mask to trigger ${type} onCancel`, async () => {
|
||||
jest.useFakeTimers();
|
||||
const mock = jest.fn();
|
||||
|
||||
Modal[type]({
|
||||
maskClosable: true,
|
||||
onCancel: close => mock(close),
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
});
|
||||
|
||||
expect($$('.ant-modal-mask')).toHaveLength(1);
|
||||
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
|
||||
|
||||
$$('.ant-modal-wrap')[0].click();
|
||||
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
});
|
||||
|
||||
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0);
|
||||
expect(mock).toBeCalledWith(expect.any(Function));
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('confirm modal click Cancel button close callback is a function', async () => {
|
||||
jest.useFakeTimers();
|
||||
const mock = jest.fn();
|
||||
|
||||
Modal.confirm({
|
||||
onCancel: close => mock(close),
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
});
|
||||
|
||||
$$('.ant-modal-confirm-btns > .ant-btn')[0].click();
|
||||
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
});
|
||||
|
||||
expect(mock).toBeCalledWith(expect.any(Function));
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('close can close modal when onCancel has a close parameter', async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
Modal.confirm({
|
||||
onCancel: close => close(),
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
});
|
||||
|
||||
expect($$('.ant-modal-confirm-confirm')).toHaveLength(1);
|
||||
|
||||
$$('.ant-modal-confirm-btns > .ant-btn')[0].click();
|
||||
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
});
|
||||
|
||||
expect($$('.ant-modal-confirm-confirm')).toHaveLength(0);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
@ -1,9 +1,11 @@
|
||||
import CSSMotion from 'rc-motion';
|
||||
import { genCSSMotion } from 'rc-motion/lib/CSSMotion';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import TestUtils, { act } from 'react-dom/test-utils';
|
||||
|
||||
import Modal from '..';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import Input from '../../input';
|
||||
@ -193,4 +195,110 @@ describe('Modal.hook', () => {
|
||||
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
|
||||
expect(document.body.classList.contains('ant-modal-confirm-title')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('the callback close should be a method when onCancel has a close parameter', async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const clear = async function clear() {
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
});
|
||||
};
|
||||
|
||||
const mockFn = jest.fn();
|
||||
const Demo = () => {
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
|
||||
const openBrokenModal = React.useCallback(() => {
|
||||
modal.confirm({
|
||||
closable: true,
|
||||
keyboard: true,
|
||||
maskClosable: true,
|
||||
onCancel: close => mockFn(close),
|
||||
});
|
||||
}, [modal]);
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
{contextHolder}
|
||||
<div className="open-hook-modal-btn" onClick={openBrokenModal}>
|
||||
Test hook modal
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const { container } = render(<Demo />);
|
||||
|
||||
await clear();
|
||||
|
||||
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(0);
|
||||
// First open
|
||||
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
|
||||
await clear();
|
||||
|
||||
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(1);
|
||||
// Click mask to close
|
||||
fireEvent.click(document.body.querySelectorAll('.ant-modal-wrap')[0]);
|
||||
|
||||
await clear();
|
||||
|
||||
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(0);
|
||||
// Second open
|
||||
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
|
||||
|
||||
await clear();
|
||||
|
||||
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(1);
|
||||
// Press ESC to turn off
|
||||
TestUtils.Simulate.keyDown(document.body.querySelectorAll('.ant-modal')[0], {
|
||||
keyCode: KeyCode.ESC,
|
||||
});
|
||||
|
||||
await clear();
|
||||
|
||||
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(0);
|
||||
// Third open
|
||||
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
|
||||
|
||||
await clear();
|
||||
|
||||
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(1);
|
||||
// Click the close icon to close
|
||||
fireEvent.click(document.body.querySelectorAll('.ant-modal-close')[0]);
|
||||
|
||||
await clear();
|
||||
|
||||
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(0);
|
||||
// Last open
|
||||
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
|
||||
|
||||
await clear();
|
||||
|
||||
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(1);
|
||||
|
||||
// Click the Cancel button to close (invalid)
|
||||
fireEvent.click(document.body.querySelectorAll('.ant-modal-confirm-btns > .ant-btn')[0]);
|
||||
|
||||
await clear();
|
||||
|
||||
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(1);
|
||||
|
||||
mockFn.mockImplementation(close => close());
|
||||
|
||||
// Click the Cancel button to close (valid)
|
||||
fireEvent.click(document.body.querySelectorAll('.ant-modal-confirm-btns > .ant-btn')[0]);
|
||||
|
||||
await clear();
|
||||
|
||||
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(0);
|
||||
|
||||
// Close called 5 times
|
||||
expect(mockFn).toHaveBeenCalledTimes(5);
|
||||
|
||||
expect(mockFn.mock.calls).toEqual(Array.from({ length: 5 }, () => [expect.any(Function)]));
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
@ -30,7 +30,7 @@ export default function confirm(config: ModalFuncProps) {
|
||||
function destroy(...args: any[]) {
|
||||
const triggerCancel = args.some(param => param && param.triggerCancel);
|
||||
if (config.onCancel && triggerCancel) {
|
||||
config.onCancel(...args);
|
||||
config.onCancel(() => {}, ...args.slice(1));
|
||||
}
|
||||
for (let i = 0; i < destroyFns.length; i++) {
|
||||
const fn = destroyFns[i];
|
||||
|
@ -1,136 +0,0 @@
|
||||
@dialog-prefix-cls: ~'@{ant-prefix}-modal';
|
||||
|
||||
.@{dialog-prefix-cls} {
|
||||
.reset-component();
|
||||
.modal-mask();
|
||||
|
||||
position: relative;
|
||||
top: 100px;
|
||||
width: auto;
|
||||
max-width: calc(100vw - 32px);
|
||||
margin: 0 auto;
|
||||
padding-bottom: 24px;
|
||||
|
||||
&-wrap {
|
||||
z-index: @zindex-modal;
|
||||
}
|
||||
|
||||
&-title {
|
||||
margin: 0;
|
||||
color: @modal-heading-color;
|
||||
font-weight: 500;
|
||||
font-size: @modal-header-title-font-size;
|
||||
line-height: @modal-header-title-line-height;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
&-content {
|
||||
position: relative;
|
||||
background-color: @modal-content-bg;
|
||||
background-clip: padding-box;
|
||||
border: 0;
|
||||
border-radius: @modal-border-radius;
|
||||
box-shadow: @shadow-2;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
&-close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: @zindex-popup-close;
|
||||
padding: 0;
|
||||
color: @modal-close-color;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
|
||||
&-x {
|
||||
display: block;
|
||||
width: @modal-header-close-size;
|
||||
height: @modal-header-close-size;
|
||||
font-size: @font-size-lg;
|
||||
font-style: normal;
|
||||
line-height: @modal-header-close-size;
|
||||
text-align: center;
|
||||
text-transform: none;
|
||||
text-rendering: auto;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: @icon-color-hover;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-header {
|
||||
padding: @modal-header-padding;
|
||||
color: @text-color;
|
||||
background: @modal-header-bg;
|
||||
border-bottom: @modal-header-border-width @modal-header-border-style
|
||||
@modal-header-border-color-split;
|
||||
border-radius: @modal-border-radius @modal-border-radius 0 0;
|
||||
}
|
||||
|
||||
&-body {
|
||||
padding: @modal-body-padding;
|
||||
font-size: @font-size-base;
|
||||
line-height: @line-height-base;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
padding: @modal-footer-padding-vertical @modal-footer-padding-horizontal;
|
||||
text-align: right;
|
||||
background: @modal-footer-bg;
|
||||
border-top: @modal-footer-border-width @modal-footer-border-style
|
||||
@modal-footer-border-color-split;
|
||||
border-radius: 0 0 @modal-border-radius @modal-border-radius;
|
||||
|
||||
.@{ant-prefix}-btn + .@{ant-prefix}-btn:not(.@{ant-prefix}-dropdown-trigger) {
|
||||
margin-bottom: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.@{dialog-prefix-cls}-centered {
|
||||
text-align: center;
|
||||
|
||||
&::before {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
content: '';
|
||||
}
|
||||
.@{dialog-prefix-cls} {
|
||||
top: 0;
|
||||
display: inline-block;
|
||||
padding-bottom: 0;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: @screen-sm-max) {
|
||||
.@{dialog-prefix-cls} {
|
||||
max-width: calc(100vw - 16px);
|
||||
margin: 8px auto;
|
||||
}
|
||||
.@{dialog-prefix-cls}-centered {
|
||||
.@{dialog-prefix-cls} {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ const HookModal: React.ForwardRefRenderFunction<HookModalRef, HookModalProps> =
|
||||
setVisible(false);
|
||||
const triggerCancel = args.some(param => param && param.triggerCancel);
|
||||
if (innerConfig.onCancel && triggerCancel) {
|
||||
innerConfig.onCancel();
|
||||
innerConfig.onCancel(() => {}, ...args.slice(1));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -25,7 +25,7 @@ exports[`Radio Group passes prefixCls down to radio 1`] = `
|
||||
</label>
|
||||
<label
|
||||
class="my-radio-wrapper"
|
||||
style="font-size:12px"
|
||||
style="font-size: 12px;"
|
||||
>
|
||||
<span
|
||||
class="my-radio"
|
||||
|
@ -29,6 +29,7 @@ exports[`Radio Button should render correctly 1`] = `
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
@ -65,7 +66,7 @@ exports[`Radio Group passes prefixCls down to radio 1`] = `
|
||||
</label>
|
||||
<label
|
||||
class="my-radio-wrapper"
|
||||
style="font-size:12px"
|
||||
style="font-size: 12px;"
|
||||
>
|
||||
<span
|
||||
class="my-radio"
|
||||
|
@ -54,6 +54,7 @@ exports[`Radio should render correctly 1`] = `
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { mount, render } from 'enzyme';
|
||||
import { fireEvent, render as testLibRender } from '@testing-library/react';
|
||||
import Radio from '..';
|
||||
|
||||
import { render, fireEvent } from '../../../tests/utils';
|
||||
|
||||
describe('Radio Group', () => {
|
||||
function createRadioGroup(props) {
|
||||
return (
|
||||
@ -28,32 +28,37 @@ describe('Radio Group', () => {
|
||||
const onMouseEnter = jest.fn();
|
||||
const onMouseLeave = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Radio.Group onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||
<Radio />
|
||||
</Radio.Group>,
|
||||
);
|
||||
|
||||
wrapper.find('div').at(0).simulate('mouseenter');
|
||||
fireEvent.mouseEnter(container.querySelector('div'));
|
||||
expect(onMouseEnter).toHaveBeenCalled();
|
||||
|
||||
wrapper.find('div').at(0).simulate('mouseleave');
|
||||
fireEvent.mouseLeave(container.querySelector('div'));
|
||||
expect(onMouseLeave).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fire change events when value changes', () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container, rerender } = render(
|
||||
createRadioGroup({
|
||||
onChange,
|
||||
}),
|
||||
);
|
||||
const radios = wrapper.find('input');
|
||||
const radios = container.querySelectorAll('input');
|
||||
|
||||
// controlled component
|
||||
wrapper.setProps({ value: 'A' });
|
||||
radios.at(1).simulate('change');
|
||||
rerender(
|
||||
createRadioGroup({
|
||||
onChange,
|
||||
value: 'A',
|
||||
}),
|
||||
);
|
||||
fireEvent.click(radios[1]);
|
||||
expect(onChange.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
@ -61,83 +66,95 @@ describe('Radio Group', () => {
|
||||
const onChange = jest.fn();
|
||||
const onChangeRadioGroup = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
<Radio.Group onChange={onChangeRadioGroup}>
|
||||
<Radio value="A" onChange={onChange}>
|
||||
const RadioGroup = props => (
|
||||
<Radio.Group onChange={props.onChangeRadioGroup}>
|
||||
<Radio value="A" onChange={props.onChange}>
|
||||
A
|
||||
</Radio>
|
||||
<Radio value="B" onChange={onChange}>
|
||||
<Radio value="B" onChange={props.onChange}>
|
||||
B
|
||||
</Radio>
|
||||
<Radio value="C" onChange={onChange}>
|
||||
<Radio value="C" onChange={props.onChange}>
|
||||
C
|
||||
</Radio>
|
||||
</Radio.Group>,
|
||||
</Radio.Group>
|
||||
);
|
||||
const radios = wrapper.find('input');
|
||||
|
||||
const { container, rerender } = render(
|
||||
<RadioGroup onChangeRadioGroup={onChangeRadioGroup} onChange={onChange} />,
|
||||
);
|
||||
const radios = container.querySelectorAll('input');
|
||||
|
||||
// controlled component
|
||||
wrapper.setProps({ value: 'A' });
|
||||
radios.at(1).simulate('change');
|
||||
rerender(<RadioGroup value="A" onChangeRadioGroup={onChangeRadioGroup} onChange={onChange} />);
|
||||
fireEvent.click(radios[1]);
|
||||
expect(onChange.mock.calls.length).toBe(1);
|
||||
expect(onChangeRadioGroup.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('Trigger onChange when both of radioButton and radioGroup exists', () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
<Radio.Group onChange={onChange}>
|
||||
const RadioGroup = props => (
|
||||
<Radio.Group {...props}>
|
||||
<Radio.Button value="A">A</Radio.Button>
|
||||
<Radio.Button value="B">B</Radio.Button>
|
||||
<Radio.Button value="C">C</Radio.Button>
|
||||
</Radio.Group>,
|
||||
</Radio.Group>
|
||||
);
|
||||
const radios = wrapper.find('input');
|
||||
|
||||
const { container, rerender } = render(<RadioGroup onChange={onChange} />);
|
||||
const radios = container.querySelectorAll('input');
|
||||
|
||||
// controlled component
|
||||
wrapper.setProps({ value: 'A' });
|
||||
radios.at(1).simulate('change');
|
||||
rerender(<RadioGroup value="A" onChange={onChange} />);
|
||||
fireEvent.click(radios[1]);
|
||||
expect(onChange.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should only trigger once when in group with options', () => {
|
||||
const onChange = jest.fn();
|
||||
const options = [{ label: 'Bamboo', value: 'Bamboo' }];
|
||||
const wrapper = mount(<Radio.Group options={options} onChange={onChange} />);
|
||||
const { container } = render(<Radio.Group options={options} onChange={onChange} />);
|
||||
|
||||
wrapper.find('input').simulate('change');
|
||||
fireEvent.click(container.querySelector('input'));
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("won't fire change events when value not changes", () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container, rerender } = render(
|
||||
createRadioGroup({
|
||||
onChange,
|
||||
}),
|
||||
);
|
||||
const radios = wrapper.find('input');
|
||||
const radios = container.querySelectorAll('input');
|
||||
|
||||
// controlled component
|
||||
wrapper.setProps({ value: 'A' });
|
||||
radios.at(0).simulate('change');
|
||||
rerender(
|
||||
createRadioGroup({
|
||||
onChange,
|
||||
value: 'A',
|
||||
}),
|
||||
);
|
||||
fireEvent.click(radios[0]);
|
||||
expect(onChange.mock.calls.length).toBe(0);
|
||||
});
|
||||
|
||||
it('optional should correct render', () => {
|
||||
const wrapper = mount(createRadioGroupByOption());
|
||||
const radios = wrapper.find('input');
|
||||
const { container } = render(createRadioGroupByOption());
|
||||
const radios = container.querySelectorAll('input');
|
||||
|
||||
expect(radios.length).toBe(3);
|
||||
});
|
||||
|
||||
it('all children should have a name property', () => {
|
||||
const GROUP_NAME = 'radiogroup';
|
||||
const wrapper = mount(createRadioGroup({ name: GROUP_NAME }));
|
||||
const GROUP_NAME = 'GROUP_NAME';
|
||||
const { container } = render(createRadioGroup({ name: GROUP_NAME }));
|
||||
|
||||
wrapper.find('input[type="radio"]').forEach(el => {
|
||||
expect(el.props().name).toEqual(GROUP_NAME);
|
||||
container.querySelectorAll('input[type="radio"]').forEach(el => {
|
||||
expect(el.name).toEqual(GROUP_NAME);
|
||||
});
|
||||
});
|
||||
|
||||
@ -146,13 +163,13 @@ describe('Radio Group', () => {
|
||||
{ label: 'Apple', value: 'Apple' },
|
||||
{ label: 'Orange', value: 'Orange', style: { fontSize: 12 } },
|
||||
];
|
||||
const wrapper = render(<Radio.Group prefixCls="my-radio" options={options} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const { container } = render(<Radio.Group prefixCls="my-radio" options={options} />);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should forward ref', () => {
|
||||
let radioGroupRef;
|
||||
const { container } = testLibRender(
|
||||
const { container } = render(
|
||||
createRadioGroupByOption({
|
||||
ref: ref => {
|
||||
radioGroupRef = ref;
|
||||
@ -164,7 +181,7 @@ describe('Radio Group', () => {
|
||||
});
|
||||
|
||||
it('should support data-* or aria-* props', () => {
|
||||
const { container } = testLibRender(
|
||||
const { container } = render(
|
||||
createRadioGroup({
|
||||
'data-radio-group-id': 'radio-group-id',
|
||||
'aria-label': 'radio-group',
|
||||
@ -176,7 +193,7 @@ describe('Radio Group', () => {
|
||||
|
||||
it('Radio type should not be override', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Radio.Group onChange={onChange}>
|
||||
<Radio value={1} type="1">
|
||||
A
|
||||
@ -192,35 +209,28 @@ describe('Radio Group', () => {
|
||||
</Radio>
|
||||
</Radio.Group>,
|
||||
);
|
||||
const radios = wrapper.find('input');
|
||||
radios.at(1).simulate('change');
|
||||
const radios = container.querySelectorAll('input');
|
||||
fireEvent.click(radios[0]);
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
expect(radios.at(1).getDOMNode().type).toBe('radio');
|
||||
expect(radios[1].type).toBe('radio');
|
||||
});
|
||||
|
||||
describe('value is null or undefined', () => {
|
||||
it('use `defaultValue` when `value` is undefined', () => {
|
||||
const options = [{ label: 'Bamboo', value: 'bamboo' }];
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Radio.Group defaultValue="bamboo" value={undefined} options={options} />,
|
||||
);
|
||||
expect(wrapper.find('.ant-radio-wrapper').at(0).hasClass('ant-radio-wrapper-checked')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-radio-wrapper-checked').length).toBe(1);
|
||||
});
|
||||
|
||||
[undefined, null].forEach(newValue => {
|
||||
it(`should set value back when value change back to ${newValue}`, () => {
|
||||
const options = [{ label: 'Bamboo', value: 'bamboo' }];
|
||||
const wrapper = mount(<Radio.Group value="bamboo" options={options} />);
|
||||
expect(wrapper.find('.ant-radio-wrapper').at(0).hasClass('ant-radio-wrapper-checked')).toBe(
|
||||
true,
|
||||
);
|
||||
wrapper.setProps({ value: newValue });
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-radio-wrapper').at(0).hasClass('ant-radio-wrapper-checked')).toBe(
|
||||
false,
|
||||
);
|
||||
const { container, rerender } = render(<Radio.Group value="bamboo" options={options} />);
|
||||
expect(container.querySelectorAll('.ant-radio-wrapper-checked').length).toBe(1);
|
||||
rerender(<Radio.Group value={newValue} options={options} />);
|
||||
expect(container.querySelectorAll('.ant-radio-wrapper-checked').length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -228,7 +238,7 @@ describe('Radio Group', () => {
|
||||
it('onBlur & onFocus should work', () => {
|
||||
const handleBlur = jest.fn();
|
||||
const handleFocus = jest.fn();
|
||||
const { container } = testLibRender(
|
||||
const { container } = render(
|
||||
<Radio.Group options={['1', '2', '3']} onBlur={handleBlur} onFocus={handleFocus} />,
|
||||
);
|
||||
fireEvent.focus(container.firstChild);
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { render as testLibRender } from '@testing-library/react';
|
||||
import { mount, render } from 'enzyme';
|
||||
import React from 'react';
|
||||
import Radio, { Button } from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
|
||||
import { render, fireEvent } from '../../../tests/utils';
|
||||
|
||||
describe('Radio Button', () => {
|
||||
focusTest(Button, { refFocus: true });
|
||||
mountTest(Button);
|
||||
@ -13,20 +13,22 @@ describe('Radio Button', () => {
|
||||
rtlTest(Button);
|
||||
|
||||
it('should render correctly', () => {
|
||||
const wrapper = render(<Button className="customized">Test</Button>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const { container } = render(<Button className="customized">Test</Button>);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('responses hover events', () => {
|
||||
const onMouseEnter = jest.fn();
|
||||
const onMouseLeave = jest.fn();
|
||||
|
||||
const wrapper = mount(<Button onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />);
|
||||
const { container } = render(
|
||||
<Button onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />,
|
||||
);
|
||||
|
||||
wrapper.find('label').simulate('mouseenter');
|
||||
fireEvent.mouseEnter(container.querySelector('label'));
|
||||
expect(onMouseEnter).toHaveBeenCalled();
|
||||
|
||||
wrapper.find('label').simulate('mouseleave');
|
||||
fireEvent.mouseLeave(container.querySelector('label'));
|
||||
expect(onMouseLeave).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -46,32 +48,29 @@ describe('Radio Group', () => {
|
||||
const onMouseEnter = jest.fn();
|
||||
const onMouseLeave = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Radio.Group onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||
<Radio />
|
||||
</Radio.Group>,
|
||||
);
|
||||
|
||||
wrapper.find('div').at(0).simulate('mouseenter');
|
||||
fireEvent.mouseEnter(container.querySelectorAll('div')[0]);
|
||||
expect(onMouseEnter).toHaveBeenCalled();
|
||||
|
||||
wrapper.find('div').at(0).simulate('mouseleave');
|
||||
fireEvent.mouseLeave(container.querySelectorAll('div')[0]);
|
||||
expect(onMouseLeave).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fire change events when value changes', () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
createRadioGroup({
|
||||
onChange,
|
||||
}),
|
||||
);
|
||||
const radios = wrapper.find('input');
|
||||
const { container, rerender } = render(createRadioGroup({ onChange }));
|
||||
|
||||
const radios = container.querySelectorAll('input');
|
||||
|
||||
// controlled component
|
||||
wrapper.setProps({ value: 'A' });
|
||||
radios.at(1).simulate('change');
|
||||
rerender(createRadioGroup({ onChange, value: 'A' }));
|
||||
fireEvent.click(radios[1]);
|
||||
expect(onChange.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
@ -79,7 +78,7 @@ describe('Radio Group', () => {
|
||||
const onChange = jest.fn();
|
||||
const onChangeRadioGroup = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Radio.Group onChange={onChangeRadioGroup}>
|
||||
<Radio value="A" onChange={onChange}>
|
||||
A
|
||||
@ -92,63 +91,69 @@ describe('Radio Group', () => {
|
||||
</Radio>
|
||||
</Radio.Group>,
|
||||
);
|
||||
const radios = wrapper.find('input');
|
||||
const radios = container.querySelectorAll('input');
|
||||
|
||||
// controlled component
|
||||
wrapper.setProps({ value: 'A' });
|
||||
radios.at(1).simulate('change');
|
||||
fireEvent.click(radios[1]);
|
||||
expect(onChange.mock.calls.length).toBe(1);
|
||||
expect(onChangeRadioGroup.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('Trigger onChange when both of Button and radioGroup exists', () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container, rerender } = render(
|
||||
<Radio.Group onChange={onChange}>
|
||||
<Button value="A">A</Button>
|
||||
<Button value="B">B</Button>
|
||||
<Button value="C">C</Button>
|
||||
</Radio.Group>,
|
||||
);
|
||||
const radios = wrapper.find('input');
|
||||
const radios = container.querySelectorAll('input');
|
||||
|
||||
// controlled component
|
||||
wrapper.setProps({ value: 'A' });
|
||||
radios.at(1).simulate('change');
|
||||
rerender(
|
||||
<Radio.Group value="A" onChange={onChange}>
|
||||
<Button value="A">A</Button>
|
||||
<Button value="B">B</Button>
|
||||
<Button value="C">C</Button>
|
||||
</Radio.Group>,
|
||||
);
|
||||
fireEvent.click(radios[1]);
|
||||
expect(onChange.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should only trigger once when in group with options', () => {
|
||||
const onChange = jest.fn();
|
||||
const options = [{ label: 'Bamboo', value: 'Bamboo' }];
|
||||
const wrapper = mount(<Radio.Group options={options} onChange={onChange} />);
|
||||
const { container } = render(<Radio.Group options={options} onChange={onChange} />);
|
||||
|
||||
wrapper.find('input').simulate('change');
|
||||
fireEvent.click(container.querySelector('input'));
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("won't fire change events when value not changes", () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container, rerender } = render(
|
||||
createRadioGroup({
|
||||
onChange,
|
||||
}),
|
||||
);
|
||||
const radios = wrapper.find('input');
|
||||
const radios = container.querySelectorAll('input');
|
||||
|
||||
// controlled component
|
||||
wrapper.setProps({ value: 'A' });
|
||||
radios.at(0).simulate('change');
|
||||
rerender(createRadioGroup({ onChange, value: 'A' }));
|
||||
fireEvent.click(radios[0]);
|
||||
expect(onChange.mock.calls.length).toBe(0);
|
||||
});
|
||||
|
||||
it('all children should have a name property', () => {
|
||||
const GROUP_NAME = 'radiogroup';
|
||||
const wrapper = mount(createRadioGroup({ name: GROUP_NAME }));
|
||||
const GROUP_NAME = 'GROUP_NAME';
|
||||
const { container } = render(createRadioGroup({ name: GROUP_NAME }));
|
||||
|
||||
wrapper.find('input[type="radio"]').forEach(el => {
|
||||
expect(el.props().name).toEqual(GROUP_NAME);
|
||||
container.querySelectorAll('input[type="radio"]').forEach(el => {
|
||||
expect(el.name).toEqual(GROUP_NAME);
|
||||
});
|
||||
});
|
||||
|
||||
@ -157,13 +162,13 @@ describe('Radio Group', () => {
|
||||
{ label: 'Apple', value: 'Apple' },
|
||||
{ label: 'Orange', value: 'Orange', style: { fontSize: 12 } },
|
||||
];
|
||||
const wrapper = render(<Radio.Group prefixCls="my-radio" options={options} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const { container } = render(<Radio.Group prefixCls="my-radio" options={options} />);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should forward ref', () => {
|
||||
let radioGroupRef;
|
||||
const { container } = testLibRender(
|
||||
const { container } = render(
|
||||
createRadioGroup({
|
||||
ref: ref => {
|
||||
radioGroupRef = ref;
|
||||
@ -175,7 +180,7 @@ describe('Radio Group', () => {
|
||||
});
|
||||
|
||||
it('should support data-* or aria-* props', () => {
|
||||
const { container } = testLibRender(
|
||||
const { container } = render(
|
||||
createRadioGroup({
|
||||
'data-radio-group-id': 'radio-group-id',
|
||||
'aria-label': 'radio-group',
|
||||
@ -187,7 +192,7 @@ describe('Radio Group', () => {
|
||||
|
||||
it('Radio type should not be override', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Radio.Group onChange={onChange}>
|
||||
<Radio value={1} type="1">
|
||||
A
|
||||
@ -203,48 +208,36 @@ describe('Radio Group', () => {
|
||||
</Radio>
|
||||
</Radio.Group>,
|
||||
);
|
||||
const radios = wrapper.find('input');
|
||||
radios.at(1).simulate('change');
|
||||
const radios = container.querySelectorAll('input');
|
||||
fireEvent.click(radios[0]);
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
expect(radios.at(1).getDOMNode().type).toBe('radio');
|
||||
expect(radios[1].type).toBe('radio');
|
||||
});
|
||||
|
||||
describe('value is null or undefined', () => {
|
||||
it('use `defaultValue` when `value` is undefined', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Radio.Group defaultValue="bamboo" value={undefined}>
|
||||
<Button value="bamboo">Bamboo</Button>
|
||||
</Radio.Group>,
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-radio-button-wrapper')
|
||||
.at(0)
|
||||
.hasClass('ant-radio-button-wrapper-checked'),
|
||||
).toBe(true);
|
||||
expect(container.querySelectorAll('.ant-radio-button-wrapper-checked').length).toBe(1);
|
||||
});
|
||||
|
||||
[undefined, null].forEach(newValue => {
|
||||
it(`should set value back when value change back to ${newValue}`, () => {
|
||||
const wrapper = mount(
|
||||
const { container, rerender } = render(
|
||||
<Radio.Group value="bamboo">
|
||||
<Button value="bamboo">Bamboo</Button>
|
||||
</Radio.Group>,
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-radio-button-wrapper')
|
||||
.at(0)
|
||||
.hasClass('ant-radio-button-wrapper-checked'),
|
||||
).toBe(true);
|
||||
wrapper.setProps({ value: newValue });
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-radio-button-wrapper')
|
||||
.at(0)
|
||||
.hasClass('ant-radio-button-wrapper-checked'),
|
||||
).toBe(false);
|
||||
expect(container.querySelectorAll('.ant-radio-button-wrapper-checked').length).toBe(1);
|
||||
rerender(
|
||||
<Radio.Group value={newValue}>
|
||||
<Button value="bamboo">Bamboo</Button>
|
||||
</Radio.Group>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-radio-button-wrapper-checked').length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { mount, render } from 'enzyme';
|
||||
import React from 'react';
|
||||
import Radio, { Button, Group } from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
|
||||
import { render, fireEvent } from '../../../tests/utils';
|
||||
|
||||
describe('Radio', () => {
|
||||
focusTest(Radio, { refFocus: true });
|
||||
mountTest(Radio);
|
||||
@ -16,20 +17,20 @@ describe('Radio', () => {
|
||||
rtlTest(Button);
|
||||
|
||||
it('should render correctly', () => {
|
||||
const wrapper = render(<Radio className="customized">Test</Radio>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const { container } = render(<Radio className="customized">Test</Radio>);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('responses hover events', () => {
|
||||
const onMouseEnter = jest.fn();
|
||||
const onMouseLeave = jest.fn();
|
||||
|
||||
const wrapper = mount(<Radio onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />);
|
||||
const { container } = render(<Radio onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />);
|
||||
|
||||
wrapper.find('label').simulate('mouseenter');
|
||||
fireEvent.mouseEnter(container.querySelector('label'));
|
||||
expect(onMouseEnter).toHaveBeenCalled();
|
||||
|
||||
wrapper.find('label').simulate('mouseleave');
|
||||
fireEvent.mouseLeave(container.querySelector('label'));
|
||||
expect(onMouseLeave).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -1665,9 +1665,9 @@ exports[`renders ./components/select/demo/custom-dropdown-menu.md extend context
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<a
|
||||
class="ant-typography"
|
||||
style="white-space:nowrap"
|
||||
<button
|
||||
class="ant-btn ant-btn-text"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="plus"
|
||||
@ -1692,8 +1692,10 @@ exports[`renders ./components/select/demo/custom-dropdown-menu.md extend context
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Add item
|
||||
</a>
|
||||
<span>
|
||||
Add item
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,16 +7,17 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `dropdownRender` 对下拉菜单进行自由扩展。自定义内容点击时会关闭浮层,如果不喜欢关闭,可以添加 `onMouseDown={e => e.preventDefault()}` 进行阻止(更多详情见 [#13448](https://github.com/ant-design/ant-design/issues/13448))。
|
||||
使用 `open` 对下拉菜单进行自由扩展。如果希望点击自定义内容后关闭浮层,你需要使用受控模式自行控制([codesandbox](https://codesandbox.io/s/ji-ben-shi-yong-antd-4-21-7-forked-gnp4cy?file=/demo.js))。
|
||||
|
||||
## en-US
|
||||
|
||||
Customize the dropdown menu via `dropdownRender`. Dropdown menu will be closed if click `dropdownRender` area, you can prevent it by wrapping `onMouseDown={e => e.preventDefault()}` (see more at [#13448](https://github.com/ant-design/ant-design/issues/13448)).
|
||||
Customize the dropdown menu via `dropdownRender`. If you want to close the dropdown after clicking the custom content, you need to control `open` prop, here is an [codesandbox](https://codesandbox.io/s/ji-ben-shi-yong-antd-4-21-7-forked-gnp4cy?file=/demo.js).
|
||||
|
||||
```tsx
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Divider, Input, Select, Space, Typography } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { Divider, Input, Select, Space, Button } from 'antd';
|
||||
import type { InputRef } from 'antd';
|
||||
import React, { useState, useRef } from 'react';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@ -25,6 +26,7 @@ let index = 0;
|
||||
const App: React.FC = () => {
|
||||
const [items, setItems] = useState(['jack', 'lucy']);
|
||||
const [name, setName] = useState('');
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
|
||||
const onNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setName(event.target.value);
|
||||
@ -34,6 +36,9 @@ const App: React.FC = () => {
|
||||
e.preventDefault();
|
||||
setItems([...items, name || `New item ${index++}`]);
|
||||
setName('');
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus();
|
||||
}, 0);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -44,11 +49,16 @@ const App: React.FC = () => {
|
||||
<>
|
||||
{menu}
|
||||
<Divider style={{ margin: '8px 0' }} />
|
||||
<Space align="center" style={{ padding: '0 8px 4px' }}>
|
||||
<Input placeholder="Please enter item" value={name} onChange={onNameChange} />
|
||||
<Typography.Link onClick={addItem} style={{ whiteSpace: 'nowrap' }}>
|
||||
<PlusOutlined /> Add item
|
||||
</Typography.Link>
|
||||
<Space style={{ padding: '0 8px 4px' }}>
|
||||
<Input
|
||||
placeholder="Please enter item"
|
||||
ref={inputRef}
|
||||
value={name}
|
||||
onChange={onNameChange}
|
||||
/>
|
||||
<Button type="text" icon={<PlusOutlined />} onClick={addItem}>
|
||||
Add item
|
||||
</Button>
|
||||
</Space>
|
||||
</>
|
||||
)}
|
||||
|
@ -111,15 +111,15 @@ Select component to select value from options.
|
||||
|
||||
It's caused by option with different `label` and `value`. You can use `optionFilterProp="label"` to change filter logic instead.
|
||||
|
||||
### The dropdown is closed when click `dropdownRender` area?
|
||||
### When I click elements in dropdownRender, the select dropdown will not be closed?
|
||||
|
||||
Dropdown menu will be closed if click `dropdownRender` area, you can prevent it by wrapping `onMouseDown={e => e.preventDefault()}` (see more at [#13448](https://github.com/ant-design/ant-design/issues/13448)).
|
||||
You can control it by `open` prop: [codesandbox](https://codesandbox.io/s/ji-ben-shi-yong-antd-4-21-7-forked-gnp4cy?file=/demo.js).
|
||||
|
||||
### Why sometime customize Option cause scroll break?
|
||||
|
||||
Virtual scroll internal set item height as `32px`. You need to adjust `listItemHeight` when your option height is less and `listHeight` config list container height:
|
||||
|
||||
```tsx
|
||||
```jsx
|
||||
<Select listItemHeight={10} listHeight={250} />
|
||||
```
|
||||
|
||||
|
@ -112,9 +112,9 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
|
||||
|
||||
这一般是 `options` 中的 `label` 和 `value` 不同导致的,你可以通过 `optionFilterProp="label"` 将过滤设置为展示值以避免这种情况。
|
||||
|
||||
### 点击 `dropdownRender` 里的内容浮层关闭怎么办?
|
||||
### 点击 `dropdownRender` 里的元素,下拉菜单不会自动消失?
|
||||
|
||||
自定义内容点击时会关闭浮层,如果不喜欢关闭,可以添加 `onMouseDown={e => e.preventDefault()}` 进行阻止(更多详情见 [#13448](https://github.com/ant-design/ant-design/issues/13448))。
|
||||
你可以使用受控模式,手动设置 `open` 属性:[codesandbox](https://codesandbox.io/s/ji-ben-shi-yong-antd-4-21-7-forked-gnp4cy?file=/demo.js)。
|
||||
|
||||
### 自定义 Option 样式导致滚动异常怎么办?
|
||||
|
||||
|
@ -148,42 +148,6 @@ exports[`renders ./components/space/demo/align.md extend context correctly 1`] =
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="space-align-block"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-stretch"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
stretch
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Primary
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span
|
||||
class="mock-block"
|
||||
>
|
||||
Block
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -148,42 +148,6 @@ exports[`renders ./components/space/demo/align.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="space-align-block"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-stretch"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
stretch
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Primary
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span
|
||||
class="mock-block"
|
||||
>
|
||||
Block
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -47,13 +47,6 @@ const App: React.FC = () => (
|
||||
<span className="mock-block">Block</span>
|
||||
</Space>
|
||||
</div>
|
||||
<div className="space-align-block">
|
||||
<Space align="stretch">
|
||||
stretch
|
||||
<Button type="primary">Primary</Button>
|
||||
<span className="mock-block">Block</span>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -16,7 +16,7 @@ Avoid components clinging together and set a unified space.
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| align | Align items | `start` \| `end` \|`center` \|`baseline` \|`stretch` | - | 4.2.0 |
|
||||
| align | Align items | `start` \| `end` \|`center` \|`baseline` | - | 4.2.0 |
|
||||
| direction | The space direction | `vertical` \| `horizontal` | `horizontal` | 4.1.0 |
|
||||
| size | The space size | [Size](#Size) \| [Size\[\]](#Size) | `small` | 4.1.0 \| Array: 4.9.0 |
|
||||
| split | Set split | ReactNode | - | 4.7.0 |
|
||||
|
@ -24,7 +24,7 @@ export interface SpaceProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
size?: SpaceSize | [SpaceSize, SpaceSize];
|
||||
direction?: 'horizontal' | 'vertical';
|
||||
// No `stretch` since many components do not support that.
|
||||
align?: 'start' | 'end' | 'center' | 'baseline' | 'stretch';
|
||||
align?: 'start' | 'end' | 'center' | 'baseline';
|
||||
split?: React.ReactNode;
|
||||
wrap?: boolean;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/wc6%263gJ0Y8/Space.svg
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| align | 对齐方式 | `start` \| `end` \|`center` \|`baseline` \|`stretch` | - | 4.2.0 |
|
||||
| align | 对齐方式 | `start` \| `end` \|`center` \|`baseline` | - | 4.2.0 |
|
||||
| direction | 间距方向 | `vertical` \| `horizontal` | `horizontal` | 4.1.0 |
|
||||
| size | 间距大小 | [Size](#Size) \| [Size\[\]](#Size) | `small` | 4.1.0 \| Array: 4.9.0 |
|
||||
| split | 设置拆分 | ReactNode | - | 4.7.0 |
|
||||
|
@ -36,9 +36,6 @@ const genSpaceStyle: GenerateStyle<SpaceToken> = token => {
|
||||
'&-baseline': {
|
||||
alignItems: 'flex-baseline',
|
||||
},
|
||||
'&-stretch': {
|
||||
alignItems: 'stretch',
|
||||
},
|
||||
},
|
||||
[`${componentCls}-space-item`]: {
|
||||
'&:empty': {
|
||||
|
@ -26636,6 +26636,709 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/table/demo/tree-table-ellipsis.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
style="margin-bottom:16px"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
Fixed first column:
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
aria-checked="true"
|
||||
class="ant-switch ant-switch-checked"
|
||||
role="switch"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="ant-switch-handle"
|
||||
/>
|
||||
<span
|
||||
class="ant-switch-inner"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-table-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-spin-nested-loading"
|
||||
>
|
||||
<div
|
||||
class="ant-spin-container"
|
||||
>
|
||||
<div
|
||||
class="ant-table ant-table-has-fix-left"
|
||||
>
|
||||
<div
|
||||
class="ant-table-container"
|
||||
>
|
||||
<div
|
||||
class="ant-table-content"
|
||||
>
|
||||
<table
|
||||
style="table-layout:fixed"
|
||||
>
|
||||
<colgroup>
|
||||
<col
|
||||
class="ant-table-selection-col"
|
||||
style="width:100px"
|
||||
/>
|
||||
<col
|
||||
style="width:30%"
|
||||
/>
|
||||
<col
|
||||
style="width:12%"
|
||||
/>
|
||||
</colgroup>
|
||||
<thead
|
||||
class="ant-table-thead"
|
||||
>
|
||||
<tr>
|
||||
<th
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<div
|
||||
class="ant-table-selection"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis"
|
||||
style="position:sticky;left:0"
|
||||
title="Name"
|
||||
>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Name
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
>
|
||||
Age
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
>
|
||||
Address
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
class="ant-table-tbody"
|
||||
>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-0"
|
||||
data-row-key="1"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-0"
|
||||
style="padding-left:0px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
60
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
New York No. 1 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-1"
|
||||
data-row-key="11"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-1"
|
||||
style="padding-left:15px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
42
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
New York No. 2 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-1"
|
||||
data-row-key="12"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-1"
|
||||
style="padding-left:15px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
30
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
New York No. 3 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-2"
|
||||
data-row-key="121"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-2"
|
||||
style="padding-left:30px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
16
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
New York No. 3 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-1"
|
||||
data-row-key="13"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="Jim Green sr. Jim Green sr. Jim Green sr. Jim Green sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-1"
|
||||
style="padding-left:15px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Jim Green sr. Jim Green sr. Jim Green sr. Jim Green sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
72
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
London No. 1 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-2"
|
||||
data-row-key="131"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="Jim Green. Jim Green. Jim Green. Jim Green. Jim Green. Jim Green."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-2"
|
||||
style="padding-left:30px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Jim Green. Jim Green. Jim Green. Jim Green. Jim Green. Jim Green.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
42
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
London No. 2 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-3"
|
||||
data-row-key="1311"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="Jim Green jr. Jim Green jr. Jim Green jr. Jim Green jr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-3"
|
||||
style="padding-left:45px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Jim Green jr. Jim Green jr. Jim Green jr. Jim Green jr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
25
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
London No. 3 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-3"
|
||||
data-row-key="1312"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="Jimmy Green sr. Jimmy Green sr. Jimmy Green sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-3"
|
||||
style="padding-left:45px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Jimmy Green sr. Jimmy Green sr. Jimmy Green sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
18
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
London No. 4 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-0"
|
||||
data-row-key="2"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="Joe Black"
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-0"
|
||||
style="padding-left:0px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Joe Black
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
32
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
Sidney No. 1 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
class="ant-pagination ant-table-pagination ant-table-pagination-right"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
<li
|
||||
aria-disabled="true"
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
class="anticon anticon-left"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="left"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
tabindex="0"
|
||||
title="1"
|
||||
>
|
||||
<a
|
||||
rel="nofollow"
|
||||
>
|
||||
1
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
aria-disabled="true"
|
||||
class="ant-pagination-next ant-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/table/demo/virtual-list.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-table-wrapper virtual-table"
|
||||
|
@ -20815,6 +20815,709 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/table/demo/tree-table-ellipsis.md correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
style="margin-bottom:16px"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
Fixed first column:
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
aria-checked="true"
|
||||
class="ant-switch ant-switch-checked"
|
||||
role="switch"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="ant-switch-handle"
|
||||
/>
|
||||
<span
|
||||
class="ant-switch-inner"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-table-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-spin-nested-loading"
|
||||
>
|
||||
<div
|
||||
class="ant-spin-container"
|
||||
>
|
||||
<div
|
||||
class="ant-table ant-table-has-fix-left"
|
||||
>
|
||||
<div
|
||||
class="ant-table-container"
|
||||
>
|
||||
<div
|
||||
class="ant-table-content"
|
||||
>
|
||||
<table
|
||||
style="table-layout:fixed"
|
||||
>
|
||||
<colgroup>
|
||||
<col
|
||||
class="ant-table-selection-col"
|
||||
style="width:100px"
|
||||
/>
|
||||
<col
|
||||
style="width:30%"
|
||||
/>
|
||||
<col
|
||||
style="width:12%"
|
||||
/>
|
||||
</colgroup>
|
||||
<thead
|
||||
class="ant-table-thead"
|
||||
>
|
||||
<tr>
|
||||
<th
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<div
|
||||
class="ant-table-selection"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis"
|
||||
style="position:sticky;left:0"
|
||||
title="Name"
|
||||
>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Name
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
>
|
||||
Age
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
>
|
||||
Address
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
class="ant-table-tbody"
|
||||
>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-0"
|
||||
data-row-key="1"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-0"
|
||||
style="padding-left:0px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
60
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
New York No. 1 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-1"
|
||||
data-row-key="11"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-1"
|
||||
style="padding-left:15px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
42
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
New York No. 2 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-1"
|
||||
data-row-key="12"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-1"
|
||||
style="padding-left:15px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
30
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
New York No. 3 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-2"
|
||||
data-row-key="121"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-2"
|
||||
style="padding-left:30px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
16
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
New York No. 3 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-1"
|
||||
data-row-key="13"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="Jim Green sr. Jim Green sr. Jim Green sr. Jim Green sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-1"
|
||||
style="padding-left:15px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Jim Green sr. Jim Green sr. Jim Green sr. Jim Green sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
72
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
London No. 1 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-2"
|
||||
data-row-key="131"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="Jim Green. Jim Green. Jim Green. Jim Green. Jim Green. Jim Green."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-2"
|
||||
style="padding-left:30px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Jim Green. Jim Green. Jim Green. Jim Green. Jim Green. Jim Green.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
42
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
London No. 2 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-3"
|
||||
data-row-key="1311"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="Jim Green jr. Jim Green jr. Jim Green jr. Jim Green jr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-3"
|
||||
style="padding-left:45px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Jim Green jr. Jim Green jr. Jim Green jr. Jim Green jr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
25
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
London No. 3 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-3"
|
||||
data-row-key="1312"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="Jimmy Green sr. Jimmy Green sr. Jimmy Green sr."
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-3"
|
||||
style="padding-left:45px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Jimmy Green sr. Jimmy Green sr. Jimmy Green sr.
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
18
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
London No. 4 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-table-row ant-table-row-level-0"
|
||||
data-row-key="2"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
|
||||
style="position:sticky;left:0"
|
||||
title="Joe Black"
|
||||
>
|
||||
<span
|
||||
class="ant-table-row-indent indent-level-0"
|
||||
style="padding-left:0px"
|
||||
/>
|
||||
<button
|
||||
aria-label="Collapse row"
|
||||
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
|
||||
type="button"
|
||||
/>
|
||||
<span
|
||||
class="ant-table-cell-content"
|
||||
>
|
||||
Joe Black
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
32
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
>
|
||||
Sidney No. 1 Lake Park
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
class="ant-pagination ant-table-pagination ant-table-pagination-right"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
<li
|
||||
aria-disabled="true"
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
class="anticon anticon-left"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="left"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
tabindex="0"
|
||||
title="1"
|
||||
>
|
||||
<a
|
||||
rel="nofollow"
|
||||
>
|
||||
1
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
aria-disabled="true"
|
||||
class="ant-pagination-next ant-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/table/demo/virtual-list.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-table-wrapper virtual-table"
|
||||
|
136
components/table/demo/tree-table-ellipsis.md
Normal file
136
components/table/demo/tree-table-ellipsis.md
Normal file
@ -0,0 +1,136 @@
|
||||
---
|
||||
order: 17.1
|
||||
title:
|
||||
en-US: Tree data ellipsis debug demo
|
||||
zh-CN: 树形数据省略情况测试
|
||||
debug: true
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
https://github.com/ant-design/ant-design/issues/36583
|
||||
|
||||
## en-US
|
||||
|
||||
https://github.com/ant-design/ant-design/issues/36583
|
||||
|
||||
```tsx
|
||||
import { Space, Switch, Table } from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
interface DataType {
|
||||
key: React.ReactNode;
|
||||
name: string;
|
||||
age: number;
|
||||
address: string;
|
||||
children?: DataType[];
|
||||
}
|
||||
|
||||
const data: DataType[] = [
|
||||
{
|
||||
key: 1,
|
||||
name: 'John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.',
|
||||
age: 60,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
children: [
|
||||
{
|
||||
key: 11,
|
||||
name: 'John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.',
|
||||
age: 42,
|
||||
address: 'New York No. 2 Lake Park',
|
||||
},
|
||||
{
|
||||
key: 12,
|
||||
name: 'John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.',
|
||||
age: 30,
|
||||
address: 'New York No. 3 Lake Park',
|
||||
children: [
|
||||
{
|
||||
key: 121,
|
||||
name: 'John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.',
|
||||
age: 16,
|
||||
address: 'New York No. 3 Lake Park',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 13,
|
||||
name: 'Jim Green sr. Jim Green sr. Jim Green sr. Jim Green sr.',
|
||||
age: 72,
|
||||
address: 'London No. 1 Lake Park',
|
||||
children: [
|
||||
{
|
||||
key: 131,
|
||||
name: 'Jim Green. Jim Green. Jim Green. Jim Green. Jim Green. Jim Green.',
|
||||
age: 42,
|
||||
address: 'London No. 2 Lake Park',
|
||||
children: [
|
||||
{
|
||||
key: 1311,
|
||||
name: 'Jim Green jr. Jim Green jr. Jim Green jr. Jim Green jr.',
|
||||
age: 25,
|
||||
address: 'London No. 3 Lake Park',
|
||||
},
|
||||
{
|
||||
key: 1312,
|
||||
name: 'Jimmy Green sr. Jimmy Green sr. Jimmy Green sr.',
|
||||
age: 18,
|
||||
address: 'London No. 4 Lake Park',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park',
|
||||
},
|
||||
];
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [fixed, setFixed] = useState(true);
|
||||
|
||||
const columns: ColumnsType<DataType> = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '30%',
|
||||
ellipsis: true,
|
||||
fixed,
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
width: '12%',
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Space align="center" style={{ marginBottom: 16 }}>
|
||||
Fixed first column: <Switch checked={fixed} onChange={setFixed} />
|
||||
</Space>
|
||||
<Table
|
||||
columns={columns}
|
||||
rowSelection={{ columnWidth: 100 }}
|
||||
expandable={{ defaultExpandAllRows: true }}
|
||||
dataSource={data}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
```
|
@ -17,6 +17,10 @@ const genExpandStyle: GenerateStyle<TableToken, CSSObject> = token => {
|
||||
tableExpandIconBg,
|
||||
tableExpandColumnWidth,
|
||||
radiusBase,
|
||||
fontSize,
|
||||
fontSizeSM,
|
||||
lineHeight,
|
||||
lineWidth,
|
||||
tablePaddingVertical,
|
||||
tablePaddingHorizontal,
|
||||
tableExpandedRowBg,
|
||||
@ -35,6 +39,12 @@ const genExpandStyle: GenerateStyle<TableToken, CSSObject> = token => {
|
||||
|
||||
[`${componentCls}-row-expand-icon-cell`]: {
|
||||
textAlign: 'center',
|
||||
|
||||
[`${componentCls}-row-expand-icon`]: {
|
||||
display: 'inline-flex',
|
||||
float: 'none',
|
||||
verticalAlign: 'sub',
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-row-indent`]: {
|
||||
@ -44,8 +54,7 @@ const genExpandStyle: GenerateStyle<TableToken, CSSObject> = token => {
|
||||
[`${componentCls}-row-expand-icon`]: {
|
||||
...operationUnit(token),
|
||||
position: 'relative',
|
||||
display: 'inline-flex',
|
||||
verticalAlign: 'text-top',
|
||||
float: 'left',
|
||||
boxSizing: 'border-box',
|
||||
width: expandIconSize,
|
||||
height: expandIconSize,
|
||||
@ -104,15 +113,12 @@ const genExpandStyle: GenerateStyle<TableToken, CSSObject> = token => {
|
||||
border: 0,
|
||||
visibility: 'hidden',
|
||||
},
|
||||
|
||||
[`+ ${componentCls}-cell-content`]: {
|
||||
display: 'inline-block !important',
|
||||
width: `calc(100% - (${expandIconSize}px + ${paddingXS}px))`,
|
||||
verticalAlign: 'top',
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-row-indent + ${componentCls}-row-expand-icon`]: {
|
||||
marginTop:
|
||||
(fontSize * lineHeight - lineWidth * 3) / 2 -
|
||||
Math.ceil((fontSizeSM * 1.4 - lineWidth * 3) / 2),
|
||||
marginInlineEnd: paddingXS,
|
||||
},
|
||||
|
||||
|
@ -38,6 +38,10 @@ const genFixedStyle: GenerateStyle<TableToken, CSSObject> = token => {
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
|
||||
[`${componentCls}-cell-fix-left-all::after`]: {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
[`
|
||||
${componentCls}-cell-fix-right-first::after,
|
||||
${componentCls}-cell-fix-right-last::after
|
||||
|
@ -1,49 +0,0 @@
|
||||
// ================================================================
|
||||
// = Border Radius =
|
||||
// ================================================================
|
||||
.@{table-prefix-cls} {
|
||||
/* title + table */
|
||||
&-title {
|
||||
border-radius: @table-border-radius-base @table-border-radius-base 0 0;
|
||||
}
|
||||
|
||||
&-title + &-container {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
|
||||
table {
|
||||
border-radius: 0;
|
||||
|
||||
> thead > tr:first-child {
|
||||
th:first-child {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
th:last-child {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* table */
|
||||
&-container {
|
||||
border-top-left-radius: @table-border-radius-base;
|
||||
border-top-right-radius: @table-border-radius-base;
|
||||
|
||||
table > thead > tr:first-child {
|
||||
th:first-child {
|
||||
border-top-left-radius: @table-border-radius-base;
|
||||
}
|
||||
|
||||
th:last-child {
|
||||
border-top-right-radius: @table-border-radius-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* table + footer */
|
||||
&-footer {
|
||||
border-radius: 0 0 @table-border-radius-base @table-border-radius-base;
|
||||
}
|
||||
}
|
@ -15,13 +15,17 @@ const genRadiusStyle: GenerateStyle<TableToken, CSSObject> = token => {
|
||||
borderStartStartRadius: 0,
|
||||
borderStartEndRadius: 0,
|
||||
|
||||
'table > thead > tr:first-child': {
|
||||
'th:first-child': {
|
||||
borderRadius: 0,
|
||||
},
|
||||
table: {
|
||||
borderRadius: 0,
|
||||
|
||||
'th:last-child': {
|
||||
borderRadius: 0,
|
||||
'> thead > tr:first-child': {
|
||||
'th:first-child': {
|
||||
borderRadius: 0,
|
||||
},
|
||||
|
||||
'th:last-child': {
|
||||
borderRadius: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -21,7 +21,7 @@ Ant Design has 3 types of Tabs for different situations.
|
||||
### Tabs
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| activeKey | Current TabPane's key | string | - | |
|
||||
| addIcon | Customize add icon | ReactNode | - | 4.4.0 |
|
||||
| animated | Whether to change tabs with animation. Only works while `tabPosition="top"` | boolean \| { inkBar: boolean, tabPane: boolean } | { inkBar: true, tabPane: false } | |
|
||||
@ -35,7 +35,7 @@ Ant Design has 3 types of Tabs for different situations.
|
||||
| tabBarExtraContent | Extra content in tab bar | ReactNode \| {left?: ReactNode, right?: ReactNode} | - | object: 4.6.0 |
|
||||
| tabBarGutter | The gap between tabs | number | - | |
|
||||
| tabBarStyle | Tab bar style object | CSSProperties | - | |
|
||||
| tabPosition | Position of tabs | `top` \| `right` \| `bottom` \| `left` | `top` | |
|
||||
| tabPosition | Position of tabs | `top` \| `right` \| `bottom` \ | `left` | `top` | |
|
||||
| destroyInactiveTabPane | Whether destroy inactive TabPane when change tab | boolean | false | |
|
||||
| type | Basic style of tabs | `line` \| `card` \| `editable-card` | `line` | |
|
||||
| onChange | Callback executed when active tab is changed | function(activeKey) {} | - | |
|
||||
@ -50,6 +50,7 @@ More option at [rc-tabs option](https://github.com/react-component/tabs#tabs)
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| closeIcon | Customize close icon in TabPane's head. Only works while `type="editable-card"` | ReactNode | - |
|
||||
| disabled | Set TabPane disabled | boolean | false |
|
||||
| forceRender | Forced render of content in tabs, not lazy render after clicking on tabs | boolean | false |
|
||||
| key | TabPane's key | string | - |
|
||||
| tab | Show text in TabPane's head | ReactNode | - |
|
||||
|
@ -27,7 +27,7 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
|
||||
| --- | --- | --- | --- | --- |
|
||||
| activeKey | 当前激活 tab 面板的 key | string | - | |
|
||||
| addIcon | 自定义添加按钮 | ReactNode | - | 4.4.0 |
|
||||
| animated | 是否使用动画切换 Tabs, 仅生效于 `tabPosition="top"` | boolean \| { inkBar: boolean, tabPane: boolean } | { inkBar: true, tabPane: false } | |
|
||||
| animated | 是否使用动画切换 Tabs, 仅生效于 `tabPosition="top"` | boolean\| { inkBar: boolean, tabPane: boolean } | { inkBar: true, tabPane: false } | |
|
||||
| centered | 标签居中展示 | boolean | false | 4.4.0 |
|
||||
| defaultActiveKey | 初始化选中面板的 key,如果没有设置 activeKey | string | `第一个面板` | |
|
||||
| hideAdd | 是否隐藏加号图标,在 `type="editable-card"` 时有效 | boolean | false | |
|
||||
@ -46,11 +46,16 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
|
||||
| onTabClick | tab 被点击的回调 | function(key: string, event: MouseEvent) | - | |
|
||||
| onTabScroll | tab 滚动时触发 | function({ direction: `left` \| `right` \| `top` \| `bottom` }) | - | 4.3.0 |
|
||||
|
||||
> 更多属性查看 [rc-tabs tabs](https://github.com/react-component/tabs#tabs)
|
||||
|
||||
### Tabs.TabPane
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ----------- | ----------------------------------------------- | --------- | ------ |
|
||||
| closeIcon | 自定义关闭图标,`在 type="editable-card"`时有效 | ReactNode | - |
|
||||
| disabled | 禁用某一项 | boolean | false |
|
||||
| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false |
|
||||
| key | 对应 activeKey | string | - |
|
||||
| tab | 选项卡头显示文字 | ReactNode | - |
|
||||
|
||||
> 更多属性查看 [rc-tabs tabpane](https://github.com/react-component/tabs#tabpane)
|
||||
|
@ -11,6 +11,7 @@ import type { ComponentToken as CheckboxComponentToken } from '../checkbox/style
|
||||
import type { ComponentToken as DatePickerComponentToken } from '../date-picker/style';
|
||||
import type { ComponentToken as DividerComponentToken } from '../divider/style';
|
||||
import type { ComponentToken as DropdownComponentToken } from '../dropdown/style';
|
||||
import type { ComponentToken as DrawerComponentToken } from '../drawer/style';
|
||||
import type { ComponentToken as EmptyComponentToken } from '../empty/style';
|
||||
import type { ComponentToken as ImageComponentToken } from '../image/style';
|
||||
import type { ComponentToken as InputNumberComponentToken } from '../input-number/style';
|
||||
@ -84,7 +85,7 @@ export interface ComponentTokenMap {
|
||||
DatePicker?: DatePickerComponentToken;
|
||||
Descriptions?: {};
|
||||
Divider?: DividerComponentToken;
|
||||
Drawer?: {};
|
||||
Drawer?: DrawerComponentToken;
|
||||
Dropdown?: DropdownComponentToken;
|
||||
Empty?: EmptyComponentToken;
|
||||
Form?: {};
|
||||
|
@ -60,7 +60,7 @@ export type RenderFunction = () => React.ReactNode;
|
||||
|
||||
export interface TooltipPropsWithOverlay extends AbstractTooltipProps {
|
||||
title?: React.ReactNode | RenderFunction;
|
||||
overlay: React.ReactNode | RenderFunction;
|
||||
overlay?: React.ReactNode | RenderFunction;
|
||||
}
|
||||
|
||||
export interface TooltipPropsWithTitle extends AbstractTooltipProps {
|
||||
|
@ -45,7 +45,7 @@ Tree selection control.
|
||||
| size | To set the size of the select input | `large` \| `middle` \| `small` | - | |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| suffixIcon | The custom suffix icon,you must set `showArrow` to `true` manually in multiple selection mode | ReactNode | - | |
|
||||
| switcherIcon | customize collapse \| expand icon of tree node | ReactNode | - | |
|
||||
| switcherIcon | Customize collapse/expand icon of tree node | ReactNode \| ((props: AntTreeNodeProps) => ReactNode) | - | renderProps: 4.20.0 |
|
||||
| tagRender | Customize tag render when `multiple` | (props) => ReactNode | - | |
|
||||
| treeCheckable | Whether to show checkbox on the treeNodes | boolean | false | |
|
||||
| treeCheckStrictly | Whether to check nodes precisely (in the `checkable` mode), means parent and child nodes are not associated, and it will make `labelInValue` be true | boolean | false | |
|
||||
|
@ -46,7 +46,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Ax4DA0njr/TreeSelect.svg
|
||||
| size | 选择框大小 | `large` \| `middle` \| `small` | - | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| suffixIcon | 自定义的选择框后缀图标, 多选模式下必须同时设置 `showArrow` 为 true | ReactNode | - | |
|
||||
| switcherIcon | 自定义树节点的展开/折叠图标 | ReactNode | - | |
|
||||
| switcherIcon | 自定义树节点的展开/折叠图标 | ReactNode \| ((props: AntTreeNodeProps) => ReactNode) | - | renderProps: 4.20.0 |
|
||||
| tagRender | 自定义 tag 内容,多选时生效 | (props) => ReactNode | - | |
|
||||
| treeCheckable | 显示 Checkbox | boolean | false | |
|
||||
| treeCheckStrictly | `checkable` 状态下节点选择完全受控(父子节点选中状态不再关联),会使得 `labelInValue` 强制为 true | boolean | false | |
|
||||
|
@ -12,7 +12,7 @@ import renderSwitcherIcon from './utils/iconUtil';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
export type SwitcherIcon = React.ReactNode | ((props: { expanded: boolean }) => React.ReactNode);
|
||||
export type SwitcherIcon = React.ReactNode | ((props: AntTreeNodeProps) => React.ReactNode);
|
||||
|
||||
export interface AntdTreeNodeAttribute {
|
||||
eventKey: string;
|
||||
@ -97,7 +97,8 @@ export interface AntTreeNodeDropEvent {
|
||||
// [Legacy] Compatible for v3
|
||||
export type TreeNodeNormal = DataNode;
|
||||
|
||||
type DraggableFn = (node: AntTreeNode) => boolean;
|
||||
type DraggableFn = (node: DataNode) => boolean;
|
||||
|
||||
interface DraggableConfig {
|
||||
icon?: React.ReactNode | false;
|
||||
nodeDraggable?: DraggableFn;
|
||||
|
@ -76,7 +76,7 @@ describe('Tree', () => {
|
||||
it('switcherIcon in Tree could be render prop function', () => {
|
||||
const { container } = render(
|
||||
<Tree
|
||||
switcherIcon={expanded =>
|
||||
switcherIcon={({ expanded }) =>
|
||||
expanded ? <span className="open" /> : <span className="close" />
|
||||
}
|
||||
defaultExpandAll
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { BasicDataNode } from 'rc-tree';
|
||||
import * as React from 'react';
|
||||
import { render } from '../../../tests/utils';
|
||||
import type { DataNode } from '../index';
|
||||
import Tree from '../index';
|
||||
|
||||
const { DirectoryTree } = Tree;
|
||||
@ -74,4 +75,25 @@ describe('Tree.TypeScript', () => {
|
||||
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
|
||||
it('draggable params type', () => {
|
||||
const { container } = render(
|
||||
<Tree
|
||||
treeData={[
|
||||
{
|
||||
title: 'Bamboo',
|
||||
key: 'bamboo',
|
||||
children: [
|
||||
{
|
||||
title: 'Little',
|
||||
key: 'little',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
draggable={(node: DataNode) => node.title === 'Little'}
|
||||
/>,
|
||||
);
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -44,7 +44,7 @@ Almost anything can be represented in a tree structure. Examples include directo
|
||||
| selectedKeys | (Controlled) Specifies the keys of the selected treeNodes | string\[] | - | |
|
||||
| showIcon | Shows the icon before a TreeNode's title. There is no default style; you must set a custom style for it if set to true | boolean | false | |
|
||||
| showLine | Shows a connecting line | boolean \| {showLeafIcon: boolean} | false | |
|
||||
| switcherIcon | Customize collapse/expand icon of tree node | ReactNode \| (({ expanded: boolean }) => React.ReactNode) | - | renderProps: 4.20.0 |
|
||||
| switcherIcon | Customize collapse/expand icon of tree node | ReactNode \| ((props: AntTreeNodeProps) => ReactNode) | - | renderProps: 4.20.0 |
|
||||
| titleRender | Customize tree node title render | (nodeData) => ReactNode | - | 4.5.0 |
|
||||
| treeData | The treeNodes data Array, if set it then you need not to construct children TreeNode. (key should be unique across the whole array) | array<{ key, title, children, \[disabled, selectable] }> | - | |
|
||||
| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 |
|
||||
|
@ -45,7 +45,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Xh-oWqg9k/Tree.svg
|
||||
| selectedKeys | (受控)设置选中的树节点 | string\[] | - | |
|
||||
| showIcon | 是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true,需要自行定义图标相关样式 | boolean | false | |
|
||||
| showLine | 是否展示连接线 | boolean \| {showLeafIcon: boolean} | false | |
|
||||
| switcherIcon | 自定义树节点的展开/折叠图标 | ReactNode \| (({ expanded: boolean }) => React.ReactNode) | - | renderProps: 4.20.0 |
|
||||
| switcherIcon | 自定义树节点的展开/折叠图标 | ReactNode \| ((props: AntTreeNodeProps) => ReactNode) | - | renderProps: 4.20.0 |
|
||||
| titleRender | 自定义渲染节点 | (nodeData) => ReactNode | - | 4.5.0 |
|
||||
| treeData | treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点(key 在整个树范围内唯一) | array<{key, title, children, \[disabled, selectable]}> | - | |
|
||||
| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 4.1.0 |
|
||||
|
@ -35,8 +35,7 @@ export default function renderSwitcherIcon(
|
||||
|
||||
const switcherCls = `${prefixCls}-switcher-icon`;
|
||||
|
||||
const switcher =
|
||||
typeof switcherIcon === 'function' ? switcherIcon({ expanded: !!expanded }) : switcherIcon;
|
||||
const switcher = typeof switcherIcon === 'function' ? switcherIcon(treeNodeProps) : switcherIcon;
|
||||
|
||||
if (isValidElement(switcher)) {
|
||||
return cloneElement(switcher, {
|
||||
|
@ -1,25 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import Tooltip from '../../tooltip';
|
||||
import type { TooltipProps } from '../../tooltip';
|
||||
|
||||
export interface EllipsisTooltipProps {
|
||||
title?: React.ReactNode;
|
||||
tooltipProps?: TooltipProps;
|
||||
enabledEllipsis: boolean;
|
||||
isEllipsis?: boolean;
|
||||
children: React.ReactElement;
|
||||
}
|
||||
|
||||
const EllipsisTooltip = ({
|
||||
title,
|
||||
enabledEllipsis,
|
||||
isEllipsis,
|
||||
children,
|
||||
tooltipProps,
|
||||
}: EllipsisTooltipProps) => {
|
||||
if (!title || !enabledEllipsis) {
|
||||
if (!tooltipProps?.title || !enabledEllipsis) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip title={title} visible={isEllipsis ? undefined : false}>
|
||||
<Tooltip visible={isEllipsis ? undefined : false} {...tooltipProps}>
|
||||
{children}
|
||||
</Tooltip>
|
||||
);
|
||||
|
@ -13,9 +13,10 @@ import { composeRef } from 'rc-util/lib/ref';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../../config-provider';
|
||||
import { useLocaleReceiver } from '../../locale-provider/LocaleReceiver';
|
||||
import Tooltip from '../../tooltip';
|
||||
import { isStyleSupport } from '../../_util/styleChecker';
|
||||
import TransButton from '../../_util/transButton';
|
||||
import { isStyleSupport } from '../../_util/styleChecker';
|
||||
import type { TooltipProps } from '../../tooltip';
|
||||
import Tooltip from '../../tooltip';
|
||||
import Editable from '../Editable';
|
||||
import useMergedConfig from '../hooks/useMergedConfig';
|
||||
import useUpdatedEffect from '../hooks/useUpdatedEffect';
|
||||
@ -55,7 +56,7 @@ export interface EllipsisConfig {
|
||||
symbol?: React.ReactNode;
|
||||
onExpand?: React.MouseEventHandler<HTMLElement>;
|
||||
onEllipsis?: (ellipsis: boolean) => void;
|
||||
tooltip?: React.ReactNode;
|
||||
tooltip?: React.ReactNode | TooltipProps;
|
||||
}
|
||||
|
||||
export interface BlockProps extends TypographyProps {
|
||||
@ -309,7 +310,16 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
|
||||
}, [enableEllipsis, cssEllipsis, children, cssLineClamp]);
|
||||
|
||||
// ========================== Tooltip ===========================
|
||||
const tooltipTitle = ellipsisConfig.tooltip === true ? children : ellipsisConfig.tooltip;
|
||||
let tooltipProps: TooltipProps = {};
|
||||
if (ellipsisConfig.tooltip === true) {
|
||||
tooltipProps = { title: children };
|
||||
} else if (React.isValidElement(ellipsisConfig.tooltip)) {
|
||||
tooltipProps = { title: ellipsisConfig.tooltip };
|
||||
} else if (typeof ellipsisConfig.tooltip === 'object') {
|
||||
tooltipProps = { title: children, ...ellipsisConfig.tooltip };
|
||||
} else {
|
||||
tooltipProps = { title: ellipsisConfig.tooltip };
|
||||
}
|
||||
const topAriaLabel = React.useMemo(() => {
|
||||
const isValid = (val: any) => ['string', 'number'].includes(typeof val);
|
||||
|
||||
@ -325,12 +335,12 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
|
||||
return title;
|
||||
}
|
||||
|
||||
if (isValid(tooltipTitle)) {
|
||||
return tooltipTitle;
|
||||
if (isValid(tooltipProps.title)) {
|
||||
return tooltipProps.title;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [enableEllipsis, cssEllipsis, title, tooltipTitle, isMergedEllipsis]);
|
||||
}, [enableEllipsis, cssEllipsis, title, tooltipProps.title, isMergedEllipsis]);
|
||||
|
||||
// =========================== Render ===========================
|
||||
// >>>>>>>>>>> Editing input
|
||||
@ -452,7 +462,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
|
||||
<ResizeObserver onResize={onResize} disabled={!mergedEnableEllipsis || cssEllipsis}>
|
||||
{resizeRef => (
|
||||
<EllipsisTooltip
|
||||
title={tooltipTitle}
|
||||
tooltipProps={tooltipProps}
|
||||
enabledEllipsis={mergedEnableEllipsis}
|
||||
isEllipsis={isMergedEllipsis}
|
||||
>
|
||||
|
@ -292,6 +292,38 @@ describe('Typography.Ellipsis', () => {
|
||||
expect(baseElement.querySelector('.ant-tooltip-open')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
it('tooltip props', async () => {
|
||||
const { container, baseElement } = await getWrapper({
|
||||
title: 'This is tooltip',
|
||||
className: 'tooltip-class-name',
|
||||
});
|
||||
fireEvent.mouseEnter(container.firstChild);
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('.tooltip-class-name')).toBeTruthy();
|
||||
expect(baseElement.querySelector('.ant-tooltip-open')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
it('tooltip title true', async () => {
|
||||
const { container, baseElement } = await getWrapper({
|
||||
title: true,
|
||||
className: 'tooltip-class-name',
|
||||
});
|
||||
fireEvent.mouseEnter(container.firstChild);
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('.tooltip-class-name')).toBeTruthy();
|
||||
expect(baseElement.querySelector('.ant-tooltip-open')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
it('tooltip element', async () => {
|
||||
const { container, baseElement } = await getWrapper(
|
||||
<div className="tooltip-class-name">title</div>,
|
||||
);
|
||||
fireEvent.mouseEnter(container.firstChild);
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('.tooltip-class-name')).toBeTruthy();
|
||||
expect(baseElement.querySelector('.ant-tooltip-open')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('js ellipsis should show aria-label', () => {
|
||||
|
@ -122,7 +122,7 @@ Basic text writing, including headings, body text, lists, and more.
|
||||
expandable: boolean,
|
||||
suffix: string,
|
||||
symbol: ReactNode,
|
||||
tooltip: boolean | ReactNode,
|
||||
tooltip: boolean | ReactNode | TooltipProps,
|
||||
onExpand: function(event),
|
||||
onEllipsis: function(ellipsis),
|
||||
}
|
||||
@ -133,7 +133,7 @@ Basic text writing, including headings, body text, lists, and more.
|
||||
| rows | Max rows of content | number | - | |
|
||||
| suffix | Suffix of ellipsis content | string | - | |
|
||||
| symbol | Custom description of ellipsis | ReactNode | `Expand` | |
|
||||
| tooltip | Show tooltip when ellipsis | boolean \| ReactNode | - | 4.11.0 |
|
||||
| tooltip | Show tooltip when ellipsis | boolean \| ReactNode \| [TooltipProps](/components/tooltip/#API) | - | 4.11.0 |
|
||||
| onEllipsis | Called when enter or leave ellipsis state | function(ellipsis) | - | 4.2.0 |
|
||||
| onExpand | Called when expand content | function(event) | - | |
|
||||
|
||||
|
@ -123,20 +123,20 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg
|
||||
expandable: boolean,
|
||||
suffix: string,
|
||||
symbol: ReactNode,
|
||||
tooltip: boolean | ReactNode,
|
||||
tooltip: boolean | ReactNode | TooltipProps,
|
||||
onExpand: function(event),
|
||||
onEllipsis: function(ellipsis),
|
||||
}
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| ---------- | -------------------- | -------------------- | ------ | ------ |
|
||||
| expandable | 是否可展开 | boolean | - | |
|
||||
| rows | 最多显示的行数 | number | - | |
|
||||
| suffix | 自定义省略内容后缀 | string | - | |
|
||||
| symbol | 自定义展开描述文案 | ReactNode | `展开` | |
|
||||
| tooltip | 省略时,展示提示信息 | boolean \| ReactNode | - | 4.11.0 |
|
||||
| onEllipsis | 触发省略时的回调 | function(ellipsis) | - | 4.2.0 |
|
||||
| onExpand | 点击展开时的回调 | function(event) | - | |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| expandable | 是否可展开 | boolean | - | |
|
||||
| rows | 最多显示的行数 | number | - | |
|
||||
| suffix | 自定义省略内容后缀 | string | - | |
|
||||
| symbol | 自定义展开描述文案 | ReactNode | `展开` | |
|
||||
| tooltip | 省略时,展示提示信息 | boolean \| ReactNode \| [TooltipProps](/components/tooltip/#API) | - | 4.11.0 |
|
||||
| onEllipsis | 触发省略时的回调 | function(ellipsis) | - | 4.2.0 |
|
||||
| onExpand | 点击展开时的回调 | function(event) | - | |
|
||||
|
||||
## FAQ
|
||||
|
||||
|
@ -269,8 +269,15 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
|
||||
const removedFileList = removeFileItem(file, mergedFileList);
|
||||
|
||||
if (removedFileList) {
|
||||
currentFile = { ...file };
|
||||
currentFile = { ...file, status: 'removed' };
|
||||
mergedFileList?.forEach(item => {
|
||||
const matchKey = currentFile.uid !== undefined ? 'uid' : 'name';
|
||||
if (item[matchKey] === currentFile[matchKey] && !Object.isFrozen(item)) {
|
||||
item.status = 'removed';
|
||||
}
|
||||
});
|
||||
upload.current?.abort(currentFile);
|
||||
|
||||
onInternalChange(currentFile, removedFileList);
|
||||
}
|
||||
});
|
||||
|
@ -72,6 +72,15 @@ const ListItem = React.forwardRef(
|
||||
}: ListItemProps,
|
||||
ref: React.Ref<HTMLDivElement>,
|
||||
) => {
|
||||
// Status: which will ignore `removed` status
|
||||
const { status } = file;
|
||||
const [mergedStatus, setMergedStatus] = React.useState(status);
|
||||
React.useEffect(() => {
|
||||
if (status !== 'removed') {
|
||||
setMergedStatus(status);
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
// Delay to show the progress bar
|
||||
const [showProgress, setShowProgress] = React.useState(false);
|
||||
const progressRafRef = React.useRef<any>();
|
||||
@ -88,10 +97,10 @@ const ListItem = React.forwardRef(
|
||||
const iconNode = iconRender(file);
|
||||
let icon = <div className={`${prefixCls}-icon`}>{iconNode}</div>;
|
||||
if (listType === 'picture' || listType === 'picture-card') {
|
||||
if (file.status === 'uploading' || (!file.thumbUrl && !file.url)) {
|
||||
if (mergedStatus === 'uploading' || (!file.thumbUrl && !file.url)) {
|
||||
const uploadingClassName = classNames({
|
||||
[`${prefixCls}-list-item-thumbnail`]: true,
|
||||
[`${prefixCls}-list-item-file`]: file.status !== 'uploading',
|
||||
[`${prefixCls}-list-item-file`]: mergedStatus !== 'uploading',
|
||||
});
|
||||
icon = <div className={uploadingClassName}>{iconNode}</div>;
|
||||
} else {
|
||||
@ -125,7 +134,7 @@ const ListItem = React.forwardRef(
|
||||
|
||||
const listItemClassName = classNames(
|
||||
`${prefixCls}-list-item`,
|
||||
`${prefixCls}-list-item-${file.status}`,
|
||||
`${prefixCls}-list-item-${mergedStatus}`,
|
||||
);
|
||||
const linkProps =
|
||||
typeof file.linkProps === 'string' ? JSON.parse(file.linkProps) : file.linkProps;
|
||||
@ -142,7 +151,7 @@ const ListItem = React.forwardRef(
|
||||
: null;
|
||||
|
||||
const downloadIcon =
|
||||
showDownloadIcon && file.status === 'done'
|
||||
showDownloadIcon && mergedStatus === 'done'
|
||||
? actionIconRender(
|
||||
(typeof customDownloadIcon === 'function'
|
||||
? customDownloadIcon(file)
|
||||
@ -211,10 +220,10 @@ const ListItem = React.forwardRef(
|
||||
</a>
|
||||
) : null;
|
||||
|
||||
const pictureCardActions = listType === 'picture-card' && file.status !== 'uploading' && (
|
||||
const pictureCardActions = listType === 'picture-card' && mergedStatus !== 'uploading' && (
|
||||
<span className={`${prefixCls}-list-item-actions`}>
|
||||
{previewIcon}
|
||||
{file.status === 'done' && downloadIcon}
|
||||
{mergedStatus === 'done' && downloadIcon}
|
||||
{removeIcon}
|
||||
</span>
|
||||
);
|
||||
@ -230,7 +239,7 @@ const ListItem = React.forwardRef(
|
||||
{showProgress && (
|
||||
<CSSMotion
|
||||
motionName={`${rootPrefixCls}-fade`}
|
||||
visible={file.status === 'uploading'}
|
||||
visible={mergedStatus === 'uploading'}
|
||||
motionDeadline={2000}
|
||||
>
|
||||
{({ className: motionClassName }) => {
|
||||
@ -256,7 +265,7 @@ const ListItem = React.forwardRef(
|
||||
? file.response
|
||||
: file.error?.statusText || file.error?.message || locale.uploadError;
|
||||
const item =
|
||||
file.status === 'error' ? (
|
||||
mergedStatus === 'error' ? (
|
||||
<Tooltip title={message} getPopupContainer={node => node.parentNode as HTMLElement}>
|
||||
{dom}
|
||||
</Tooltip>
|
||||
|
@ -110,7 +110,7 @@ exports[`Upload List itemRender 1`] = `
|
||||
class="custom-item-render"
|
||||
>
|
||||
<span>
|
||||
uid:-1 name: xxx.png status: done url: https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png 1/2
|
||||
uid:-1 name: xxx.png status: removed url: https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png 1/2
|
||||
</span>
|
||||
<span
|
||||
class="custom-item-render-action-remove"
|
||||
@ -136,7 +136,7 @@ exports[`Upload List itemRender 1`] = `
|
||||
class="custom-item-render"
|
||||
>
|
||||
<span>
|
||||
uid:-2 name: yyy.png status: done url: https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png 2/2
|
||||
uid:-2 name: yyy.png status: removed url: https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png 2/2
|
||||
</span>
|
||||
<span
|
||||
class="custom-item-render-action-remove"
|
||||
|
@ -505,9 +505,6 @@ describe('Upload', () => {
|
||||
<Upload fileList={[file]} onChange={onChange} onRemove={onRemove} />,
|
||||
);
|
||||
fireEvent.click(container.querySelector('div.ant-upload-list-item .anticon-delete'));
|
||||
expect(container.querySelector('.ant-upload-list-item').className).toContain(
|
||||
'ant-upload-list-item-uploading',
|
||||
);
|
||||
|
||||
// uploadStart is a batch work which we need wait for react act
|
||||
await act(async () => {
|
||||
@ -520,13 +517,8 @@ describe('Upload', () => {
|
||||
await removePromise(true);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/36286
|
||||
expect(container.querySelector('.ant-upload-list-item').className).toContain(
|
||||
'ant-upload-list-item-uploading',
|
||||
);
|
||||
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
expect(file.status).toBe('uploading');
|
||||
expect(file.status).toBe('removed');
|
||||
});
|
||||
|
||||
it('should not stop download when return use onDownload', done => {
|
||||
|
@ -1567,4 +1567,45 @@ describe('Upload List', () => {
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/36286
|
||||
it('remove should keep origin className', async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const onChange = jest.fn();
|
||||
const list = [
|
||||
{
|
||||
uid: '0',
|
||||
name: 'xxx.png',
|
||||
status: 'error',
|
||||
},
|
||||
];
|
||||
const { container } = render(
|
||||
<Upload fileList={list} listType="picture-card" onChange={onChange} />,
|
||||
);
|
||||
|
||||
fireEvent.click(container.querySelector('.anticon-delete'));
|
||||
|
||||
// Wait for Upload sync
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
file: expect.objectContaining({
|
||||
status: 'removed',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(container.querySelector('.ant-upload-list-item-error')).toBeTruthy();
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
@ -57,7 +57,7 @@ Extends File with additional props.
|
||||
| crossOrigin | CORS settings attributes | `'anonymous'` \| `'use-credentials'` \| `''` | - | 4.20.0 |
|
||||
| name | File name | string | - | - |
|
||||
| percent | Upload progress percent | number | - | - |
|
||||
| status | Upload status. Show different style when configured | `error` \| `success` \| `done` \| `uploading` | - | - |
|
||||
| status | Upload status. Show different style when configured | `error` \| `success` \| `done` \| `uploading` \| `removed` | - | - |
|
||||
| thumbUrl | Thumb image url | string | - | - |
|
||||
| uid | unique id. Will auto generate when not provided | string | - | - |
|
||||
| url | Download url | string | - | - |
|
||||
@ -82,7 +82,7 @@ When uploading state change, it returns:
|
||||
{
|
||||
uid: 'uid', // unique identifier, negative is recommend, to prevent interference with internal generated id
|
||||
name: 'xx.png', // file name
|
||||
status: 'done', // options:uploading, done, error. Intercepted file by beforeUpload don't have status field.
|
||||
status: 'done', // options:uploading, done, error, removed. Intercepted file by beforeUpload don't have status field.
|
||||
response: '{"status": "success"}', // response from server
|
||||
linkProps: '{"download": "image"}', // additional html props of file link
|
||||
xhr: 'XMLHttpRequest{ ... }', // XMLHttpRequest Header
|
||||
|
@ -58,7 +58,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg
|
||||
| crossOrigin | CORS 属性设置 | `'anonymous'` \| `'use-credentials'` \| `''` | - | 4.20.0 |
|
||||
| name | 文件名 | string | - | - |
|
||||
| percent | 上传进度 | number | - | - |
|
||||
| status | 上传状态,不同状态展示颜色也会有所不同 | `error` \| `success` \| `done` \| `uploading` | - | - |
|
||||
| status | 上传状态,不同状态展示颜色也会有所不同 | `error` \| `success` \| `done` \| `uploading` \| `removed` | - | - |
|
||||
| thumbUrl | 缩略图地址 | string | - | - |
|
||||
| uid | 唯一标识符,不设置时会自动生成 | string | - | - |
|
||||
| url | 下载地址 | string | - | - |
|
||||
@ -83,7 +83,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg
|
||||
{
|
||||
uid: 'uid', // 文件唯一标识,建议设置为负数,防止和内部产生的 id 冲突
|
||||
name: 'xx.png' // 文件名
|
||||
status: 'done', // 状态有:uploading done error,beforeUpload 拦截的文件没有 status 属性
|
||||
status: 'done', // 状态有:uploading done error removed,被 beforeUpload 拦截的文件没有 status 属性
|
||||
response: '{"status": "success"}', // 服务端响应内容
|
||||
linkProps: '{"download": "image"}', // 下载链接额外的 HTML 属性
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export interface RcFile extends OriRcFile {
|
||||
readonly lastModifiedDate: Date;
|
||||
}
|
||||
|
||||
export type UploadFileStatus = 'error' | 'success' | 'done' | 'uploading';
|
||||
export type UploadFileStatus = 'error' | 'success' | 'done' | 'uploading' | 'removed';
|
||||
|
||||
export interface HttpRequestHeader {
|
||||
[key: string]: string;
|
||||
|
@ -21,6 +21,10 @@ But in antd, `undefined` is treated as uncontrolled, and `null` is used as an ex
|
||||
|
||||
Note: For `options` in `Select-like` components, it is **strongly recommended not** to use `undefined` and `null` as `value` in `option`. Please use `string | number` as a valid `value` in `option`.
|
||||
|
||||
## Can I use internal API which is not documented on the site?
|
||||
|
||||
NOT RECOMMEND. Internal API is not guaranteed to be compatible with future versions. It may be removed or changed in some versions. If you really need to use it, you should to make sure these API is still valid when upgrading to a new version or just lock version for usage.
|
||||
|
||||
## `Select Dropdown DatePicker TimePicker Popover Popconfirm` disappears when I click another popup component inside it. How do I resolve this?
|
||||
|
||||
This is an old bug that has been fixed since `v3.11.x`. If you're using an older version, you can use `<Select getPopupContainer={trigger => trigger.parentElement}>` to render a component inside Popover. (Or other `getXxxxContainer` props)
|
||||
|
@ -21,6 +21,10 @@ title: FAQ
|
||||
|
||||
注意:对于类 `Select` 组件的 `options`,我们**强烈不建议**使用 `undefined` 和 `null` 作为 `option` 中的 `value`,请使用 `string | number` 作为 `option` 的 `value`。
|
||||
|
||||
## 官方文档中没有提供的隐藏 API 我可以使用吗?
|
||||
|
||||
不推荐。对内接口不保证兼容性,它很可能在某个版本中因重构而移除。如果你确实需要使用,需自行确保版本升级时隐藏接口仍旧可用,或者锁定版本。
|
||||
|
||||
## 当我点击 `Select Dropdown DatePicker TimePicker Popover Popconfirm` 内的另一个 popup 组件时它会消失,如何解决?
|
||||
|
||||
该问题在 `3.11.0` 后已经解决。如果你仍在使用旧版本,你可以通过 `<Select getPopupContainer={trigger => trigger.parentElement}>` 来在 Popover 中渲染组件,或者使用其他的 `getXxxxContainer` 参数。
|
||||
|
31
package.json
31
package.json
@ -49,6 +49,7 @@
|
||||
"authors": "node ./scripts/generate-authors",
|
||||
"build": "npm run compile && NODE_OPTIONS='--max-old-space-size=4096' npm run dist",
|
||||
"bundlesize": "bundlesize",
|
||||
"size-limit": "size-limit",
|
||||
"check-commit": "node ./scripts/check-commit",
|
||||
"check-ts-demo": "node ./scripts/check-ts-demo",
|
||||
"clean": "antd-tools run clean && rm -rf es lib coverage dist report.html",
|
||||
@ -123,12 +124,12 @@
|
||||
"rc-checkbox": "~2.3.0",
|
||||
"rc-collapse": "~3.3.0",
|
||||
"rc-dialog": "~8.9.0",
|
||||
"rc-drawer": "~4.4.2",
|
||||
"rc-drawer": "~5.1.0-alpha.1",
|
||||
"rc-dropdown": "~4.0.0",
|
||||
"rc-field-form": "~1.27.0",
|
||||
"rc-image": "~5.7.0",
|
||||
"rc-input": "~0.0.1-alpha.5",
|
||||
"rc-input-number": "~7.3.0",
|
||||
"rc-input-number": "~7.3.5",
|
||||
"rc-mentions": "~1.9.0",
|
||||
"rc-menu": "~9.6.0",
|
||||
"rc-motion": "^2.6.1",
|
||||
@ -143,7 +144,7 @@
|
||||
"rc-slider": "~10.0.0",
|
||||
"rc-steps": "~4.1.0",
|
||||
"rc-switch": "~3.2.0",
|
||||
"rc-table": "~7.25.0",
|
||||
"rc-table": "~7.25.3",
|
||||
"rc-tabs": "~11.16.0",
|
||||
"rc-textarea": "~0.3.0",
|
||||
"rc-tooltip": "~5.2.0",
|
||||
@ -161,6 +162,7 @@
|
||||
"@ant-design/tools": "^15.0.4",
|
||||
"@docsearch/css": "^3.0.0",
|
||||
"@qixian.cs/github-contributors-list": "^1.0.3",
|
||||
"@size-limit/file": "^7.0.8",
|
||||
"@stackblitz/sdk": "^1.3.0",
|
||||
"@testing-library/jest-dom": "^5.16.3",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
@ -282,6 +284,7 @@
|
||||
"scrollama": "^3.0.0",
|
||||
"semver": "^7.3.5",
|
||||
"simple-git": "^3.0.0",
|
||||
"size-limit": "^7.0.8",
|
||||
"stylelint": "^14.9.0",
|
||||
"stylelint-config-prettier": "^9.0.2",
|
||||
"stylelint-config-rational-order": "^0.1.2",
|
||||
@ -302,6 +305,28 @@
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"size-limit": [
|
||||
{
|
||||
"path": "./dist/antd.min.js",
|
||||
"limit": "282 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/antd.min.css",
|
||||
"limit": "66 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/antd.dark.min.css",
|
||||
"limit": "67.5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/antd.compact.min.css",
|
||||
"limit": "66 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/antd.variable.min.css",
|
||||
"limit": "67 kB"
|
||||
}
|
||||
],
|
||||
"bundlesize": [
|
||||
{
|
||||
"path": "./dist/antd.min.js",
|
||||
|
@ -7,6 +7,23 @@ const chalk = require('chalk');
|
||||
const { spawnSync } = require('child_process');
|
||||
const packageJson = require('../package.json');
|
||||
|
||||
const DEPRECIATED_VERSION = {
|
||||
'>= 4.21.6 < 4.22.0': ['https://github.com/ant-design/ant-design/pull/36682'],
|
||||
};
|
||||
|
||||
function matchDeprecated(version) {
|
||||
const match = Object.keys(DEPRECIATED_VERSION).find(depreciated =>
|
||||
semver.satisfies(version, depreciated),
|
||||
);
|
||||
|
||||
const reason = DEPRECIATED_VERSION[match] || [];
|
||||
|
||||
return {
|
||||
match,
|
||||
reason: Array.isArray(reason) ? reason : [reason],
|
||||
};
|
||||
}
|
||||
|
||||
const SAFE_DAYS_START = 1000 * 60 * 60 * 24 * 15; // 15 days
|
||||
const SAFE_DAYS_DIFF = 1000 * 60 * 60 * 24 * 3; // 3 days not update seems to be stable
|
||||
|
||||
@ -35,16 +52,27 @@ const SAFE_DAYS_DIFF = 1000 * 60 * 60 * 24 * 3; // 3 days not update seems to be
|
||||
});
|
||||
|
||||
// Slice for choosing the latest versions
|
||||
const latestVersions = versionList.slice(0, 20).map(version => ({
|
||||
publishTime: time[version],
|
||||
timeDiff: dayjs().diff(dayjs(time[version])),
|
||||
value: version,
|
||||
}));
|
||||
const latestVersions = versionList
|
||||
// Cut off
|
||||
.slice(0, 30)
|
||||
// Formatter
|
||||
.map(version => ({
|
||||
publishTime: time[version],
|
||||
timeDiff: dayjs().diff(dayjs(time[version])),
|
||||
value: version,
|
||||
depreciated: matchDeprecated(version).match,
|
||||
}));
|
||||
|
||||
const startDefaultVersionIndex = latestVersions.findIndex(
|
||||
const filteredLatestVersions = latestVersions
|
||||
// Filter deprecated versions
|
||||
.filter(({ depreciated }) => !depreciated);
|
||||
|
||||
const startDefaultVersionIndex = filteredLatestVersions.findIndex(
|
||||
({ timeDiff }) => timeDiff >= SAFE_DAYS_START,
|
||||
);
|
||||
const defaultVersionList = latestVersions.slice(0, startDefaultVersionIndex + 1).reverse();
|
||||
const defaultVersionList = filteredLatestVersions
|
||||
.slice(0, startDefaultVersionIndex + 1)
|
||||
.reverse();
|
||||
|
||||
// Find safe version
|
||||
let defaultVersionObj;
|
||||
@ -64,26 +92,52 @@ const SAFE_DAYS_DIFF = 1000 * 60 * 60 * 24 * 3; // 3 days not update seems to be
|
||||
const defaultVersion = defaultVersionObj ? defaultVersionObj.value : null;
|
||||
|
||||
// Selection
|
||||
const { conchVersion } = await inquirer.prompt([
|
||||
let { conchVersion } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'conchVersion',
|
||||
default: defaultVersion,
|
||||
message: 'Please select Conch Version:',
|
||||
choices: latestVersions.map(info => {
|
||||
const { value, publishTime } = info;
|
||||
const { value, publishTime, depreciated } = info;
|
||||
const desc = dayjs(publishTime).fromNow();
|
||||
|
||||
return {
|
||||
...info,
|
||||
name: `${value} (${desc}) ${value === defaultVersion ? '(default)' : ''}`,
|
||||
name: `${depreciated ? '🚨' : '✅'} ${value} (${desc}) ${
|
||||
value === defaultVersion ? '(default)' : ''
|
||||
}`,
|
||||
};
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
// Make sure it's not deprecated version
|
||||
const deprecatedObj = matchDeprecated(conchVersion);
|
||||
if (deprecatedObj.match) {
|
||||
console.log('\n');
|
||||
console.log(chalk.red('Deprecated For:'));
|
||||
deprecatedObj.reason.forEach(reason => {
|
||||
console.log(chalk.yellow(` * ${reason}`));
|
||||
});
|
||||
console.log('\n');
|
||||
|
||||
const { conchConfirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'conchVersion',
|
||||
default: false,
|
||||
message: 'SURE to continue?!!',
|
||||
},
|
||||
]);
|
||||
|
||||
if (!conchConfirm) {
|
||||
conchVersion = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if need to update
|
||||
if (distTags.conch === conchVersion) {
|
||||
if (!conchVersion || distTags.conch === conchVersion) {
|
||||
console.log(`🎃 Conch Version not change. Safe to ${chalk.green('ignore')}.`);
|
||||
} else {
|
||||
console.log('💾 Tagging Conch Version:', chalk.green(conchVersion));
|
||||
|
@ -1,8 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import Trigger from 'rc-trigger/lib/mock';
|
||||
import { TriggerMockContext } from '../shared/demoTest';
|
||||
import { TriggerMockContext } from '../shared/demoTestContext';
|
||||
|
||||
export default React.forwardRef((props, ref) => {
|
||||
const mergedPopupVisible = React.useContext(TriggerMockContext) ?? props.popupVisible;
|
||||
global.triggerProps = props;
|
||||
return <Trigger {...props} ref={ref} popupVisible={mergedPopupVisible} />;
|
||||
});
|
||||
|
@ -1,16 +1,15 @@
|
||||
/* eslint-disable react/jsx-no-constructed-context-values */
|
||||
import * as React from 'react';
|
||||
import glob from 'glob';
|
||||
import { render } from 'enzyme';
|
||||
import { render as enzymeRender } from 'enzyme';
|
||||
import MockDate from 'mockdate';
|
||||
import dayjs from 'dayjs';
|
||||
import { StyleProvider, createCache } from '@ant-design/cssinjs';
|
||||
import type { TriggerProps } from 'rc-trigger';
|
||||
import { excludeWarning } from './excludeWarning';
|
||||
import { render, act } from '../utils';
|
||||
import { TriggerMockContext } from './demoTestContext';
|
||||
|
||||
export const TriggerMockContext = React.createContext<Partial<TriggerProps> | undefined>(undefined);
|
||||
|
||||
type CheerIO = ReturnType<typeof render>;
|
||||
type CheerIO = ReturnType<typeof enzymeRender>;
|
||||
type CheerIOElement = CheerIO[0];
|
||||
// We should avoid use it in 4.0. Reopen if can not handle this.
|
||||
const USE_REPLACEMENT = false;
|
||||
@ -53,6 +52,7 @@ function ariaConvert(wrapper: CheerIO) {
|
||||
|
||||
type Options = {
|
||||
skip?: boolean | string[];
|
||||
testingLib?: boolean;
|
||||
};
|
||||
|
||||
function baseText(doInject: boolean, component: string, options: Options = {}) {
|
||||
@ -76,7 +76,7 @@ function baseText(doInject: boolean, component: string, options: Options = {}) {
|
||||
// Inject cssinjs cache to avoid create <style /> element
|
||||
Demo = <StyleProvider cache={createCache()}>{Demo}</StyleProvider>;
|
||||
|
||||
render(Demo);
|
||||
enzymeRender(Demo);
|
||||
|
||||
expect(errSpy).not.toHaveBeenCalledWith(expect.stringContaining('[Ant Design CSS-in-JS]'));
|
||||
MockDate.reset();
|
||||
@ -90,8 +90,9 @@ function baseText(doInject: boolean, component: string, options: Options = {}) {
|
||||
doInject ? `renders ${file} extend context correctly` : `renders ${file} correctly`,
|
||||
() => {
|
||||
const errSpy = excludeWarning();
|
||||
const mockDate = dayjs('2016-11-22').valueOf();
|
||||
|
||||
MockDate.set(dayjs('2016-11-22').valueOf());
|
||||
MockDate.set(mockDate);
|
||||
let Demo = require(`../.${file}`).default; // eslint-disable-line global-require, import/no-dynamic-require
|
||||
// Inject Trigger status unless skipped
|
||||
Demo = typeof Demo === 'function' ? <Demo /> : Demo;
|
||||
@ -110,14 +111,29 @@ function baseText(doInject: boolean, component: string, options: Options = {}) {
|
||||
// Inject cssinjs cache to avoid create <style /> element
|
||||
Demo = <StyleProvider cache={createCache()}>{Demo}</StyleProvider>;
|
||||
|
||||
const wrapper = render(Demo);
|
||||
if (options?.testingLib) {
|
||||
jest.useFakeTimers().setSystemTime(mockDate);
|
||||
|
||||
// Convert aria related content
|
||||
ariaConvert(wrapper);
|
||||
const { container } = render(Demo);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
const { children } = container;
|
||||
const child = children.length > 1 ? children : children[0];
|
||||
expect(child).toMatchSnapshot();
|
||||
|
||||
jest.useRealTimers();
|
||||
} else {
|
||||
const wrapper = enzymeRender(Demo);
|
||||
|
||||
// Convert aria related content
|
||||
ariaConvert(wrapper);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
}
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
MockDate.reset();
|
||||
|
||||
errSpy();
|
||||
},
|
||||
);
|
||||
|
7
tests/shared/demoTestContext.ts
Normal file
7
tests/shared/demoTestContext.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import * as React from 'react';
|
||||
import type { TriggerProps } from 'rc-trigger';
|
||||
|
||||
// We export context here is to avoid testing-lib inject `afterEach` in `tests/index.test.js`
|
||||
// Which breaks the circle deps
|
||||
export const TriggerMockContext = React.createContext<Partial<TriggerProps> | undefined>(undefined);
|
Loading…
Reference in New Issue
Block a user