Compare commits

..

38 Commits
4.6.0 ... 4.6.2

Author SHA1 Message Date
ZHANGYU
09dd53915e docs: update modal doc (#26492)
* docs: update modal doc

* docs: sort modal props
2020-08-31 14:18:50 +08:00
信鑫-King
fb8be1e249 release: 4.6.2 changelog (#26490)
* docs: changelog

* chore: version

* chore: changelog

* docs: changelog
2020-08-31 11:44:12 +08:00
xrkffgg
5f6470d8f3 style: optimize col rtl style (#26482) 2020-08-29 22:06:07 +08:00
二货机器人
88ae028321 fix: Upload controlled issue (#26481)
* fix upload sync

* add test case

* rm only

* update snapshot

* fix test case
2020-08-29 21:28:44 +08:00
Moein Alizadeh
6b90477b53 fix: rlt style of columns (#26479)
* Fix rlt issue for columns

This fix should exist in rtl styles.

* Update rtl.less
2020-08-29 19:33:28 +08:00
xrkffgg
bf72f5538a docs: update api naming 2020-08-29 15:25:09 +08:00
Kaan KÜÇÜK
7e223c1edc Update index.en-US.md (#26476)
Type for Checkbox.Group Options is not valid on docs.
2020-08-29 12:11:22 +08:00
Rainy
221a38db91 fix: no shadow in the style of the Select focus. (#26465)
Fix #26255 style error.
2020-08-28 23:08:33 +08:00
xrkffgg
ae650485a0 docs: optimize site menu sort (#26471) 2020-08-28 22:55:16 +08:00
xrkffgg
2005546d22 test: update snap (#26467) 2020-08-28 21:43:44 +08:00
zhangchen
c47bfb0c91 fix: Table topPaginationNode display logic should same with bottomPaginationNode (#26143)
* fix: Tree bottomPaginationNode display logic should same with bottomPaginationNode

* update: test
2020-08-28 15:48:19 +08:00
二货机器人
77cbf724cf fix: TimePicker.RangePicker props (#26446) 2020-08-28 11:46:35 +08:00
二货机器人
d561d73614 fix: Space should support fragement (#26444) 2020-08-28 11:29:37 +08:00
baidumap
3507a01798 fix: 修复disabledTime参数文档 (#26439)
* fix: 修复disabledTime参数文档

* fix: 英文版dates -> date

Co-authored-by: jingruoyu <jingruoyu@xiaomi.com>
2020-08-27 21:15:52 +08:00
Arvin Xu
948ba88629 docs: update image cover (#26431)
* docs: update image cover

* docs: update image cover
2020-08-27 17:02:02 +08:00
ZHANGYU
dd547aabfd docs: fix Tree prop autoExpandParent default value (#26426) 2020-08-27 11:45:10 +08:00
二货机器人
a6180008c5 Revert "fix: ref lost in Radio.Group (#25328)" (#26418)
This reverts commit 0a8641fc02.
2020-08-26 21:36:21 +08:00
Ben Callaway
852fad9e3b fix: Add missing properties to ShowUploadListInterface (#26406) 2020-08-26 18:43:34 +08:00
xrkffgg
008dc3e7f5 docs: update select demo and site (#26417) 2020-08-26 17:50:14 +08:00
偏右
c53e8ac4c9 test: add test cases to increase coverage (#26410) 2020-08-26 16:42:47 +08:00
偏右
e11da52bf7 fix: 🐛 Table Expand Icon size issue (#26409)
* - https://github.com/ant-design/ant-design/issues/24719
Expand Icon

* - https://github.com/ant-design/ant-design/issues/24719
Expand Icon

Co-authored-by: Gary Wei <wgz@tip-lab.com>
2020-08-26 13:43:48 +08:00
zhangchen
453eafe68d fix: Badge not work when props.color is empty (#26375)
* fix: Badge not work when props.color is empty

* update: test
2020-08-26 11:27:07 +08:00
xishanxu
507fdc44ae docs: fix typo in demo (#26391)
* Update extra.md

* Update extra.md
2020-08-26 11:09:14 +08:00
xrkffgg
73f4b9bcd7 docs: complete expandedRowClassName (#26397) 2020-08-25 22:54:29 +08:00
偏右
7274a2ef4a fix: colorPalette is not defined when use theme (#26395)
close #26290
2020-08-25 22:54:04 +08:00
Bosseskompis
19606b375c docs: Add expandedRowClassName to Table.expandable (#26392)
* Add expandedRowClassName to Table.expandable docs

* Use alphabetical order
2020-08-25 20:26:04 +08:00
二货机器人
875e190ac7 fix: Space preserve empty dom when children is null (#26389)
* fix: Space additional dom render

* test case
2020-08-25 16:43:52 +08:00
偏右
fb839f096c fix: Tree draggable transition style (#26387)
close #26383
2020-08-25 15:55:10 +08:00
偏右
e25f03206c docs: 📝 fix typo 2020-08-25 13:55:44 +08:00
afc163
92ab198198 docs: improve changelog 2020-08-25 13:32:19 +08:00
zombiej
04d7a81ff7 docs: Update Form FAQ 2020-08-25 11:27:01 +08:00
afc163
902f5617db docs: improve changelog 2020-08-25 10:51:58 +08:00
偏右
24a86df257 style: 💄 fix Upload picture-card button style (#26367)
* style: fix Upload picture-card button style

close https://github.com/ant-design/ant-design/issues/26317
close #23339

* style: tweak Upload list animation

close #22386

* fix snapshot change

* docs: update Upload demos

* add test case

* update form demo

* fix test case
2020-08-24 16:33:24 +08:00
偏右
fbef76e943 test: add more test cases (#26362) 2020-08-24 11:50:53 +08:00
信鑫-King
9b6db94fdf chore: esbuild target (#26357)
* chore: esbuild target

* Update bisheng.config.js

* fix lint

Co-authored-by: 二货机器人 <smith3816@gmail.com>
2020-08-24 11:46:21 +08:00
二货机器人
54b6bab2bf docs: 4.6.1 changelog (#26356) 2020-08-24 10:48:35 +08:00
偏右
d5eda4f87e test: add more test cases (#26350)
* test: add more test to Layout

* test: add more test to DatePicker

* refactor: remove unused code in Menu
2020-08-24 10:09:57 +08:00
Kermit Xuan
d8215589de fix(upload): props type declaration (#26347)
* fix(upload): props  type declaration

* fix lint

* chore: add type test case
2020-08-23 22:46:55 +08:00
98 changed files with 12394 additions and 6877 deletions

View File

@@ -15,10 +15,41 @@ timeline: true
---
## 4.6.2
`2020-08-31`
- Upload
- 🐞 Fix Upload list status issue when in control mode. [#26481](https://github.com/ant-design/ant-design/pull/26481)
- 💄 Fix Upload `picture-card` style unexpected margin in Form.Item. [#26367](https://github.com/ant-design/ant-design/pull/26367)
- 💄 Fix Select focus shadow style. [#26465](https://github.com/ant-design/ant-design/pull/26465) [@Rainy](https://github.com/Rainy)
- Table
- 🐞 Fix Table Pagination not hide with empty data when show on top position. [#26143](https://github.com/ant-design/ant-design/pull/26143) [@zhangchen915](https://github.com/zhangchen915)
- 💄 Fix Table expand icon size issue when `@font-size-base` is `12px`. [#26409](https://github.com/ant-design/ant-design/pull/26409)
- Space
- 🐞 Fix Space not support React.Fragment issue. [#26444](https://github.com/ant-design/ant-design/pull/26444)
- 🐞 Fix Space preserve empty dom node when `children` contains empty node. [#26389](https://github.com/ant-design/ant-design/pull/26389)
- 🐞 Fix Badge not work when `status` or `color` is empty. [#26375](https://github.com/ant-design/ant-design/pull/26375) [@zhangchen915](https://github.com/zhangchen915)
- 💄 Fix Tree draggable transition style. [#26387](https://github.com/ant-design/ant-design/pull/26387)
- 🐞 Fix `colorPalette is not defined` when customize theme in some situation. [#26395](https://github.com/ant-design/ant-design/pull/26395)
- TypeScript
- 🐞 Fix TimePicker.RangePicker typescript need `picker` issue. [#26446](https://github.com/ant-design/ant-design/pull/26446)
- 🐞 Upload extended `showUploadList` of Upload with `removeIcon` and `downloadIcon` properties. [#26406](https://github.com/ant-design/ant-design/pull/26406) [@bencallaway](https://github.com/bencallaway)
- RTL
- 🐞 Fix the rtl style of Col. [#26479](https://github.com/ant-design/ant-design/pull/26479) [#26482](https://github.com/ant-design/ant-design/pull/26482) [@TrueMoein](https://github.com/TrueMoein)
## 4.6.1
`2020-08-24`
- TypeScript
- 🐞 Fix Upload type declaration missing `children`. [#26347](https://github.com/ant-design/ant-design/pull/26347)
## 4.6.0
`2020-08-23`
- 💄 Darker `@text-color` for WCAG 2.0 on contrast ratio. [#25630](https://github.com/ant-design/ant-design/pull/25630)
- 🔥 New Image component. [#26296](https://github.com/ant-design/ant-design/pull/26296)
- 🔥 Table support `sticky` prop to sticky header and scroll bar. [#25939](https://github.com/ant-design/ant-design/pull/25939)
- Form
@@ -56,10 +87,8 @@ timeline: true
- 💄 Optimize the display effect of Descriptions when there is more content. [#25903](https://github.com/ant-design/ant-design/pull/25903)
- 🆕 message could be detroied by `message.desctroy(key)`. [#26052](https://github.com/ant-design/ant-design/pull/26052) [@lihqi](https://github.com/lihqi)
- 💄 Adjust InputNumber handler bar to be hidden when `readOnly`. [#25998](https://github.com/ant-design/ant-design/pull/25998)
- 💄 Lighten `@text-color` color in dark theme. [#25923](https://github.com/ant-design/ant-design/pull/25923)
- 💄 Darken `@text-color` color in default theme. [#25630](https://github.com/ant-design/ant-design/pull/25630)
- Locale
- 🏴󠁥󠁳󠁧󠁡󠁿 Add Galician Locale support. [#26015](https://github.com/ant-design/ant-design/pull/26015) [@barreeeiroo](https://github.com/barreeeiroo)
- 🌐 Add Galician locale support. [#26015](https://github.com/ant-design/ant-design/pull/26015) [@barreeeiroo](https://github.com/barreeeiroo)
- 🇱🇹 Add Lithuanian locale support. [#26312](https://github.com/ant-design/ant-design/pull/26312) [@mslotvinskij](https://github.com/mslotvinskij)
- 🌐 Use `kmr_IQ` to replace `ku_IQ`. [#26030](https://github.com/ant-design/ant-design/pull/26030)
- RTL

View File

@@ -15,10 +15,41 @@ timeline: true
---
## 4.6.2
`2020-08-31`
- Upload
- 🐞 修复 Upload 在受控模式下同步更新导致的状态错误问题。[#26481](https://github.com/ant-design/ant-design/pull/26481)
- 💄 修复 Upload 图片样式在 Form.Item 中有异常 margin 的问题。[#26367](https://github.com/ant-design/ant-design/pull/26367)
- 💄 修复 Select focus 状态样式无阴影。[#26465](https://github.com/ant-design/ant-design/pull/26465) [@Rainy](https://github.com/Rainy)
- Table
- 🐞 修复 Table Pagination 展示于上侧且没有数据时不消失的问题。[#26143](https://github.com/ant-design/ant-design/pull/26143) [@zhangchen915](https://github.com/zhangchen915)
- 💄 修复 Table 展开图标在 `@font-size-base``12px` 时样式错位的问题。[#26409](https://github.com/ant-design/ant-design/pull/26409)
- Space
- 🐞 修复 Space 不支持 React.Fragment 的问题。[#26444](https://github.com/ant-design/ant-design/pull/26444)
- 🐞 修复 Space 在 `children` 中包含空节点时会出现空 dom 的问题。[#26389](https://github.com/ant-design/ant-design/pull/26389)
- 🐞 修复 Badge 在 `status``color` 为空时不展示。[#26375](https://github.com/ant-design/ant-design/pull/26375) [@zhangchen915](https://github.com/zhangchen915)
- 💄 修复 Tree `draggable` 切换时样式 transition 变化的问题。[#26387](https://github.com/ant-design/ant-design/pull/26387)
- 🐞 修复使用主题有时会报 `colorPalette is not defined` 的问题。[#26395](https://github.com/ant-design/ant-design/pull/26395)
- TypeScript
- 🐞 修复 TimePicker.RangePicker 定义需要 `picker` 的问题。[#26446](https://github.com/ant-design/ant-design/pull/26446)
- 🐞 Upload 组件 `showUploadList` 类型添加 `removeIcon``downloadIcon` 属性声明。[#26406](https://github.com/ant-design/ant-design/pull/26406) [@bencallaway](https://github.com/bencallaway)
- RTL
- 🐞 修复 Col RTL 样式。[#26479](https://github.com/ant-design/ant-design/pull/26479) [#26482](https://github.com/ant-design/ant-design/pull/26482) [@TrueMoein](https://github.com/TrueMoein)
## 4.6.1
`2020-08-24`
- TypeScript
- 🐞 修复 Upload 类型声明丢失 `children` 的问题。[#26347](https://github.com/ant-design/ant-design/pull/26347)
## 4.6.0
`2020-08-23`
- 💄 加深默认文本 `@text-color` 以满足 WCAG 2.0 对比度的规范。[#25630](https://github.com/ant-design/ant-design/pull/25630)
- 🔥 新增图片组件 Image。[#26296](https://github.com/ant-design/ant-design/pull/26296)
- 🔥 Table 新增 `sticky` 属性以支持固定表头和滚动条。[#25939](https://github.com/ant-design/ant-design/pull/25939)
- Form
@@ -26,22 +57,22 @@ timeline: true
- 🆕 Form.List 中的 `add` 方法支持第二个 `index` 参数。[#26081](https://github.com/ant-design/ant-design/pull/26081)
- 🆕 虚拟滚动支持无闪动滚动,修复 Select/TreeSelect 滚动时列表空白的问题。[#26306](https://github.com/ant-design/ant-design/pull/26306)
- Typography
- 🆕 新增 Typography.Text success 类型。[#26145](https://github.com/ant-design/ant-design/pull/26145) [@llwslc](https://github.com/llwslc)
- 🆕 新增 Typography.Text `success` 类型。[#26145](https://github.com/ant-design/ant-design/pull/26145) [@llwslc](https://github.com/llwslc)
- 🆕 Typography `copyable` 支持隐藏提示,`editable` 支持设置图标与提示。[#25953](https://github.com/ant-design/ant-design/pull/25953) [@llwslc](https://github.com/llwslc)
- 🆕 新增 Typography.Title 5 级标题。[#25861](https://github.com/ant-design/ant-design/pull/25861)
- 🆕 Typography 的 `editable` 配置中增加了 `maxLength` & `autoSize` 支持。[#25373](https://github.com/ant-design/ant-design/pull/25373) [@CornerSkyless](https://github.com/CornerSkyless)
- 🆕 Typography 的 `editable` 配置中增加了 `maxLength` `autoSize` 属性。[#25373](https://github.com/ant-design/ant-design/pull/25373) [@CornerSkyless](https://github.com/CornerSkyless)
- 🐞 修复 Transfer 搜索空格时 `filterOption` 没有触发的问题。[#26335](https://github.com/ant-design/ant-design/pull/26335)
- Progress
- 🐞 修复 Progress `steps` 属性对于 `trailColor` 不生效的问题。[#26323](https://github.com/ant-design/ant-design/pull/26323)
- 🐞 修复 Progress 当 `type="circle"``success.percent` 不生效的问题。[#26307](https://github.com/ant-design/ant-design/pull/26307)
- 🐞 修复 Textarea 当 `value``undefined` 时未显示 `defaultValue` 问题。[#26327](https://github.com/ant-design/ant-design/pull/26327)
- Cascader
- 🐞 修复 Cascader 在按下 ESC 键,然后通过输入进行搜索时 options 不展开的问题。[#26271](https://github.com/ant-design/ant-design/pull/26271) [@flyerH](https://github.com/flyerH)
- 🐞 修复 Cascader 在按下 ESC 键,然后通过输入进行搜索时 `options` 不展开的问题。[#26271](https://github.com/ant-design/ant-design/pull/26271) [@flyerH](https://github.com/flyerH)
- 💄 优化 Cascader 清除动画效果。[#26186](https://github.com/ant-design/ant-design/pull/26186)
- 🗑 移除遗留的 Button.Group 支持,请使用 Space 代替。[#26260](https://github.com/ant-design/ant-design/pull/26260)
- Select
- 🆕 Select 支持 onClear 属性。[#25907](https://github.com/ant-design/ant-design/pull/25907)
- 🐞 修复 Select mode="tags" 搜索显示两条重复条目的问题。[#25907](https://github.com/ant-design/ant-design/pull/25907)
- 🆕 Select 支持 `onClear` 属性。[#25907](https://github.com/ant-design/ant-design/pull/25907)
- 🐞 修复 Select `mode="tags"` 搜索显示两条重复条目的问题。[#25907](https://github.com/ant-design/ant-design/pull/25907)
- 🐞 修复 Select 聚焦时被禁用的样式异常问题。[#26255](https://github.com/ant-design/ant-design/pull/26255)
- 🐞 修复多选模式的 Select 在 `showArrow` 时图标重叠问题。[#26168](https://github.com/ant-design/ant-design/pull/26168) [@zhangchen915](https://github.com/zhangchen915)
- DatePicker
@@ -56,12 +87,10 @@ timeline: true
- 💄 优化 Descriptions 在内容比较多时的显示效果。[#25903](https://github.com/ant-design/ant-design/pull/25903)
- 🆕 message 支持通过 `message.desctroy(key)` 销毁。[#26052](https://github.com/ant-design/ant-design/pull/26052) [@lihqi](https://github.com/lihqi)
- 💄 调整 InputNumber 操作栏在 `readOnly` 时为隐藏。[#25998](https://github.com/ant-design/ant-design/pull/25998)
- 💄 加深暗色主题下 `@text-color` 颜色。[#25923](https://github.com/ant-design/ant-design/pull/25923)
- 💄 加深默认主题下 `@text-color` 颜色。[#25630](https://github.com/ant-design/ant-design/pull/25630)
- 国际化
- 🏴󠁥󠁳󠁧󠁡󠁿 添加加利西亚语支持。[#26015](https://github.com/ant-design/ant-design/pull/26015) [@barreeeiroo](https://github.com/barreeeiroo)
- 🌐 添加加利西亚语支持。[#26015](https://github.com/ant-design/ant-design/pull/26015) [@barreeeiroo](https://github.com/barreeeiroo)
- 🇱🇹 添加立陶宛语支持。[#26312](https://github.com/ant-design/ant-design/pull/26312) [@mslotvinskij](https://github.com/mslotvinskij)
- 🌐 `kmr_IQ` 替换 `ku_IQ` 缩写。[#26030](https://github.com/ant-design/ant-design/pull/26030)
- 🌐 新增 `kmr_IQ` 语言包用以代替 ku_IQ。[#26030](https://github.com/ant-design/ant-design/pull/26030)
- RTL
- 💄 优化 Tree RTL 模式下连接线的样式。[#26205](https://github.com/ant-design/ant-design/pull/26205)
- 💄 优化 Dropdown RTL 写法避免暗黑模式样式覆盖。[#26206](https://github.com/ant-design/ant-design/pull/26206)

View File

@@ -13,6 +13,7 @@ class AffixMounter extends React.Component<{
offsetBottom?: number;
offsetTop?: number;
onTestUpdatePosition?(): void;
onChange?: () => void;
}> {
private container: HTMLDivElement;
@@ -131,13 +132,15 @@ describe('Affix Render', () => {
it('updatePosition when offsetTop changed', async () => {
document.body.innerHTML = '<div id="mounter" />';
const onChange = jest.fn();
affixMounterWrapper = mount(<AffixMounter offsetTop={0} />, {
affixMounterWrapper = mount(<AffixMounter offsetTop={0} onChange={onChange} />, {
attachTo: document.getElementById('mounter'),
});
await sleep(20);
await movePlaceholder(-100);
expect(onChange).toHaveBeenLastCalledWith(true);
expect(affixMounterWrapper.instance().affix.state.affixStyle?.top).toBe(0);
affixMounterWrapper.setProps({
offsetTop: 10,

View File

@@ -30,7 +30,7 @@ exports[`AutoComplete legacy dataSource should accept react element option 1`] =
<div>
<div
class="ant-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div

View File

@@ -141,6 +141,17 @@ describe('Badge', () => {
expect(wrapper).toMatchSnapshot();
});
it('Badge should work when status/color is empty string', () => {
const wrapper = mount(
<>
<Badge color="" text="text" />
<Badge status="" text="text" />
</>,
);
expect(wrapper.find('.ant-badge')).toHaveLength(2);
});
it('render Badge size when contains children', () => {
const wrapper = render(
<>

View File

@@ -62,9 +62,7 @@ const Badge: CompoundedComponent = ({
return displayCount as string | number | null;
};
const hasStatus = (): boolean => {
return !!status || !!color;
};
const hasStatus = (): boolean => (status !== null && status !== undefined) || (color !== null && color !== undefined);
const isZero = () => {
const numberedDisplayCount = getNumberedDisplayCount();

View File

@@ -29,11 +29,13 @@ describe('Calendar', () => {
it('Calendar should be selectable', () => {
const onSelect = jest.fn();
const wrapper = mount(<Calendar onSelect={onSelect} />);
const onChange = jest.fn();
const wrapper = mount(<Calendar onSelect={onSelect} onChange={onChange} />);
wrapper.find('.ant-picker-cell').at(0).simulate('click');
expect(onSelect).toHaveBeenCalledWith(expect.anything());
const value = onSelect.mock.calls[0][0];
expect(Moment.isMoment(value)).toBe(true);
expect(onChange).toHaveBeenCalled();
});
it('only Valid range should be selectable', () => {

View File

@@ -4,7 +4,7 @@ exports[`Cascader can be selected 1`] = `
<div>
<div
class="ant-cascader-menus"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<ul
@@ -117,7 +117,7 @@ exports[`Cascader can be selected 2`] = `
<div>
<div
class="ant-cascader-menus"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<ul
@@ -364,7 +364,7 @@ exports[`Cascader can be selected in RTL direction 1`] = `
<div>
<div
class="ant-cascader-menus ant-cascader-menu-rtl"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<ul
@@ -477,7 +477,7 @@ exports[`Cascader can be selected in RTL direction 2`] = `
<div>
<div
class="ant-cascader-menus ant-cascader-menu-rtl"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<ul
@@ -601,7 +601,7 @@ exports[`Cascader have a notFoundContent that fit trigger input width 1`] = `
<div>
<div
class="ant-cascader-menus"
style="opacity: 0;"
style="opacity: 0; pointer-events: none;"
>
<div>
<ul
@@ -679,7 +679,7 @@ exports[`Cascader popup correctly when panel is open 1`] = `
<div>
<div
class="ant-cascader-menus"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<ul
@@ -757,7 +757,7 @@ exports[`Cascader popup correctly with defaultValue 1`] = `
<div>
<div
class="ant-cascader-menus"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<ul
@@ -881,7 +881,7 @@ exports[`Cascader popup correctly with defaultValue RTL 1`] = `
<div>
<div
class="ant-cascader-menus ant-cascader-menu-rtl"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<ul
@@ -1133,6 +1133,7 @@ exports[`Cascader should highlight keyword and filter when search in Cascader 1`
style={
Object {
"opacity": 0,
"pointerEvents": "none",
"zIndex": undefined,
}
}
@@ -1144,6 +1145,7 @@ exports[`Cascader should highlight keyword and filter when search in Cascader 1`
style={
Object {
"opacity": 0,
"pointerEvents": "none",
"zIndex": undefined,
}
}
@@ -1442,7 +1444,7 @@ exports[`Cascader should highlight keyword and filter when search in Cascader wi
<div>
<div
class="ant-cascader-menus"
style="opacity: 0;"
style="opacity: 0; pointer-events: none;"
>
<div>
<ul
@@ -1573,6 +1575,7 @@ exports[`Cascader should render not found content 1`] = `
style={
Object {
"opacity": 0,
"pointerEvents": "none",
"zIndex": undefined,
}
}
@@ -1584,6 +1587,7 @@ exports[`Cascader should render not found content 1`] = `
style={
Object {
"opacity": 0,
"pointerEvents": "none",
"zIndex": undefined,
}
}
@@ -1894,6 +1898,7 @@ exports[`Cascader should show not found content when options.length is 0 1`] = `
style={
Object {
"opacity": 0,
"pointerEvents": "none",
"zIndex": undefined,
}
}
@@ -1905,6 +1910,7 @@ exports[`Cascader should show not found content when options.length is 0 1`] = `
style={
Object {
"opacity": 0,
"pointerEvents": "none",
"zIndex": undefined,
}
}

View File

@@ -34,7 +34,7 @@ Checkbox component.
| defaultValue | Default selected value | string\[] | \[] | |
| disabled | If disable all checkboxes | boolean | false | |
| name | The `name` property of all `input[type="checkbox"]` children | string | - | |
| options | Specifies options | string\[] | \[] | |
| options | Specifies options | string\[] \| Option\[] | \[] | |
| value | Used for setting the currently selected value | string\[] | \[] | |
| onChange | The callback function that is triggered when the state changes | function(checkedValue) | - | |

View File

@@ -18826,7 +18826,7 @@ exports[`ConfigProvider components Popconfirm configProvider 1`] = `
<div>
<div
class="config-popover config-popconfirm"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-popover-content"
@@ -18910,7 +18910,7 @@ exports[`ConfigProvider components Popconfirm configProvider componentSize large
<div>
<div
class="config-popover config-popconfirm"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-popover-content"
@@ -18994,7 +18994,7 @@ exports[`ConfigProvider components Popconfirm configProvider componentSize middl
<div>
<div
class="config-popover config-popconfirm"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-popover-content"
@@ -19078,7 +19078,7 @@ exports[`ConfigProvider components Popconfirm configProvider virtual and dropdow
<div>
<div
class="ant-popover ant-popconfirm"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-popover-content"
@@ -19162,7 +19162,7 @@ exports[`ConfigProvider components Popconfirm normal 1`] = `
<div>
<div
class="ant-popover ant-popconfirm"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-popover-content"
@@ -19246,7 +19246,7 @@ exports[`ConfigProvider components Popconfirm prefixCls 1`] = `
<div>
<div
class="prefix-Popconfirm prefix-Popconfirm"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="prefix-Popconfirm-content"
@@ -19330,7 +19330,7 @@ exports[`ConfigProvider components Popover configProvider 1`] = `
<div>
<div
class="config-popover"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-popover-content"
@@ -19366,7 +19366,7 @@ exports[`ConfigProvider components Popover configProvider componentSize large 1`
<div>
<div
class="config-popover"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-popover-content"
@@ -19402,7 +19402,7 @@ exports[`ConfigProvider components Popover configProvider componentSize middle 1
<div>
<div
class="config-popover"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-popover-content"
@@ -19438,7 +19438,7 @@ exports[`ConfigProvider components Popover configProvider virtual and dropdownMa
<div>
<div
class="ant-popover"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-popover-content"
@@ -19474,7 +19474,7 @@ exports[`ConfigProvider components Popover normal 1`] = `
<div>
<div
class="ant-popover"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-popover-content"
@@ -19510,7 +19510,7 @@ exports[`ConfigProvider components Popover prefixCls 1`] = `
<div>
<div
class="prefix-Popover"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="prefix-Popover-content"
@@ -21879,7 +21879,7 @@ exports[`ConfigProvider components Select configProvider 1`] = `
<div>
<div
class="config-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div
@@ -22005,7 +22005,7 @@ exports[`ConfigProvider components Select configProvider componentSize large 1`]
<div>
<div
class="config-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div
@@ -22131,7 +22131,7 @@ exports[`ConfigProvider components Select configProvider componentSize middle 1`
<div>
<div
class="config-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div
@@ -22257,7 +22257,7 @@ exports[`ConfigProvider components Select configProvider virtual and dropdownMat
<div>
<div
class="ant-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div
@@ -22383,7 +22383,7 @@ exports[`ConfigProvider components Select normal 1`] = `
<div>
<div
class="ant-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div
@@ -22509,7 +22509,7 @@ exports[`ConfigProvider components Select prefixCls 1`] = `
<div>
<div
class="prefix-Select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div
@@ -22797,7 +22797,7 @@ exports[`ConfigProvider components Slider configProvider 1`] = `
<div>
<div
class="config-tooltip config-slider-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-tooltip-content"
@@ -22851,7 +22851,7 @@ exports[`ConfigProvider components Slider configProvider componentSize large 1`]
<div>
<div
class="config-tooltip config-slider-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-tooltip-content"
@@ -22905,7 +22905,7 @@ exports[`ConfigProvider components Slider configProvider componentSize middle 1`
<div>
<div
class="config-tooltip config-slider-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-tooltip-content"
@@ -22959,7 +22959,7 @@ exports[`ConfigProvider components Slider configProvider virtual and dropdownMat
<div>
<div
class="ant-tooltip ant-slider-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
@@ -23013,7 +23013,7 @@ exports[`ConfigProvider components Slider normal 1`] = `
<div>
<div
class="ant-tooltip ant-slider-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
@@ -23067,7 +23067,7 @@ exports[`ConfigProvider components Slider prefixCls 1`] = `
<div>
<div
class="prefix-Slider-tooltip prefix-Slider-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="prefix-Slider-tooltip-content"
@@ -23822,7 +23822,7 @@ exports[`ConfigProvider components Table configProvider 1`] = `
<div>
<div
class="config-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-table-filter-dropdown"
@@ -24096,7 +24096,7 @@ exports[`ConfigProvider components Table configProvider componentSize large 1`]
<div>
<div
class="config-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-table-filter-dropdown"
@@ -24370,7 +24370,7 @@ exports[`ConfigProvider components Table configProvider componentSize middle 1`]
<div>
<div
class="config-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-table-filter-dropdown"
@@ -24644,7 +24644,7 @@ exports[`ConfigProvider components Table configProvider virtual and dropdownMatc
<div>
<div
class="ant-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-table-filter-dropdown"
@@ -24918,7 +24918,7 @@ exports[`ConfigProvider components Table normal 1`] = `
<div>
<div
class="ant-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-table-filter-dropdown"
@@ -25192,7 +25192,7 @@ exports[`ConfigProvider components Table prefixCls 1`] = `
<div>
<div
class="ant-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="prefix-Table-filter-dropdown"
@@ -25992,7 +25992,7 @@ Array [
<div>
<div
class="config-picker-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-picker-panel-container"
@@ -27404,7 +27404,7 @@ Array [
<div>
<div
class="config-picker-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-picker-panel-container"
@@ -28816,7 +28816,7 @@ Array [
<div>
<div
class="config-picker-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-picker-panel-container"
@@ -30228,7 +30228,7 @@ Array [
<div>
<div
class="ant-picker-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-picker-panel-container"
@@ -31640,7 +31640,7 @@ Array [
<div>
<div
class="ant-picker-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-picker-panel-container"
@@ -33052,7 +33052,7 @@ Array [
<div>
<div
class="prefix-TimePicker-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="prefix-TimePicker-panel-container"
@@ -34558,7 +34558,7 @@ Array [
<div>
<div
class="config-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-tooltip-content"
@@ -34592,7 +34592,7 @@ Array [
<div>
<div
class="config-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-tooltip-content"
@@ -34626,7 +34626,7 @@ Array [
<div>
<div
class="config-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="config-tooltip-content"
@@ -34660,7 +34660,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
@@ -34694,7 +34694,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
@@ -34728,7 +34728,7 @@ Array [
<div>
<div
class="prefix-Tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="prefix-Tooltip-content"
@@ -37149,7 +37149,7 @@ exports[`ConfigProvider components TreeSelect configProvider 1`] = `
<div>
<div
class="config-select-dropdown config-tree-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div>
@@ -37267,7 +37267,7 @@ exports[`ConfigProvider components TreeSelect configProvider componentSize large
<div>
<div
class="config-select-dropdown config-tree-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div>
@@ -37385,7 +37385,7 @@ exports[`ConfigProvider components TreeSelect configProvider componentSize middl
<div>
<div
class="config-select-dropdown config-tree-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div>
@@ -37503,7 +37503,7 @@ exports[`ConfigProvider components TreeSelect configProvider virtual and dropdow
<div>
<div
class="ant-select-dropdown ant-tree-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div>
@@ -37621,7 +37621,7 @@ exports[`ConfigProvider components TreeSelect normal 1`] = `
<div>
<div
class="ant-select-dropdown ant-tree-select-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div>
@@ -37739,7 +37739,7 @@ exports[`ConfigProvider components TreeSelect prefixCls 1`] = `
<div>
<div
class="prefix-TreeSelect-dropdown prefix-TreeSelect-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
<div>

View File

@@ -78,4 +78,106 @@ describe('DatePicker', () => {
const wrapper = mount(<DatePicker placeholder={undefined} />);
expect(wrapper.find('input').props().placeholder).toEqual('Select date');
});
it('showTime={{ showHour: true, showMinute: true }}', () => {
const wrapper = mount(
<DatePicker
defaultValue={moment()}
showTime={{ showHour: true, showMinute: true }}
format="YYYY-MM-DD"
open
/>,
);
expect(wrapper.find('.ant-picker-time-panel-column').length).toBe(2);
expect(
wrapper.find('.ant-picker-time-panel-column').at(0).find('.ant-picker-time-panel-cell')
.length,
).toBe(24);
expect(
wrapper.find('.ant-picker-time-panel-column').at(1).find('.ant-picker-time-panel-cell')
.length,
).toBe(60);
});
it('showTime={{ showHour: true, showSecond: true }}', () => {
const wrapper = mount(
<DatePicker
defaultValue={moment()}
showTime={{ showHour: true, showSecond: true }}
format="YYYY-MM-DD"
open
/>,
);
expect(wrapper.find('.ant-picker-time-panel-column').length).toBe(2);
expect(
wrapper.find('.ant-picker-time-panel-column').at(0).find('.ant-picker-time-panel-cell')
.length,
).toBe(24);
expect(
wrapper.find('.ant-picker-time-panel-column').at(1).find('.ant-picker-time-panel-cell')
.length,
).toBe(60);
});
it('showTime={{ showMinute: true, showSecond: true }}', () => {
const wrapper = mount(
<DatePicker
defaultValue={moment()}
showTime={{ showMinute: true, showSecond: true }}
format="YYYY-MM-DD"
open
/>,
);
expect(wrapper.find('.ant-picker-time-panel-column').length).toBe(2);
expect(
wrapper.find('.ant-picker-time-panel-column').at(0).find('.ant-picker-time-panel-cell')
.length,
).toBe(60);
expect(
wrapper.find('.ant-picker-time-panel-column').at(1).find('.ant-picker-time-panel-cell')
.length,
).toBe(60);
});
it('12 hours', () => {
const wrapper = mount(
<DatePicker defaultValue={moment()} showTime format="YYYY-MM-DD HH:mm:ss A" open />,
);
expect(wrapper.find('.ant-picker-time-panel-column').length).toBe(4);
expect(
wrapper.find('.ant-picker-time-panel-column').at(0).find('.ant-picker-time-panel-cell')
.length,
).toBe(12);
expect(
wrapper.find('.ant-picker-time-panel-column').at(1).find('.ant-picker-time-panel-cell')
.length,
).toBe(60);
expect(
wrapper.find('.ant-picker-time-panel-column').at(2).find('.ant-picker-time-panel-cell')
.length,
).toBe(60);
expect(
wrapper.find('.ant-picker-time-panel-column').at(3).find('.ant-picker-time-panel-cell')
.length,
).toBe(2);
});
it('24 hours', () => {
const wrapper = mount(
<DatePicker defaultValue={moment()} showTime format="YYYY-MM-DD HH:mm:ss" open />,
);
expect(wrapper.find('.ant-picker-time-panel-column').length).toBe(3);
expect(
wrapper.find('.ant-picker-time-panel-column').at(0).find('.ant-picker-time-panel-cell')
.length,
).toBe(24);
expect(
wrapper.find('.ant-picker-time-panel-column').at(1).find('.ant-picker-time-panel-cell')
.length,
).toBe(60);
expect(
wrapper.find('.ant-picker-time-panel-column').at(2).find('.ant-picker-time-panel-cell')
.length,
).toBe(60);
});
});

View File

@@ -45,7 +45,7 @@ Array [
<div>
<div
class="ant-picker-dropdown"
style="opacity: 0;"
style="opacity: 0; pointer-events: none;"
>
<div
class="ant-picker-panel-container"
@@ -669,7 +669,7 @@ Array [
<div>
<div
class="ant-picker-dropdown"
style="opacity: 0;"
style="opacity: 0; pointer-events: none;"
>
<div
class="ant-picker-panel-container"

View File

@@ -150,7 +150,7 @@ Array [
<div>
<div
class="ant-picker-dropdown ant-picker-dropdown-range"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-picker-range-wrapper ant-picker-date-range-wrapper"

View File

@@ -4,7 +4,7 @@ exports[`MonthPicker and WeekPicker render MonthPicker 1`] = `
<div>
<div
class="ant-picker-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-picker-panel-container"
@@ -197,7 +197,7 @@ exports[`MonthPicker and WeekPicker render WeekPicker 1`] = `
<div>
<div
class="ant-picker-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-picker-panel-container"

View File

@@ -156,7 +156,7 @@ Added in `4.1.0`.
| defaultValue | To set default date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - | |
| defaultPickerValue | To set default picker date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)\] | - | |
| disabled | If disable start or end | \[boolean, boolean] | - | |
| disabledTime | To specify the time that cannot be selected | function(dates: \[moment, moment], partial: `start` \| `end`) | - | |
| disabledTime | To specify the time that cannot be selected | function(date: moment, partial: `start` \| `end`) | - | |
| format | To set the date format, refer to [moment.js](http://momentjs.com/). When an array is provided, all values are used for parsing and first value is used for formatting | string \| string[] | `YYYY-MM-DD HH:mm:ss` | |
| ranges | The preseted ranges for quick selection | { \[range: string]: [moment](http://momentjs.com/)\[] } \| { \[range: string]: () => [moment](http://momentjs.com/)\[] } | - | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |

View File

@@ -157,7 +157,7 @@ import locale from 'antd/es/locale/zh_CN';
| defaultValue | 默认日期 | [moment](http://momentjs.com/)\[] | - | |
| defaultPickerValue | 默认面板日期 | [moment](http://momentjs.com/)\[] | - | |
| disabled | 禁用起始项 | \[boolean, boolean] | - | |
| disabledTime | 不可选择的时间 | function(dates: \[moment, moment\], partial: `start` \| `end`) | - | |
| disabledTime | 不可选择的时间 | function(date: moment, partial: `start` \| `end`) | - | |
| format | 展示的日期格式 | string | `YYYY-MM-DD HH:mm:ss` | |
| ranges | 预设时间范围快捷选择 | { \[range: string]: [moment](http://momentjs.com/)\[] } \| { \[range: string]: () => [moment](http://momentjs.com/)\[] } | - | |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |

View File

@@ -11,7 +11,7 @@ Array [
<div>
<div
class="ant-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div>
menu
@@ -32,7 +32,7 @@ Array [
<div>
<div
class="ant-dropdown"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<span>
string

View File

@@ -6055,7 +6055,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
</svg>
</span>
<span>
Click to upload
Click to upload
</span>
</button>
</span>

View File

@@ -177,9 +177,7 @@ const Demo = () => {
extra="longgggggggggggggggggggggggggggggggggg"
>
<Upload name="logo" action="/upload.do" listType="picture">
<Button>
<UploadOutlined /> Click to upload
</Button>
<Button icon={<UploadOutlined />}>Click to upload</Button>
</Upload>
</Form.Item>

View File

@@ -338,6 +338,10 @@ Before Modal opens, children elements do not exist in the view. You can set `for
Components inside Form.Item with name property will turn into controlled mode, which makes `defaultValue` not work anymore. Please try `initialValues` of Form to set default value.
### Why can not call `ref` of From at first time?
`ref` only receives the mounted instance. please ref React official doc: https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs
### Why `resetFields` will re-mount component?
`resetFields` will re-mount component under Field to clean up customize component side effects (like async data, cached state, etc.). It's by design.

View File

@@ -340,6 +340,10 @@ validator(rule, value, callback) => {
当你为 Form.Item 设置 `name` 属性后,子组件会转为受控模式。因而 `defaultValue` 不会生效。你需要在 Form 上通过 `initialValues` 设置默认值。
### 为什么第一次调用 `ref` 的 From 为空?
`ref` 仅在节点被加载时才会被赋值,请参考 React 官方文档https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs
### 为什么 `resetFields` 会重新 mount 组件?
`resetFields` 会重置整个 Field因而其子组件也会重新 mount 从而消除自定义组件可能存在的副作用(例如异步数据、状态等等)。

View File

@@ -7,13 +7,6 @@
}
}
.@{ant-prefix}-col {
&&-rtl {
float: right;
width: 100%;
}
}
// mixin
.loop-grid-columns(@index, @class) when (@index > 0) {
.@{ant-prefix}-col@{class}-push-@{index} {

View File

@@ -3,7 +3,7 @@ category: Components
type: Data Display
title: Image
cols: 2
cover: https://gw.alipayobjects.com/zos/bmw-prod/a77ae075-27de-49c9-9f44-a505f2be07fa.svg
cover: https://gw.alipayobjects.com/zos/antfincdn/D1dXz9PZqa/image.svg
---
Previewable image.

View File

@@ -4,7 +4,7 @@ subtitle: 图片
type: 数据展示
title: Image
cols: 2
cover: https://gw.alipayobjects.com/zos/bmw-prod/a77ae075-27de-49c9-9f44-a505f2be07fa.svg
cover: https://gw.alipayobjects.com/zos/antfincdn/D1dXz9PZqa/image.svg
---
可预览的图片。

View File

@@ -112,28 +112,22 @@ class InternalSider extends React.Component<InternalSideProps, SiderState> {
this.responsiveHandler(this.mql);
}
if (this.props.siderHook) {
this.props.siderHook.addSider(this.uniqueId);
}
this.props?.siderHook.addSider(this.uniqueId);
}
componentWillUnmount() {
if (this.mql) {
this.mql.removeListener(this.responsiveHandler as any);
}
if (this.props.siderHook) {
this.props.siderHook.removeSider(this.uniqueId);
}
this?.mql?.removeListener(this.responsiveHandler as any);
this.props?.siderHook.removeSider(this.uniqueId);
}
responsiveHandler = (mql: MediaQueryListEvent | MediaQueryList) => {
this.setState({ below: mql.matches });
const { onBreakpoint } = this.props;
const { collapsed } = this.state;
if (onBreakpoint) {
onBreakpoint(mql.matches);
}
if (this.state.collapsed !== mql.matches) {
if (collapsed !== mql.matches) {
this.setCollapsed(mql.matches, 'responsive');
}
};

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { mount, render } from 'enzyme';
import Layout from '..';
import Icon from '../../icon';
@@ -14,7 +14,7 @@ describe('Layout', () => {
mountTest(Sider);
mountTest(() => (
<Layout>
<Sider />
<Sider breakpoint="xs" />
<Content />
</Layout>
));
@@ -23,7 +23,7 @@ describe('Layout', () => {
rtlTest(Content);
rtlTest(Sider);
it('detect the sider as children', async () => {
it('detect the sider as children', () => {
const wrapper = mount(
<Layout>
<Sider>Sider</Sider>
@@ -31,6 +31,34 @@ describe('Layout', () => {
</Layout>,
);
expect(wrapper.find('.ant-layout').hasClass('ant-layout-has-sider')).toBe(true);
wrapper.unmount();
});
it('umount from multiple siders', async () => {
const App = () => {
const [hide1, setHide1] = useState(false);
const [hide2, setHide2] = useState(false);
return (
<Layout>
{hide1 ? null : <Sider>Sider</Sider>}
{hide2 ? null : <Sider>Sider</Sider>}
<Content>
<button onClick={() => setHide1(true)} type="button">
hide sider 1
</button>
<button onClick={() => setHide2(true)} type="button">
hide sider 2
</button>
</Content>
</Layout>
);
};
const wrapper = mount(<App />);
expect(wrapper.find('.ant-layout').hasClass('ant-layout-has-sider')).toBe(true);
wrapper.find('button').at(0).simulate('click');
expect(wrapper.find('.ant-layout').hasClass('ant-layout-has-sider')).toBe(true);
wrapper.find('button').at(1).simulate('click');
expect(wrapper.find('.ant-layout').hasClass('ant-layout-has-sider')).toBe(false);
});
it('detect the sider inside the children', async () => {

View File

@@ -396,7 +396,7 @@ exports[`List.pagination should change page size work 2`] = `
<div>
<div
class="ant-select-dropdown"
style="min-width: 0; opacity: 0;"
style="min-width: 0; opacity: 0; pointer-events: none;"
>
<div>
<div

View File

@@ -46,6 +46,7 @@ import jaJP from '../ja_JP';
import knIN from '../kn_IN';
import koKR from '../ko_KR';
import kmrIQ from '../kmr_IQ';
import kuIQ from '../ku_IQ';
import lvLV from '../lv_LV';
import ltLT from '../lt_LT';
import mkMK from '../mk_MK';
@@ -103,6 +104,7 @@ const locales = [
knIN,
koKR,
kmrIQ,
kuIQ,
ltLT,
mkMK,
msMY,

View File

@@ -16,16 +16,6 @@ export interface MenuItemProps extends Omit<RcMenuItemProps, 'title'> {
export default class MenuItem extends React.Component<MenuItemProps> {
static isMenuItem = true;
private menuItem: this;
onKeyDown = (e: React.MouseEvent<HTMLElement>) => {
this.menuItem.onKeyDown(e);
};
saveMenuItem = (menuItem: this) => {
this.menuItem = menuItem;
};
renderItemChildren(inlineCollapsed: boolean) {
const { icon, children, level, rootPrefixCls } = this.props;
// inline-collapsed.md demo 依赖 span 来隐藏文字,有 icon 属性,则内部包裹一个 span
@@ -79,7 +69,6 @@ export default class MenuItem extends React.Component<MenuItemProps> {
(icon ? childrenLength + 1 : childrenLength) === 1,
})}
title={title}
ref={this.saveMenuItem}
>
{icon}
{this.renderItemChildren(inlineCollapsed)}

View File

@@ -31,16 +31,6 @@ class SubMenu extends React.Component<SubMenuProps, any> {
// fix issue:https://github.com/ant-design/ant-design/issues/8666
static isSubMenu = 1;
private subMenu: any;
onKeyDown = (e: React.MouseEvent<HTMLElement>) => {
this.subMenu.onKeyDown(e);
};
saveSubMenu = (subMenu: any) => {
this.subMenu = subMenu;
};
renderTitle(inlineCollapsed: boolean) {
const { icon, title, level, rootPrefixCls } = this.props;
if (!icon) {
@@ -69,7 +59,6 @@ class SubMenu extends React.Component<SubMenuProps, any> {
<RcSubMenu
{...omit(this.props, ['icon'])}
title={this.renderTitle(inlineCollapsed)}
ref={this.saveSubMenu}
popupClassName={classNames(
rootPrefixCls,
`${rootPrefixCls}-${antdMenuTheme}`,

View File

@@ -1,3 +1,4 @@
import TestUtils from 'react-dom/test-utils';
import Modal from '..';
import { destroyFns } from '../Modal';
@@ -140,6 +141,42 @@ describe('Modal.confirm triggers callbacks correctly', () => {
jest.useRealTimers();
});
it('should close confirm modal when click cancel button', () => {
jest.useFakeTimers();
const onCancel = jest.fn();
Modal.confirm({
title: 'title',
content: 'content',
onCancel,
});
jest.runAllTimers();
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(1);
$$('.ant-btn')[0].click();
jest.runAllTimers();
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(0);
expect(onCancel).toHaveBeenCalledTimes(1);
jest.useRealTimers();
});
it('should close confirm modal when press ESC', () => {
jest.useFakeTimers();
const onCancel = jest.fn();
Modal.confirm({
title: 'title',
content: 'content',
onCancel,
});
jest.runAllTimers();
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(1);
TestUtils.Simulate.keyDown($$('.ant-modal')[0], {
keyCode: 27,
});
jest.runAllTimers();
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(0);
expect(onCancel).toHaveBeenCalledTimes(1);
jest.useRealTimers();
});
it('should not close modals when click confirm button when onOk has argument', () => {
jest.useFakeTimers();
['info', 'success', 'warning', 'error'].forEach(type => {

View File

@@ -18,6 +18,7 @@ When requiring users to interact with the application, but without jumping to a
| afterClose | Specify a function that will be called when modal is closed completely | function | - |
| bodyStyle | Body style for modal body element. Such as height, padding etc | CSSProperties | {} |
| cancelText | Text of the Cancel button | string \| ReactNode | `Cancel` |
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#API) | - |
| centered | Centered Modal | boolean | false |
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | true |
| closeIcon | Custom close icon | ReactNode | &lt;CloseOutlined /> |
@@ -26,21 +27,21 @@ When requiring users to interact with the application, but without jumping to a
| footer | Footer content, set as `footer={null}` when you don't need default buttons | string \| ReactNode | (OK and Cancel buttons) |
| forceRender | Force render Modal | boolean | false |
| getContainer | Return the mount node for Modal | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body |
| keyboard | Whether support press esc to close | boolean | true |
| mask | Whether show mask or not | boolean | true |
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | true |
| maskStyle | Style for modal's mask element | object | {} |
| okButtonProps | The ok button props | [ButtonProps](/components/button/#API) | - |
| okText | Text of the OK button | string \| ReactNode | `OK` |
| okType | Button `type` of the OK button | string | `primary` |
| okButtonProps | The ok button props | [ButtonProps](/components/button/#API) | - |
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#API) | - |
| onCancel | Specify a function that will be called when a user clicks mask, close button on top right or Cancel button | function(e) | - |
| onOk | Specify a function that will be called when a user clicks the OK button | function(e) | - |
| style | Style of floating layer, typically used at least for adjusting the position | CSSProperties | - |
| title | The modal dialog's title | string \| ReactNode | - |
| visible | Whether the modal dialog is visible or not | boolean | false |
| width | Width of the modal dialog | string \| number | 520 |
| wrapClassName | The class name of the container of the modal dialog | string | - |
| zIndex | The `z-index` of the Modal | number | 1000 |
| onCancel | Specify a function that will be called when a user clicks mask, close button on top right or Cancel button | function(e) | - |
| onOk | Specify a function that will be called when a user clicks the OK button | function(e) | - |
#### Note
@@ -63,23 +64,26 @@ The items listed above are all functions, expecting a settings object as paramet
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| autoFocusButton | Specify which button to autofocus | null \| `ok` \| `cancel` | `ok` | |
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#API) | - | |
| cancelText | Text of the Cancel button with Modal.confirm | string | `Cancel` | |
| centered | Centered Modal | boolean | false | |
| className | The className of container | string | - | |
| content | Content | string \| ReactNode | - | |
| getContainer | Return the mount node for Modal | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
| icon | Custom icon | ReactNode | &lt;QuestionCircle /> | 3.12.0 |
| keyboard | Whether support press esc to close | boolean | true | |
| mask | Whether show mask or not. | boolean | true | |
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | false | |
| maskStyle | Style for modal's mask element | object | {} | |
| okButtonProps | The ok button props | [ButtonProps](/components/button/#API) | - | |
| okText | Text of the OK button | string | `OK` | |
| okType | Button `type` of the OK button | string | `primary` | |
| okButtonProps | The ok button props | [ButtonProps](/components/button/#API) | - | |
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#API) | - | |
| onCancel | Specify a function that will be called when the user clicks the Cancel button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - | |
| onOk | Specify a function that will be called when the user clicks the OK button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - | |
| style | Style of floating layer, typically used at least for adjusting the position | CSSProperties | - | |
| title | Title | string \| ReactNode | - | |
| width | Width of the modal dialog | string \| number | 416 | |
| zIndex | The `z-index` of the Modal | number | 1000 | |
| onCancel | Specify a function that will be called when the user clicks the Cancel button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - | |
| onOk | Specify a function that will be called when the user clicks the OK button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - | |
All the `Modal.method`s will return a reference, and then we can update and close the modal dialog by the reference.

View File

@@ -20,6 +20,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3StSdUlSH/Modal.svg
| --- | --- | --- | --- |
| afterClose | Modal 完全关闭后的回调 | function | - |
| bodyStyle | Modal body 样式 | object | {} |
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#API) | - |
| cancelText | 取消按钮文字 | string \| ReactNode | `取消` |
| centered | 垂直居中展示 Modal | boolean | false |
| closable | 是否显示右上角的关闭按钮 | boolean | true |
@@ -33,18 +34,17 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3StSdUlSH/Modal.svg
| mask | 是否展示遮罩 | boolean | true |
| maskClosable | 点击蒙层是否允许关闭 | boolean | true |
| maskStyle | 遮罩样式 | object | {} |
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#API) | - |
| okText | 确认按钮文字 | string \| ReactNode | `确定` |
| okType | 确认按钮类型 | string | `primary` |
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#API) | - |
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#API) | - |
| onCancel | 点击遮罩层或右上角叉或取消按钮的回调 | function(e) | - |
| onOk | 点击确定回调 | function(e) | - |
| style | 可用于设置浮层的样式,调整浮层位置等 | CSSProperties | - |
| title | 标题 | string \| ReactNode | - |
| visible | 对话框是否可见 | boolean | - |
| width | 宽度 | string \| number | 520 |
| wrapClassName | 对话框外层容器的类名 | string | - |
| zIndex | 设置 Modal 的 `z-index` | number | 1000 |
| onCancel | 点击遮罩层或右上角叉或取消按钮的回调 | function(e) | - |
| onOk | 点击确定回调 | function(e) | - |
#### 注意
@@ -67,21 +67,26 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3StSdUlSH/Modal.svg
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| autoFocusButton | 指定自动获得焦点的按钮 | null \| `ok` \| `cancel` | `ok` | |
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#API) | - | |
| cancelText | 设置 Modal.confirm 取消按钮文字 | string | `取消` | |
| centered | 垂直居中展示 Modal | boolean | false | |
| className | 容器类名 | string | - | |
| content | 内容 | string \| ReactNode | - | |
| getContainer | 指定 Modal 挂载的 HTML 节点, false 为挂载在当前 dom | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
| icon | 自定义图标 | ReactNode | &lt;QuestionCircle /> | 3.12.0 |
| keyboard | 是否支持键盘 esc 关闭 | boolean | true | |
| mask | 是否展示遮罩 | boolean | true | |
| maskClosable | 点击蒙层是否允许关闭 | boolean | false | |
| maskStyle | 遮罩样式 | object | {} | |
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#API) | - | |
| okText | 确认按钮文字 | string | `确定` | |
| okType | 确认按钮类型 | string | `primary` | |
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#API) | - | |
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#API) | - | |
| onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - | |
| onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - | |
| style | 可用于设置浮层的样式,调整浮层位置等 | CSSProperties | - | |
| title | 标题 | string \| ReactNode | - | |
| width | 宽度 | string \| number | 416 | |
| zIndex | 设置 Modal 的 `z-index` | number | 1000 | |
| onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - | |
| onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - | |
以上函数调用后,会返回一个引用,可以通过该引用更新和关闭弹窗。

View File

@@ -12,7 +12,7 @@ Array [
<div>
<div
class="ant-popover ant-popover-rtl"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-popover-content"

View File

@@ -7,7 +7,7 @@ import { ConfigContext } from '../config-provider';
import SizeContext from '../config-provider/SizeContext';
import { RadioGroupContextProvider } from './context';
const RadioGroup = React.forwardRef<unknown, RadioGroupProps>((props, ref) => {
const RadioGroup: React.FC<RadioGroupProps> = props => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const size = React.useContext(SizeContext);
@@ -53,7 +53,6 @@ const RadioGroup = React.forwardRef<unknown, RadioGroupProps>((props, ref) => {
// 此处类型自动推导为 string
return (
<Radio
ref={ref}
key={option}
prefixCls={optionsPrefixCls}
disabled={disabled}
@@ -67,7 +66,6 @@ const RadioGroup = React.forwardRef<unknown, RadioGroupProps>((props, ref) => {
// 此处类型自动推导为 { label: string value: string }
return (
<Radio
ref={ref}
key={`radio-group-value-options-${option.value}`}
prefixCls={optionsPrefixCls}
disabled={option.disabled || disabled}
@@ -116,7 +114,7 @@ const RadioGroup = React.forwardRef<unknown, RadioGroupProps>((props, ref) => {
{renderGroup()}
</RadioGroupContextProvider>
);
});
};
RadioGroup.defaultProps = {
buttonStyle: 'outline' as RadioGroupButtonStyle,

View File

@@ -735,7 +735,7 @@ exports[`renders ./components/select/demo/custom-dropdown-menu.md correctly 1`]
exports[`renders ./components/select/demo/custom-tag-render.md correctly 1`] = `
<div
class="ant-select ant-select-multiple ant-select-show-search"
class="ant-select ant-select-multiple ant-select-show-arrow ant-select-show-search"
style="width:100%"
>
<div
@@ -825,6 +825,33 @@ exports[`renders ./components/select/demo/custom-tag-render.md correctly 1`] = `
</span>
</span>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
`;
@@ -1091,7 +1118,7 @@ exports[`renders ./components/select/demo/label-in-value.md correctly 1`] = `
exports[`renders ./components/select/demo/multiple.md correctly 1`] = `
Array [
<div
class="ant-select ant-select-multiple ant-select-show-search"
class="ant-select ant-select-multiple ant-select-allow-clear ant-select-show-search"
style="width:100%"
>
<div
@@ -1195,6 +1222,33 @@ Array [
</span>
</span>
</div>
<span
aria-hidden="true"
class="ant-select-clear"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</div>,
<br />,
<div

View File

@@ -7,11 +7,11 @@ title:
## zh-CN
允许自定义选择标签的样式
允许自定义选择标签的样式
## en-US
Allows for custom rendering of tags
Allows for custom rendering of tags.
```jsx
import { Select, Tag } from 'antd';
@@ -31,6 +31,7 @@ function tagRender(props) {
ReactDOM.render(
<Select
mode="multiple"
showArrow
tagRender={tagRender}
defaultValue={['gold', 'cyan']}
style={{ width: '100%' }}

View File

@@ -31,6 +31,7 @@ ReactDOM.render(
<>
<Select
mode="multiple"
allowClear
style={{ width: '100%' }}
placeholder="Please select"
defaultValue={['a10', 'c12']}

View File

@@ -7,11 +7,11 @@ title:
## zh-CN
tags select随意输入的内容scroll the menu
tags select随意输入的内容scroll the menu
## en-US
Select with tags, transform input to tag (scroll the menu)
Select with tags, transform input to tag (scroll the menu).
```jsx
import { Select } from 'antd';

View File

@@ -27,7 +27,10 @@ Select component to select value from options.
| allowClear | Show clear button | boolean | false | |
| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when `mode` is set to `multiple` or `tags` | boolean | true | |
| autoFocus | Get focus by default | boolean | false | |
| bordered | Whether has border style | boolean | true | |
| clearIcon | The custom clear icon | ReactNode | - | |
| defaultActiveFirstOption | Whether active first option by default | boolean | true | |
| defaultOpen | Initial open state of dropdown | boolean | - | |
| defaultValue | Initial selected option | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
| disabled | Whether disabled select | boolean | false | |
| dropdownClassName | The className of dropdown menu | string | - | |
@@ -36,32 +39,20 @@ Select component to select value from options.
| dropdownStyle | The style of dropdown menu | CSSProperties | - | |
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded | boolean \| function(inputValue, option) | true | |
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. [Example](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
| labelInValue | Whether to embed label in value, turn the format of value from `string` to `{ value: string, label: ReactNode }` | boolean | false | |
| labelInValue | Whether to embed label in value, turn the format of value from `string` to { value: string, label: ReactNode } | boolean | false | |
| listHeight | Config popup height | number | 256 | |
| loading | Indicate loading state | boolean | false | |
| maxTagCount | Max tag count to show | number | - | |
| maxTagTextLength | Max tag text length to show | number | - | |
| maxTagPlaceholder | Placeholder for not showing tags | ReactNode \| function(omittedValues) | - | |
| tagRender | Customize tag render | (props) => ReactNode | - | |
| maxTagTextLength | Max tag text length to show | number | - | |
| menuItemSelectedIcon | The custom menuItemSelected icon with multiple options | ReactNode | - | |
| mode | Set mode of Select | `multiple` \| `tags` | - | |
| notFoundContent | Specify content to show when no result matches | ReactNode | `Not Found` | |
| options | Select options. Will get better perf than jsx definition | { label, value }[] | - | |
| optionFilterProp | Which prop value of option will be used for filter if filterOption is true | string | `value` | |
| optionLabelProp | Which prop value of option will render as content of select. [Example](https://codesandbox.io/s/antd-reproduction-template-tk678) | string | `children` | |
| placeholder | Placeholder of select | string \| ReactNode | - | |
| showArrow | Whether to show the drop-down arrow | boolean | true(for single select), false(for multiple select) | |
| showSearch | Whether show search input in single mode | boolean | false | |
| size | Size of Select input | `large` \| `middle` \| `small` | - | |
| suffixIcon | The custom suffix icon | ReactNode | - | |
| removeIcon | The custom remove icon | ReactNode | - | |
| clearIcon | The custom clear icon | ReactNode | - | |
| menuItemSelectedIcon | The custom menuItemSelected icon with multiple options | ReactNode | - | |
| tokenSeparators | Separator used to tokenize on tag \| multiple mode | string\[] | - | |
| value | Current selected option | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 |
| onBlur | Called when blur | function | - | |
| onChange | Called when select an option or input value change | function(value, option:Option \| Array&lt;Option>) | - | |
| onClear | Called when clear | function | - | 4.6.0 |
| onDeselect | Called when a option is deselected, param is the selected option's value. Only called for multiple or tags, effective in multiple or tags mode only | function(string \| number \| LabeledValue) | - | |
| onDeselect | Called when a option is deselected, param is the selected option's value. Only called for `multiple` or `tags`, effective in multiple or tags mode only | function(string \| number \| LabeledValue) | - | |
| onDropdownVisibleChange | Called when dropdown open | function(open) | - | |
| onFocus | Called when focus | function | - | |
| onInputKeyDown | Called when key pressed | function | - | |
| onMouseEnter | Called when mouse enter | function | - | |
@@ -69,11 +60,22 @@ Select component to select value from options.
| onPopupScroll | Called when dropdown scrolls | function | - | |
| onSearch | Callback function that is fired when input changed | function(value: string) | - | |
| onSelect | Called when a option is selected, the params are option's value (or key) and option instance | function(string \| number \| LabeledValue, option: Option) | - | |
| defaultOpen | Initial open state of dropdown | boolean | - | |
| open | Controlled open state of dropdown | boolean | - | |
| onDropdownVisibleChange | Call when dropdown open | function(open) | - | |
| loading | Indicate loading state | boolean | false | |
| bordered | Whether has border style | boolean | true | |
| options | Select options. Will get better perf than jsx definition | { label, value }[] | - | |
| optionFilterProp | Which prop value of option will be used for filter if filterOption is true | string | `value` | |
| optionLabelProp | Which prop value of option will render as content of select. [Example](https://codesandbox.io/s/antd-reproduction-template-tk678) | string | `children` | |
| placeholder | Placeholder of select | string \| ReactNode | - | |
| removeIcon | The custom remove icon | ReactNode | - | |
| showArrow | Whether to show the drop-down arrow | boolean | true(for single select), false(for multiple select) | |
| showSearch | Whether show search input in single mode | boolean | false | |
| size | Size of Select input | `large` \| `middle` \| `small` | - | |
| suffixIcon | The custom suffix icon | ReactNode | - | |
| tagRender | Customize tag render | (props) => ReactNode | - | |
| tokenSeparators | Separator used to tokenize on `tag` and `multiple` mode | string\[] | - | |
| value | Current selected option | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 |
> Note, if you find that the drop-down menu scrolls with the page, or you need to trigger Select in other popup layers, please try to use `getPopupContainer={triggerNode => triggerNode.parentElement}` to fix the drop-down popup rendering node in the parent element of the trigger .
### Select Methods
@@ -86,10 +88,10 @@ Select component to select value from options.
| Property | Description | Type | Default | Version |
| --------- | ------------------------------------------ | ---------------- | ------- | ------- |
| className | The additional class to option | string | - | |
| disabled | Disable this option | boolean | false | |
| title | `title` of Select after select this Option | string | - | |
| value | Default to filter with this property | string \| number | - | |
| className | The additional class to option | string | - | |
### OptGroup props

View File

@@ -28,7 +28,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
| allowClear | 支持清除 | boolean | false | |
| autoClearSearchValue | 是否在选中项后清空搜索框,只在 `mode``multiple``tags` 时有效 | boolean | true | |
| autoFocus | 默认获取焦点 | boolean | false | |
| bordered | 是否有边框 | boolean | true | |
| clearIcon | 自定义的多选框清空图标 | ReactNode | - | |
| defaultActiveFirstOption | 是否默认高亮第一个选项 | boolean | true | |
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |
| defaultValue | 指定默认选中的条目 | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
| disabled | 是否禁用 | boolean | false | |
| dropdownClassName | 下拉菜单的 className 属性 | string | - | |
@@ -37,32 +40,20 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
| dropdownStyle | 下拉菜单的 style 属性 | CSSProperties | - | |
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 true反之则返回 false | boolean \| function(inputValue, option) | true | |
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 `string` 变为 `{ value: string, label: ReactNode }` 的格式 | boolean | false | |
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 `string` 变为 { value: string, label: ReactNode } 的格式 | boolean | false | |
| listHeight | 设置弹窗滚动高度 | number | 256 | |
| loading | 加载中状态 | boolean | false | |
| maxTagCount | 最多显示多少个 tag | number | - | |
| maxTagTextLength | 最大显示的 tag 文本长度 | number | - | |
| maxTagPlaceholder | 隐藏 tag 时显示的内容 | ReactNode \| function(omittedValues) | - | |
| tagRender | 自定义 tag 内容 render | (props) => ReactNode | - | |
| maxTagTextLength | 最大显示的 tag 文本长度 | number | - | |
| menuItemSelectedIcon | 自定义多选时当前选中的条目图标 | ReactNode | - | |
| mode | 设置 Select 的模式为多选或标签 | `multiple` \| `tags` | - | |
| notFoundContent | 当下拉列表为空时显示的内容 | ReactNode | `Not Found` | |
| options | 数据化配置选项内容,相比 jsx 定义会获得更好的渲染性能 | { label, value }[] | - | |
| optionFilterProp | 搜索时过滤对应的 option 属性,如设置为 children 表示对内嵌内容进行搜索。[示例](https://codesandbox.io/s/antd-reproduction-template-tk678) | string | `value` | |
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 `value` | string | `children` | |
| placeholder | 选择框默认文字 | string | - | |
| showArrow | 是否显示下拉小箭头 | boolean | 单选为 true多选为 false | |
| showSearch | 使单选模式可搜索 | boolean | false | |
| size | 选择框大小 | `large` \| `middle` \| `small` | - | |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| removeIcon | 自定义的多选框清除图标 | ReactNode | - | |
| clearIcon | 自定义的多选框清空图标 | ReactNode | - | |
| menuItemSelectedIcon | 自定义多选时当前选中的条目图标 | ReactNode | - | |
| tokenSeparators | 在 tags 和 multiple 模式下自动分词的分隔符 | string\[] | - | |
| value | 指定当前选中的条目 | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 4.1.0 |
| onBlur | 失去焦点时回调 | function | - | |
| onChange | 选中 option或 input 的 value 变化时,调用此函数 | function(value, option:Option \| Array&lt;Option>) | - | |
| onClear | 清除内容时回调 | function | - | 4.6.0 |
| onDeselect | 取消选中时调用,参数为选中项的 value (或 key) 值,仅在 multiple 或 tags 模式下生效 | function(string \| number \| LabeledValue) | - | |
| onDeselect | 取消选中时调用,参数为选中项的 value (或 key) 值,仅在 `multiple``tags` 模式下生效 | function(string \| number \| LabeledValue) | - | |
| onDropdownVisibleChange | 展开下拉菜单的回调 | function(open) | - | |
| onFocus | 获得焦点时回调 | function | - | |
| onInputKeyDown | 按键按下时回调 | function | - | |
| onMouseEnter | 鼠标移入时回调 | function | - | |
@@ -70,11 +61,20 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
| onPopupScroll | 下拉列表滚动时的回调 | function | - | |
| onSearch | 文本框值变化时回调 | function(value: string) | - | |
| onSelect | 被选中时调用,参数为选中项的 value (或 key) 值 | function(string \| number \| LabeledValue, option: Option) | - | |
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |
| open | 是否展开下拉菜单 | boolean | - | |
| onDropdownVisibleChange | 展开下拉菜单的回调 | function(open) | - | |
| loading | 加载中状态 | boolean | false | |
| bordered | 是否有边框 | boolean | true | |
| options | 数据化配置选项内容,相比 jsx 定义会获得更好的渲染性能 | { label, value }[] | - | |
| optionFilterProp | 搜索时过滤对应的 option 属性,如设置为 children 表示对内嵌内容进行搜索。[示例](https://codesandbox.io/s/antd-reproduction-template-tk678) | string | `value` | |
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 `value` | string | `children` | |
| placeholder | 选择框默认文字 | string | - | |
| removeIcon | 自定义的多选框清除图标 | ReactNode | - | |
| showArrow | 是否显示下拉小箭头 | boolean | 单选为 true多选为 false | |
| showSearch | 使单选模式可搜索 | boolean | false | |
| size | 选择框大小 | `large` \| `middle` \| `small` | - | |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| tagRender | 自定义 tag 内容 render | (props) => ReactNode | - | |
| tokenSeparators | 在 `tags``multiple` 模式下自动分词的分隔符 | string\[] | - | |
| value | 指定当前选中的条目 | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 4.1.0 |
> 注意,如果发现下拉菜单跟随页面滚动,或者需要在其他弹层中触发 Select请尝试使用 `getPopupContainer={triggerNode => triggerNode.parentElement}` 将下拉弹层渲染节点固定在触发器的父元素中。
@@ -89,10 +89,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --------- | --------------------------------- | ---------------- | ------ | ---- |
| className | Option 器类名 | string | - | |
| disabled | 是否禁用 | boolean | false | |
| title | 选中该 Option 后Select 的 title | string | - | |
| value | 默认根据此属性值进行筛选 | string \| number | - | |
| className | Option 器类名 | string | - | |
### OptGroup props

View File

@@ -28,7 +28,7 @@
}
}
.@{select-prefix-cls}-focused&:not(.@{select-prefix-cls}-disabled&) {
.@{select-prefix-cls}-focused:not(.@{select-prefix-cls}-disabled)& {
.active();
}

View File

@@ -957,7 +957,7 @@ exports[`renders ./components/slider/demo/show-tooltip.md correctly 1`] = `
<div>
<div
class="ant-tooltip ant-slider-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"

View File

@@ -57,7 +57,7 @@ exports[`Slider should render in RTL direction 1`] = `
<div>
<div
class="ant-tooltip ant-slider-tooltip ant-tooltip-rtl"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
@@ -88,7 +88,7 @@ exports[`Slider should show correct placement tooltip when set tooltipPlacement
<div>
<div
class="ant-tooltip ant-slider-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
@@ -141,7 +141,7 @@ exports[`Slider should show tooltip when hovering slider handler 1`] = `
<div>
<div
class="ant-tooltip ant-slider-tooltip"
style="opacity:0"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"

49
components/space/Item.tsx Normal file
View File

@@ -0,0 +1,49 @@
import * as React from 'react';
import { LastIndexContext } from '.';
import { SizeType } from '../config-provider/SizeContext';
const spaceSize = {
small: 8,
middle: 16,
large: 24,
};
export interface ItemProps {
className: string;
children: React.ReactNode;
index: number;
direction?: 'horizontal' | 'vertical';
size?: SizeType | number;
marginDirection: 'marginLeft' | 'marginRight';
}
export default function Item({
className,
direction,
index,
size,
marginDirection,
children,
}: ItemProps) {
const latestIndex = React.useContext(LastIndexContext);
if (children === null || children === undefined) {
return null;
}
return (
<div
className={className}
style={
index >= latestIndex
? {}
: {
[direction === 'vertical' ? 'marginBottom' : marginDirection]:
typeof size === 'string' ? spaceSize[size] : size,
}
}
>
{children}
</div>
);
}

View File

@@ -395,12 +395,6 @@ exports[`renders ./components/space/demo/debug.md correctly 1`] = `
</button>
</span>
</div>
<div
class="ant-space-item"
/>
<div
class="ant-space-item"
/>
<div
class="ant-space-item"
style="margin-right:8px"

View File

@@ -43,8 +43,8 @@ describe('Space', () => {
</Space>,
);
expect(wrapper.find('.ant-space-item').at(0).prop('style').marginRight).toBe(10);
expect(wrapper.find('.ant-space-item').at(1).prop('style').marginRight).toBeUndefined();
expect(wrapper.find('div.ant-space-item').at(0).prop('style').marginRight).toBe(10);
expect(wrapper.find('div.ant-space-item').at(1).prop('style').marginRight).toBeUndefined();
});
it('should render vertical space width customize size', () => {
@@ -55,8 +55,8 @@ describe('Space', () => {
</Space>,
);
expect(wrapper.find('.ant-space-item').at(0).prop('style').marginBottom).toBe(10);
expect(wrapper.find('.ant-space-item').at(1).prop('style').marginBottom).toBeUndefined();
expect(wrapper.find('div.ant-space-item').at(0).prop('style').marginBottom).toBe(10);
expect(wrapper.find('div.ant-space-item').at(1).prop('style').marginBottom).toBeUndefined();
});
it('should render correct with children', () => {
@@ -78,7 +78,7 @@ describe('Space', () => {
</Space>,
);
expect(wrapper.find('.ant-space-item').length).toBe(3);
expect(wrapper.find('div.ant-space-item').length).toBe(3);
});
it('should be keep store', () => {

View File

@@ -19,8 +19,10 @@ import { Space, Button, Popconfirm } from 'antd';
ReactDOM.render(
<Space>
Button
<Button>Button</Button>
<>
Button
<Button>Button</Button>
</>
Button
<Popconfirm title="Are you sure delete this task?" okText="Yes" cancelText="No">
<Button>Delete</Button>
@@ -32,6 +34,8 @@ ReactDOM.render(
{false}
{1}
Button
{null}
{undefined}
</Space>,
mountNode,
);

View File

@@ -1,7 +1,11 @@
import * as React from 'react';
import classNames from 'classnames';
import toArray from 'rc-util/lib/Children/toArray';
import { ConfigConsumerProps, ConfigContext } from '../config-provider';
import { SizeType } from '../config-provider/SizeContext';
import Item from './Item';
export const LastIndexContext = React.createContext(0);
export interface SpaceProps {
prefixCls?: string;
@@ -13,12 +17,6 @@ export interface SpaceProps {
align?: 'start' | 'end' | 'center' | 'baseline';
}
const spaceSize = {
small: 8,
middle: 16,
large: 24,
};
const Space: React.FC<SpaceProps> = props => {
const { getPrefixCls, space, direction: directionConfig }: ConfigConsumerProps = React.useContext(
ConfigContext,
@@ -34,9 +32,9 @@ const Space: React.FC<SpaceProps> = props => {
...otherProps
} = props;
const len = React.Children.count(children);
const childNodes = toArray(children, { keepEmpty: true });
if (len === 0) {
if (childNodes.length === 0) {
return null;
}
@@ -56,25 +54,32 @@ const Space: React.FC<SpaceProps> = props => {
const marginDirection = directionConfig === 'rtl' ? 'marginLeft' : 'marginRight';
// Calculate latest one
let latestIndex = 0;
const nodes = childNodes.map((child, i) => {
if (child !== null && child !== undefined) {
latestIndex = i;
}
/* eslint-disable react/no-array-index-key */
return (
<Item
className={itemClassName}
key={`${itemClassName}-${i}`}
direction={direction}
size={size}
index={i}
marginDirection={marginDirection}
>
{child}
</Item>
);
/* eslint-enable */
});
return (
<div className={cn} {...otherProps}>
{React.Children.map(children, (child, i) => (
<div
className={itemClassName}
// eslint-disable-next-line react/no-array-index-key
key={`${itemClassName}-${i}`}
style={
i === len - 1 || child === null || child === undefined
? {}
: {
[direction === 'vertical' ? 'marginBottom' : marginDirection]:
typeof size === 'string' ? spaceSize[size] : size,
}
}
>
{child}
</div>
))}
<LastIndexContext.Provider value={latestIndex}>{nodes}</LastIndexContext.Provider>
</div>
);
};

View File

@@ -420,7 +420,7 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
let topPaginationNode: React.ReactNode;
let bottomPaginationNode: React.ReactNode;
if (pagination !== false) {
if (pagination !== false && mergedPagination?.total) {
let paginationSize: TablePaginationConfig['size'];
if (mergedPagination.size) {
paginationSize = mergedPagination.size;
@@ -495,7 +495,7 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
internalRefs={internalRefs as any}
transformColumns={transformColumns}
/>
{mergedData && mergedData.length > 0 && bottomPaginationNode}
{bottomPaginationNode}
</Spin>
</div>
);

View File

@@ -378,39 +378,24 @@ describe('Table.pagination', () => {
);
});
it('should render pagination after last item on last page being removed with async mode', () => {
const lastPageNum = data.length;
it('should render pagination after last item on last page being removed', () => {
const total = data.length;
const paginationProp = {
pageSize: 1,
total,
current: total,
position: ['topLeft', 'bottomLeft'],
};
const wrapper = mount(
createTable({ pagination: { pageSize: 1, total: data.length, current: lastPageNum } }),
createTable({
pagination: paginationProp,
}),
);
const newCol = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Action',
dataIndex: 'name',
render(_, record) {
const deleteRow = () => {
const newData = data.filter(d => d.key !== record.key);
wrapper.setProps({
dataSource: newData,
pagination: { pageSize: 1, total: newData.length, current: lastPageNum },
});
};
return (
<span className="btn-delete" onClick={deleteRow}>
Delete
</span>
);
},
},
];
wrapper.setProps({ columns: newCol });
wrapper.find('.btn-delete').simulate('click');
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
wrapper.setProps({
dataSource: data.slice(total - 1),
pagination: { ...paginationProp, total: total - 1 },
});
expect(wrapper.find('.ant-pagination')).toHaveLength(2);
});
});

View File

@@ -226,7 +226,7 @@ exports[`Table.filter renders custom filter icon with right Tooltip title 1`] =
<div>
<div
class="ant-tooltip"
style="opacity: 0;"
style="opacity: 0; pointer-events: none;"
>
<div
class="ant-tooltip-content"
@@ -709,7 +709,7 @@ exports[`Table.filter should support getPopupContainer 1`] = `
<div>
<div
class="ant-dropdown"
style="opacity: 0;"
style="opacity: 0; pointer-events: none;"
>
<div
class="ant-table-filter-dropdown"
@@ -942,7 +942,7 @@ exports[`Table.filter should support getPopupContainer from ConfigProvider 1`] =
<div>
<div
class="ant-dropdown"
style="opacity: 0;"
style="opacity: 0; pointer-events: none;"
>
<div
class="ant-table-filter-dropdown"

View File

@@ -1038,7 +1038,7 @@ exports[`Table.rowSelection should support getPopupContainer 1`] = `
<div>
<div
class="ant-dropdown"
style="opacity: 0;"
style="opacity: 0; pointer-events: none;"
>
<ul
class="ant-dropdown-menu ant-dropdown-menu-light ant-dropdown-menu-root ant-dropdown-menu-vertical"
@@ -1368,7 +1368,7 @@ exports[`Table.rowSelection should support getPopupContainer from ConfigProvider
<div>
<div
class="ant-dropdown"
style="opacity: 0;"
style="opacity: 0; pointer-events: none;"
>
<ul
class="ant-dropdown-menu ant-dropdown-menu-light ant-dropdown-menu-root ant-dropdown-menu-vertical"

View File

@@ -16978,9 +16978,28 @@ exports[`renders ./components/table/demo/virtual-list.md correctly 1`] = `
style="overflow:hidden"
>
<table
style="table-layout:fixed;visibility:hidden"
style="table-layout:fixed"
>
<colgroup />
<colgroup>
<col
style="width:150px;min-width:150px"
/>
<col
style="width:0;min-width:0"
/>
<col
style="width:0;min-width:0"
/>
<col
style="width:0;min-width:0"
/>
<col
style="width:200px;min-width:200px"
/>
<col
style="width:100px;min-width:100px"
/>
</colgroup>
<thead
class="ant-table-thead"
>

View File

@@ -172,6 +172,7 @@ Properties for expandable.
| defaultExpandedRowKeys | Initial expanded row keys | string\[] | - |
| expandIcon | Customize row expand Icon. Ref [example](https://codesandbox.io/s/fervent-bird-nuzpr) | function(props): ReactNode | - |
| expandIconColumnIndex | Customize expand icon column index. Not render when `-1` | number | - |
| expandedRowClassName | Expanded row's className | function(record, index, indent): string | - |
| expandedRowKeys | Current expanded row keys | string\[] | - |
| expandedRowRender | Expanded container render for each row | function(record, index, indent, expanded): ReactNode | - |
| expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | false |

View File

@@ -179,6 +179,7 @@ const columns = [
| defaultExpandedRowKeys | 默认展开的行 | string\[] | - |
| expandIcon | 自定义展开图标,参考[示例](https://codesandbox.io/s/fervent-bird-nuzpr) | function(props): ReactNode | - |
| expandIconColumnIndex | 自定义展开按钮的列顺序,`-1` 时不展示 | number | - |
| expandedRowClassName | 展开行的 className | function(record, index, indent): string | - |
| expandedRowKeys | 展开的行,控制属性 | string\[] | - |
| expandedRowRender | 额外的展开行 | function(record, index, indent, expanded): ReactNode | - |
| expandRowByClick | 通过点击行来展开子行 | boolean | false |

View File

@@ -412,12 +412,13 @@
float: left;
box-sizing: border-box;
width: ceil(@font-size-sm * 1.4);
height: ceil(@font-size-sm * 1.4);
width: ceil((@font-size-sm * 1.4 - @border-width-base * 3) / 2) * 2 + @border-width-base * 3;
height: ceil((@font-size-sm * 1.4 - @border-width-base * 3) / 2) * 2 + @border-width-base * 3;
padding: 0;
color: inherit;
line-height: @font-size-sm;
vertical-align: floor((@font-size-base - ceil(@font-size-sm * 1.4)) / 2);
line-height: ceil((@font-size-sm * 1.4 - @border-width-base * 3) / 2) * 2 + @border-width-base *
3;
// vertical-align: floor((@font-size-base - ceil(@font-size-sm * 1.4)) / 2);
background: @table-expand-icon-bg;
border: @border-width-base @border-style-base @border-color-split;
border-radius: @border-radius-base;
@@ -440,7 +441,7 @@
}
&::before {
top: 7px;
top: ceil((@font-size-sm * 1.4 - @border-width-base * 3) / 2);
right: 3px;
left: 3px;
height: @border-width-base;
@@ -449,7 +450,7 @@
&::after {
top: 3px;
bottom: 3px;
left: 7px;
left: ceil((@font-size-sm * 1.4 - @border-width-base * 3) / 2);
width: @border-width-base;
transform: rotate(90deg);
}
@@ -474,7 +475,8 @@
}
.@{table-prefix-cls}-row-indent + & {
margin-top: (@font-size-base * @line-height-base - ceil(@font-size-sm * 1.4)) / 2;
margin-top: (@font-size-base * @line-height-base - @border-width-base * 3) / 2 -
ceil((@font-size-sm * 1.4 - @border-width-base * 3) / 2);
margin-right: @padding-xs;
}
}

View File

@@ -30,16 +30,16 @@ const OperationsSlot = {
const options = ['left', 'right'];
const Demo = () => {
const [positon, setPosition] = React.useState(['left', 'right']);
const [position, setPosition] = React.useState(['left', 'right']);
const slot = React.useMemo(() => {
if (positon.length === 0) return null;
if (position.length === 0) return null;
return positon.reduce(
(acc, direaction) => ({ ...acc, [direaction]: OperationsSlot[direaction] }),
return position.reduce(
(acc, direction) => ({ ...acc, [direction]: OperationsSlot[direction] }),
{},
);
}, [positon]);
}, [position]);
return (
<>
@@ -61,7 +61,7 @@ const Demo = () => {
<Divider />
<CheckboxGroup
options={options}
value={positon}
value={position}
onChange={value => {
setPosition(value);
}}

View File

@@ -118,7 +118,7 @@ Array [
<div>
<div
class="ant-picker-dropdown"
style="opacity: 0;"
style="opacity: 0; pointer-events: none;"
>
<div
class="ant-picker-panel-container"

View File

@@ -0,0 +1,10 @@
import * as React from 'react';
import TimePicker from '..';
describe('TimePicker.typescript', () => {
it('No need picker props', () => {
const rangePicker = <TimePicker.RangePicker />;
expect(rangePicker).toBeTruthy();
});
});

View File

@@ -12,7 +12,7 @@ export interface TimePickerLocale {
rangePlaceholder?: [string, string];
}
export interface TimeRangePickerProps extends RangePickerTimeProps<Moment> {}
export interface TimeRangePickerProps extends Omit<RangePickerTimeProps<Moment>, 'picker'> {}
const RangePicker = React.forwardRef<any, TimeRangePickerProps>((props, ref) => {
return <InternalRangePicker {...props} picker="time" mode={undefined} ref={ref} />;

View File

@@ -34,7 +34,7 @@ exports[`TreeSelect TreeSelect Custom Icons should \`treeIcon\` work 1`] = `
<div>
<div
class="ant-select-dropdown ant-tree-select-dropdown"
style="opacity: 0; min-width: 0; width: 0px;"
style="opacity: 0; pointer-events: none; min-width: 0; width: 0px;"
>
<div>
<div>

View File

@@ -45,4 +45,9 @@ describe('TreeSelect', () => {
expect(wrapper.render()).toMatchSnapshot();
});
});
it('should support notFoundContent', () => {
const wrapper = mount(<TreeSelect treeIcon open notFoundContent="notFoundContent" />);
expect(wrapper.text()).toBe('notFoundContent');
});
});

View File

@@ -35,12 +35,10 @@
}
}
// ========================== Tree ==========================
.antTreeFn(@select-tree-prefix-cls);
// change switcher icon rotation in rtl direction
.@{select-tree-prefix-cls} {
// >>> Switcher
.antTreeFn(@select-tree-prefix-cls);
// change switcher icon rotation in rtl direction
& &-switcher {
&_close {
.@{select-tree-prefix-cls}-switcher-icon {

View File

@@ -17,7 +17,7 @@ Almost anything can be represented in a tree structure. Examples include directo
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| autoExpandParent | Whether to automatically expand a parent treeNode | boolean | true | |
| autoExpandParent | Whether to automatically expand a parent treeNode | boolean | false | |
| blockNode | Whether treeNode fill remaining horizontal space | boolean | false | |
| checkable | Add a Checkbox before the treeNodes | boolean | false | |
| checkedKeys | (Controlled) Specifies the keys of the checked treeNodes (PS: When this specifies the key of a treeNode which is also a parent treeNode, all the children treeNodes of will be checked; and vice versa, when it specifies the key of a treeNode which is a child treeNode, its parent treeNode will also be checked. When `checkable` and `checkStrictly` is true, its object has `checked` and `halfChecked` property. Regardless of whether the child or parent treeNode is checked, they won't impact each other | string\[] \| {checked: string\[], halfChecked: string\[]} | \[] | |

View File

@@ -18,7 +18,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Xh-oWqg9k/Tree.svg
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| autoExpandParent | 是否自动展开父节点 | boolean | true | |
| autoExpandParent | 是否自动展开父节点 | boolean | false | |
| blockNode | 是否节点占据一行 | boolean | false | |
| checkable | 节点前添加 Checkbox 复选框 | boolean | false | |
| checkedKeys | (受控)选中复选框的树节点(注意:父子节点有关联,如果传入父节点 key则子节点自动选中相应当子节点 key 都传入,父节点也自动选中。当设置`checkable``checkStrictly`,它是一个有`checked``halfChecked`属性的对象,并且父子节点的选中与否不再关联 | string\[] \| {checked: string\[], halfChecked: string\[]} | \[] | |

View File

@@ -9,6 +9,8 @@
.antCheckboxFn(@checkbox-prefix-cls: ~'@{ant-prefix}-tree-checkbox');
.antTreeFn(@tree-prefix-cls);
.@{tree-prefix-cls} {
.antTreeFn(@tree-prefix-cls);
}
@import './rtl';

View File

@@ -31,233 +31,231 @@
.antTreeFn(@custom-tree-prefix-cls) {
@custom-tree-node-prefix-cls: ~'@{custom-tree-prefix-cls}-treenode';
.@{custom-tree-prefix-cls} {
.reset-component;
background: @tree-bg;
.reset-component;
background: @tree-bg;
border-radius: @border-radius-base;
transition: background-color 0.3s;
&-focused:not(:hover):not(&-active-focused) {
background: @primary-1;
}
// =================== Virtual List ===================
&-list-holder-inner {
align-items: flex-start;
}
&.@{custom-tree-prefix-cls}-block-node {
.@{custom-tree-prefix-cls}-list-holder-inner {
align-items: stretch;
// >>> Title
.@{custom-tree-prefix-cls}-node-content-wrapper {
flex: auto;
}
}
}
// ===================== TreeNode =====================
.@{custom-tree-node-prefix-cls} {
display: flex;
align-items: flex-start;
padding: 0 0 (@padding-xs / 2) 0;
outline: none;
// Disabled
&-disabled {
// >>> Title
.@{custom-tree-prefix-cls}-node-content-wrapper {
color: @disabled-color;
cursor: not-allowed;
&:hover {
background: transparent;
}
}
}
&-active .@{custom-tree-prefix-cls}-node-content-wrapper {
background: @tree-node-hover-bg;
}
}
// >>> Indent
&-indent {
align-self: stretch;
white-space: nowrap;
user-select: none;
&-unit {
display: inline-block;
width: @tree-title-height;
}
}
// >>> Switcher
& &-switcher {
.antTreeSwitcherIcon();
flex: none;
width: @tree-title-height;
height: @tree-title-height;
margin: 0;
line-height: @tree-title-height;
text-align: center;
cursor: pointer;
&-noop {
cursor: default;
}
&_close {
.@{custom-tree-prefix-cls}-switcher-icon {
svg {
transform: rotate(-90deg);
}
}
}
&-loading-icon {
color: @primary-color;
}
&-leaf-line {
z-index: 1;
display: inline-block;
width: 100%;
height: 100%;
&::before {
position: absolute;
height: @tree-title-height;
margin-left: -1px;
border-left: 1px solid @normal-color;
content: ' ';
}
&::after {
position: absolute;
width: @tree-title-height - 14px;
height: @tree-title-height - 10px;
margin-left: -1px;
border-bottom: 1px solid @normal-color;
content: ' ';
}
}
}
// >>> Checkbox
& &-checkbox {
top: initial;
margin: ((@tree-title-height - @checkbox-size) / 2) 8px 0 0;
}
// >>> Title
& &-node-content-wrapper {
min-height: @tree-title-height;
margin: 0;
padding: 0 4px;
color: inherit;
line-height: @tree-title-height;
background: transparent;
border-radius: @border-radius-base;
transition: background-color 0.3s;
cursor: pointer;
transition: all 0.3s, border 0s, line-height 0s;
&-focused:not(:hover):not(&-active-focused) {
background: @primary-1;
&:hover {
background-color: @tree-node-hover-bg;
}
// =================== Virtual List ===================
&-list-holder-inner {
align-items: flex-start;
&.@{custom-tree-prefix-cls}-node-selected {
background-color: @tree-node-selected-bg;
}
&.@{custom-tree-prefix-cls}-block-node {
.@{custom-tree-prefix-cls}-list-holder-inner {
align-items: stretch;
// >>> Title
.@{custom-tree-prefix-cls}-node-content-wrapper {
flex: auto;
}
}
}
// ===================== TreeNode =====================
.@{custom-tree-node-prefix-cls} {
display: flex;
align-items: flex-start;
padding: 0 0 (@padding-xs / 2) 0;
outline: none;
// Disabled
&-disabled {
// >>> Title
.@{custom-tree-prefix-cls}-node-content-wrapper {
color: @disabled-color;
cursor: not-allowed;
&:hover {
background: transparent;
}
}
}
&-active .@{custom-tree-prefix-cls}-node-content-wrapper {
background: @tree-node-hover-bg;
}
}
// >>> Indent
&-indent {
align-self: stretch;
white-space: nowrap;
user-select: none;
&-unit {
display: inline-block;
width: @tree-title-height;
}
}
// >>> Switcher
& &-switcher {
.antTreeSwitcherIcon();
flex: none;
// Icon
.@{custom-tree-prefix-cls}-iconEle {
display: inline-block;
width: @tree-title-height;
height: @tree-title-height;
margin: 0;
line-height: @tree-title-height;
text-align: center;
cursor: pointer;
&-noop {
cursor: default;
vertical-align: top;
&:empty {
display: none;
}
}
}
&_close {
.@{custom-tree-prefix-cls}-switcher-icon {
svg {
transform: rotate(-90deg);
}
}
}
// ==================== Draggable =====================
&-node-content-wrapper[draggable='true'] {
line-height: @tree-title-height - 4px;
border-top: 2px transparent solid;
border-bottom: 2px transparent solid;
user-select: none;
}
&-loading-icon {
color: @primary-color;
}
.@{custom-tree-node-prefix-cls}.drag-over {
> [draggable] {
color: white;
background-color: @primary-color;
opacity: 0.8;
}
}
.@{custom-tree-node-prefix-cls}.drag-over-gap-top {
> [draggable] {
border-top-color: @primary-color;
}
}
.@{custom-tree-node-prefix-cls}.drag-over-gap-bottom {
> [draggable] {
border-bottom-color: @primary-color;
}
}
&-leaf-line {
z-index: 1;
display: inline-block;
width: 100%;
// ==================== Show Line =====================
&-show-line {
// ================ Indent lines ================
.@{custom-tree-prefix-cls}-indent {
&-unit {
position: relative;
height: 100%;
&:first-child::after {
position: absolute;
top: calc(100% - @tree-title-height - 4px);
right: @tree-title-height / 2;
bottom: -4px;
border-right: 1px solid @border-color-base;
content: '';
}
&::before {
position: absolute;
height: @tree-title-height;
margin-left: -1px;
border-left: 1px solid @normal-color;
content: ' ';
top: calc(100% - 4px);
right: -@tree-title-height / 2;
bottom: -@tree-title-height - 4px;
border-right: 1px solid @border-color-base;
content: '';
}
&::after {
position: absolute;
width: @tree-title-height - 14px;
height: @tree-title-height - 10px;
margin-left: -1px;
border-bottom: 1px solid @normal-color;
content: ' ';
}
}
}
// >>> Checkbox
& &-checkbox {
top: initial;
margin: ((@tree-title-height - @checkbox-size) / 2) 8px 0 0;
}
// >>> Title
& &-node-content-wrapper {
min-height: @tree-title-height;
margin: 0;
padding: 0 4px;
color: inherit;
line-height: @tree-title-height;
background: transparent;
border-radius: @border-radius-base;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: @tree-node-hover-bg;
}
&.@{custom-tree-prefix-cls}-node-selected {
background-color: @tree-node-selected-bg;
}
// Icon
.@{custom-tree-prefix-cls}-iconEle {
display: inline-block;
width: @tree-title-height;
height: @tree-title-height;
line-height: @tree-title-height;
text-align: center;
vertical-align: top;
&:empty {
&-end::before,
&-end-first-level::after {
display: none;
}
}
}
// ==================== Draggable =====================
&-node-content-wrapper[draggable='true'] {
line-height: @tree-title-height - 4px;
border-top: 2px transparent solid;
border-bottom: 2px transparent solid;
user-select: none;
}
.@{custom-tree-node-prefix-cls}.drag-over {
> [draggable] {
color: white;
background-color: @primary-color;
opacity: 0.8;
}
}
.@{custom-tree-node-prefix-cls}.drag-over-gap-top {
> [draggable] {
border-top-color: @primary-color;
}
}
.@{custom-tree-node-prefix-cls}.drag-over-gap-bottom {
> [draggable] {
border-bottom-color: @primary-color;
}
}
// ==================== Show Line =====================
&-show-line {
// ================ Indent lines ================
.@{custom-tree-prefix-cls}-indent {
&-unit {
position: relative;
height: 100%;
&:first-child::after {
position: absolute;
top: calc(100% - @tree-title-height - 4px);
right: @tree-title-height / 2;
bottom: -4px;
border-right: 1px solid @border-color-base;
content: '';
}
&::before {
position: absolute;
top: calc(100% - 4px);
right: -@tree-title-height / 2;
bottom: -@tree-title-height - 4px;
border-right: 1px solid @border-color-base;
content: '';
}
&-end::before,
&-end-first-level::after {
display: none;
}
/* Motion should hide line of measure */
.@{custom-tree-node-prefix-cls}-motion:not(.@{tree-motion}-leave):not(.@{tree-motion}-appear-active) {
.@{custom-tree-prefix-cls}-indent-unit {
&::after,
&::before {
display: none;
}
}
}
/* Motion should hide line of measure */
.@{custom-tree-node-prefix-cls}-motion:not(.@{tree-motion}-leave):not(.@{tree-motion}-appear-active) {
.@{custom-tree-prefix-cls}-indent-unit {
&::after,
&::before {
display: none;
}
}
}
// ============== Cover Background ==============
.@{custom-tree-prefix-cls}-switcher {
z-index: 1;
background: @component-background;
}
// ============== Cover Background ==============
.@{custom-tree-prefix-cls}-switcher {
z-index: 1;
background: @component-background;
}
}
}

View File

@@ -5,6 +5,7 @@ import Dragger from './Dragger';
import UploadList from './UploadList';
import {
RcFile,
ShowUploadListInterface,
UploadProps,
UploadFile,
UploadLocale,
@@ -17,7 +18,6 @@ import LocaleReceiver from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale/default';
import { ConfigContext } from '../config-provider';
import devWarning from '../_util/devWarning';
import useSyncState from '../_util/hooks/useSyncState';
import useForceUpdate from '../_util/hooks/useForceUpdate';
export { UploadProps };
@@ -44,15 +44,18 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
style,
} = props;
const [getFileList, setFileList] = useSyncState<Array<UploadFile>>(
fileListProp || defaultFileList || [],
);
const [dragState, setDragState] = React.useState<string>('drop');
const forceUpdate = useForceUpdate();
// `fileListRef` used for internal state sync to avoid control mode set it back when sync update.
// `visibleFileList` used for display in UploadList instead.
// It's a workaround and not the best solution.
const fileListRef = React.useRef(fileListProp || defaultFileList || []);
const visibleFileList = fileListProp || fileListRef.current;
const upload = React.useRef<any>();
React.useEffect(() => {
setFileList(fileListProp || defaultFileList || []);
devWarning(
'fileList' in props || !('value' in props),
'Upload',
@@ -61,15 +64,15 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
}, []);
React.useEffect(() => {
if ('fileList' in props) {
setFileList(fileListProp || []);
if (fileListProp !== undefined && fileListProp !== fileListRef.current) {
fileListRef.current = fileListProp;
forceUpdate();
}
}, [fileListProp]);
const onChange = (info: UploadChangeParam) => {
if (!('fileList' in props)) {
setFileList(info.fileList);
}
fileListRef.current = info.fileList;
forceUpdate();
const { onChange: onChangeProp } = props;
if (onChangeProp) {
@@ -84,7 +87,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
const targetItem = fileToObject(file);
targetItem.status = 'uploading';
const nextFileList = getFileList().concat();
const nextFileList = fileListRef.current.concat();
const fileIndex = nextFileList.findIndex(({ uid }: UploadFile) => uid === targetItem.uid);
if (fileIndex === -1) {
@@ -107,7 +110,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
} catch (e) {
/* do nothing */
}
const targetItem = getFileItem(file, getFileList());
const targetItem = getFileItem(file, fileListRef.current);
// removed
if (!targetItem) {
return;
@@ -117,12 +120,12 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
targetItem.xhr = xhr;
onChange({
file: { ...targetItem },
fileList: getFileList().concat(),
fileList: fileListRef.current.concat(),
});
};
const onProgress = (e: { percent: number }, file: UploadFile) => {
const targetItem = getFileItem(file, getFileList());
const targetItem = getFileItem(file, fileListRef.current);
// removed
if (!targetItem) {
return;
@@ -131,12 +134,12 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
onChange({
event: e,
file: { ...targetItem },
fileList: getFileList().concat(),
fileList: fileListRef.current.concat(),
});
};
const onError = (error: Error, response: any, file: UploadFile) => {
const targetItem = getFileItem(file, getFileList());
const targetItem = getFileItem(file, fileListRef.current);
// removed
if (!targetItem) {
return;
@@ -146,7 +149,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
targetItem.status = 'error';
onChange({
file: { ...targetItem },
fileList: getFileList().concat(),
fileList: fileListRef.current.concat(),
});
};
@@ -157,7 +160,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
return;
}
const removedFileList = removeFileItem(file, getFileList());
const removedFileList = removeFileItem(file, fileListRef.current);
if (removedFileList) {
file.status = 'removed';
@@ -186,13 +189,11 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
if (result === false) {
// Get unique file list
const uniqueList: UploadFile<any>[] = [];
getFileList()
.concat(fileListArgs.map(fileToObject))
.forEach(f => {
if (uniqueList.every(uf => uf.uid !== f.uid)) {
uniqueList.push(f);
}
});
fileListRef.current.concat(fileListArgs.map(fileToObject)).forEach(f => {
if (uniqueList.every(uf => uf.uid !== f.uid)) {
uniqueList.push(f);
}
});
onChange({
file,
@@ -206,46 +207,16 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
return true;
};
// Test needs
const forceUpdate = useForceUpdate();
React.useImperativeHandle(ref, () => ({
onStart,
onSuccess,
onProgress,
onError,
fileList: getFileList(),
fileList: fileListRef.current,
upload: upload.current,
forceUpdate,
}));
const renderUploadList = (locale: UploadLocale) => {
const {
showRemoveIcon,
showPreviewIcon,
showDownloadIcon,
removeIcon,
downloadIcon,
} = showUploadList as any;
return (
<UploadList
listType={listType}
items={getFileList()}
previewFile={previewFile}
onPreview={onPreview}
onDownload={onDownload}
onRemove={handleRemove}
showRemoveIcon={!disabled && showRemoveIcon}
showPreviewIcon={showPreviewIcon}
showDownloadIcon={showDownloadIcon}
removeIcon={removeIcon}
downloadIcon={downloadIcon}
iconRender={iconRender}
locale={{ ...locale, ...propLocale }}
isImageUrl={isImageUrl}
progress={progress}
/>
);
};
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('upload', customizePrefixCls);
@@ -271,18 +242,46 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
delete rcUploadProps.id;
}
const uploadList = showUploadList ? (
<LocaleReceiver componentName="Upload" defaultLocale={defaultLocale.Upload}>
{renderUploadList}
</LocaleReceiver>
) : null;
const renderUploadList = (button?: React.ReactNode) =>
showUploadList ? (
<LocaleReceiver componentName="Upload" defaultLocale={defaultLocale.Upload}>
{(locale: UploadLocale) => {
const { showRemoveIcon, showPreviewIcon, showDownloadIcon, removeIcon, downloadIcon } =
typeof showUploadList === 'boolean' ? ({} as ShowUploadListInterface) : showUploadList;
return (
<UploadList
listType={listType}
items={visibleFileList}
previewFile={previewFile}
onPreview={onPreview}
onDownload={onDownload}
onRemove={handleRemove}
showRemoveIcon={!disabled && showRemoveIcon}
showPreviewIcon={showPreviewIcon}
showDownloadIcon={showDownloadIcon}
removeIcon={removeIcon}
downloadIcon={downloadIcon}
iconRender={iconRender}
locale={{ ...locale, ...propLocale }}
isImageUrl={isImageUrl}
progress={progress}
appendAction={button}
/>
);
}}
</LocaleReceiver>
) : (
button
);
if (type === 'drag') {
const dragCls = classNames(
prefixCls,
{
[`${prefixCls}-drag`]: true,
[`${prefixCls}-drag-uploading`]: getFileList().some(file => file.status === 'uploading'),
[`${prefixCls}-drag-uploading`]: fileListRef.current.some(
file => file.status === 'uploading',
),
[`${prefixCls}-drag-hover`]: dragState === 'dragover',
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-rtl`]: direction === 'rtl',
@@ -302,7 +301,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
<div className={`${prefixCls}-drag-container`}>{children}</div>
</RcUpload>
</div>
{uploadList}
{renderUploadList()}
</span>
);
}
@@ -323,8 +322,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
if (listType === 'picture-card') {
return (
<span className={classNames(className, `${prefixCls}-picture-card-wrapper`)}>
{uploadList}
{uploadButton}
{renderUploadList(uploadButton)}
</span>
);
}
@@ -332,13 +330,15 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
return (
<span className={className}>
{uploadButton}
{uploadList}
{renderUploadList()}
</span>
);
};
interface CompoundedComponent
extends React.ForwardRefExoticComponent<UploadProps & React.RefAttributes<any>> {
extends React.ForwardRefExoticComponent<
React.PropsWithChildren<UploadProps> & React.RefAttributes<any>
> {
Dragger: typeof Dragger;
}

View File

@@ -36,6 +36,7 @@ const InternalUploadList: React.ForwardRefRenderFunction<unknown, UploadListProp
removeIcon: customRemoveIcon,
downloadIcon: customDownloadIcon,
progress: progressProps,
appendAction,
},
ref,
) => {
@@ -334,13 +335,13 @@ const InternalUploadList: React.ForwardRefRenderFunction<unknown, UploadListProp
[`${prefixCls}-list-rtl`]: direction === 'rtl',
});
const animationDirection = listType === 'picture-card' ? 'animate-inline' : 'animate';
const transitionName = list.length === 0 ? '' : `${prefixCls}-${animationDirection}`;
return (
<Animate
transitionName={`${prefixCls}-${animationDirection}`}
component="div"
className={listClassNames}
>
<Animate transitionName={transitionName} component="div" className={listClassNames}>
{list}
{React.isValidElement(appendAction)
? React.cloneElement(appendAction, { key: 'appendAction' })
: appendAction}
</Animate>
);
};

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@ exports[`Upload List handle error 1`] = `
class="ant-upload-list ant-upload-list-text"
>
<div
class="ant-upload-animate-enter"
class=""
>
<div
class="ant-upload-list-item ant-upload-list-item-error ant-upload-list-item-list-type-text"
@@ -262,7 +262,7 @@ exports[`Upload List should be uploading when upload a file 2`] = `
class="ant-upload-list ant-upload-list-text"
>
<div
class="ant-upload-animate-enter"
class=""
>
<span>
<div

View File

@@ -0,0 +1,30 @@
import React from 'react';
import Upload from '..';
describe('Upload.typescript', () => {
it('Upload', () => {
const upload = (
<Upload>
<span>click to upload</span>
</Upload>
);
expect(upload).toBeTruthy();
});
it('showUploadList', () => {
const upload = (
<Upload
showUploadList={{
showPreviewIcon: true,
showRemoveIcon: true,
showDownloadIcon: true,
removeIcon: 'Remove',
downloadIcon: 'Download',
}}
>
<span>click to upload</span>
</Upload>
);
expect(upload).toBeTruthy();
});
});

View File

@@ -1,6 +1,7 @@
/* eslint-disable react/no-string-refs, react/prefer-es6-class */
import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import Upload from '..';
import Form from '../../form';
import { T, fileToObject, getFileItem, removeFileItem } from '../utils';
@@ -554,4 +555,66 @@ describe('Upload', () => {
wrapper.find('.ant-upload').at(1).simulate('mouseLeave');
expect(onMouseLeave).toHaveBeenCalled();
});
// https://github.com/ant-design/ant-design/issues/26427
it('should sync file list with control mode', done => {
let callTimes = 0;
const customRequest = jest.fn(async options => {
options.onProgress({ percent: 0 });
const url = Promise.resolve('https://ant.design');
options.onProgress({ percent: 100 });
options.onSuccess({}, { ...options.file, url });
});
const Demo = () => {
const [fileList, setFileList] = React.useState([]);
const onChange = e => {
const newFileList = Array.isArray(e) ? e : e.fileList;
setFileList(newFileList);
const file = newFileList[0];
callTimes += 1;
switch (callTimes) {
case 1:
case 2:
expect(file).toEqual(expect.objectContaining({ status: 'uploading', percent: 0 }));
break;
case 3:
expect(file).toEqual(expect.objectContaining({ status: 'uploading', percent: 100 }));
break;
case 4:
expect(file).toEqual(expect.objectContaining({ status: 'done', percent: 100 }));
break;
default:
// Do nothing
}
if (callTimes >= 4) {
done();
}
};
return (
<Upload customRequest={customRequest} onChange={onChange} fileList={fileList}>
<button type="button">Upload</button>
</Upload>
);
};
const wrapper = mount(<Demo />);
act(() => {
wrapper.find('input').simulate('change', {
target: {
files: [{ file: 'foo.png' }],
},
});
});
});
});

View File

@@ -1,4 +1,5 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme';
import Upload from '..';
import UploadList from '../UploadList';
@@ -801,14 +802,23 @@ describe('Upload List', () => {
const nonImageFile = new File([''], 'foo.7z', { type: 'application/x-7z-compressed' });
it('should render <img /> when upload non-image file and configure thumbUrl in onChange', done => {
let wrapper;
const onChange = async ({ fileList: files }) => {
const newfileList = files.map(item => ({
const onChange = async ({ file, fileList: files }) => {
const newFileList = files.map(item => ({
...item,
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
}));
wrapper.setProps({ fileList: newfileList });
await sleep();
wrapper.setProps({ fileList: newFileList });
if (file.status === 'uploading') {
return;
}
act(async () => {
await sleep();
wrapper.update();
});
const imgNode = wrapper.find('.ant-upload-list-item-thumbnail img');
expect(imgNode.length).toBe(1);
done();
@@ -880,4 +890,27 @@ describe('Upload List', () => {
},
});
});
it('should render button inside UploadList when listStyle is picture-card', () => {
const wrapper = mount(
<Upload
action="http://jsonplaceholder.typicode.com/posts/"
listType="picture-card"
fileList={[
{
uid: '0',
name: 'xxx.png',
},
]}
showUploadList
>
<button className="trigger" type="button">
upload
</button>
</Upload>,
);
expect(wrapper.exists('.ant-upload-list button.trigger')).toBe(true);
wrapper.setProps({ showUploadList: false });
expect(wrapper.exists('.ant-upload-list button.trigger')).toBe(false);
});
});

View File

@@ -61,13 +61,13 @@ class Avatar extends React.Component {
};
render() {
const { loading, imageUrl } = this.state;
const uploadButton = (
<div>
{this.state.loading ? <LoadingOutlined /> : <PlusOutlined />}
<div className="ant-upload-text">Upload</div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
const { imageUrl } = this.state;
return (
<Upload
name="avatar"

View File

@@ -37,9 +37,7 @@ const props = {
ReactDOM.render(
<Upload {...props}>
<Button>
<UploadOutlined /> Click to Upload
</Button>
<Button icon={<UploadOutlined />}>Click to Upload</Button>
</Upload>,
mountNode,
);

View File

@@ -45,9 +45,7 @@ const props = {
ReactDOM.render(
<Upload {...props}>
<Button>
<UploadOutlined /> Click to Upload
</Button>
<Button icon={<UploadOutlined />}>Click to Upload</Button>
</Upload>,
mountNode,
);

View File

@@ -50,9 +50,7 @@ const props = {
ReactDOM.render(
<Upload {...props}>
<Button>
<UploadOutlined /> Upload
</Button>
<Button icon={<UploadOutlined />}>Upload</Button>
</Upload>,
mountNode,
);

View File

@@ -19,9 +19,7 @@ import { UploadOutlined } from '@ant-design/icons';
ReactDOM.render(
<Upload action="https://www.mocky.io/v2/5cc8019d300000980a055e76" directory>
<Button>
<UploadOutlined /> Upload Directory
</Button>
<Button icon={<UploadOutlined />}>Upload Directory</Button>
</Upload>,
mountNode,
);

View File

@@ -116,11 +116,11 @@ class PicturesWall extends React.Component {
const uploadButton = (
<div>
<PlusOutlined />
<div className="ant-upload-text">Upload</div>
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
return (
<div className="clearfix">
<>
<Upload
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
listType="picture-card"
@@ -134,23 +134,10 @@ class PicturesWall extends React.Component {
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
</>
);
}
}
ReactDOM.render(<PicturesWall />, mountNode);
```
```css
/* you can make up upload button and sample style by using stylesheets */
.ant-upload-select-picture-card i {
color: #999;
font-size: 32px;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
```

View File

@@ -64,9 +64,7 @@ class MyUpload extends React.Component {
};
return (
<Upload {...props} fileList={this.state.fileList}>
<Button>
<UploadOutlined /> Upload
</Button>
<Button icon={<UploadOutlined />}>Upload</Button>
</Upload>
);
}

View File

@@ -56,6 +56,13 @@ class PicturesWall extends React.Component {
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
{
uid: '-xxx',
percent: 50,
name: 'image.png',
status: 'uploading',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
{
uid: '-5',
name: 'image.png',
@@ -85,11 +92,11 @@ class PicturesWall extends React.Component {
const uploadButton = (
<div>
<PlusOutlined />
<div className="ant-upload-text">Upload</div>
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
return (
<div className="clearfix">
<>
<Upload
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
listType="picture-card"
@@ -107,23 +114,10 @@ class PicturesWall extends React.Component {
>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
</>
);
}
}
ReactDOM.render(<PicturesWall />, mountNode);
```
```css
/* you can make up upload button and sample style by using stylesheets */
.ant-upload-select-picture-card i {
color: #999;
font-size: 32px;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
```

View File

@@ -32,32 +32,24 @@ const fileList = [
},
];
const props = {
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
listType: 'picture',
defaultFileList: [...fileList],
};
const props2 = {
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
listType: 'picture',
defaultFileList: [...fileList],
className: 'upload-list-inline',
};
ReactDOM.render(
<>
<Upload {...props}>
<Button>
<UploadOutlined /> Upload
</Button>
<Upload
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
listType="picture"
defaultFileList={[...fileList]}
>
<Button icon={<UploadOutlined />}>Upload</Button>
</Upload>
<br />
<br />
<Upload {...props2}>
<Button>
<UploadOutlined /> Upload
</Button>
<Upload
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
listType="picture"
defaultFileList={[...fileList]}
className="upload-list-inline"
>
<Button icon={<UploadOutlined />}>Upload</Button>
</Upload>
</>,
mountNode,
@@ -75,10 +67,4 @@ ReactDOM.render(
.upload-list-inline [class*='-upload-list-rtl'] .ant-upload-list-item {
float: right;
}
.upload-list-inline .ant-upload-animate-enter {
animation-name: uploadAnimateInlineIn;
}
.upload-list-inline .ant-upload-animate-leave {
animation-name: uploadAnimateInlineOut;
}
```

View File

@@ -34,9 +34,7 @@ const props = {
ReactDOM.render(
<Upload {...props}>
<Button>
<UploadOutlined /> Upload
</Button>
<Button icon={<UploadOutlined />}>Upload</Button>
</Upload>,
mountNode,
);

View File

@@ -43,9 +43,7 @@ const props = {
ReactDOM.render(
<>
<Upload {...props}>
<Button>
<UploadOutlined /> Upload
</Button>
<Button icon={<UploadOutlined />}>Upload</Button>
</Upload>
</>,
mountNode,

View File

@@ -56,9 +56,7 @@ const props = {
ReactDOM.render(
<Upload {...props}>
<Button>
<UploadOutlined /> Upload
</Button>
<Button icon={<UploadOutlined />}>Upload</Button>
</Upload>,
mountNode,
);

View File

@@ -82,9 +82,7 @@ class Demo extends React.Component {
return (
<>
<Upload {...props}>
<Button>
<UploadOutlined /> Select File
</Button>
<Button icon={<UploadOutlined />}>Select File</Button>
</Upload>
<Button
type="primary"

View File

@@ -36,9 +36,7 @@ const Uploader = () => {
};
return (
<Upload {...props}>
<Button>
<UploadOutlined /> Upload png only
</Button>
<Button icon={<UploadOutlined />}>Upload png only</Button>
</Upload>
);
};

View File

@@ -114,9 +114,7 @@ class AliyunOSSUpload extends React.Component {
};
return (
<Upload {...props}>
<Button>
<UploadOutlined /> Click to Upload
</Button>
<Button icon={<UploadOutlined />}>Click to Upload</Button>
</Upload>
);
}

View File

@@ -56,6 +56,8 @@ export interface ShowUploadListInterface {
showRemoveIcon?: boolean;
showPreviewIcon?: boolean;
showDownloadIcon?: boolean;
removeIcon?: React.ReactNode;
downloadIcon?: React.ReactNode;
}
export interface UploadLocale {
@@ -133,4 +135,5 @@ export interface UploadListProps<T = any> {
previewFile?: PreviewFileHandler;
iconRender?: (file: UploadFile<T>, listType?: UploadListType) => React.ReactNode;
isImageUrl?: (file: UploadFile) => boolean;
appendAction?: React.ReactNode;
}

View File

@@ -34,8 +34,6 @@
}
&&-select-picture-card {
display: table;
float: left;
width: @upload-picture-card-size;
height: @upload-picture-card-size;
margin-right: 8px;
@@ -46,15 +44,14 @@
border: @border-width-base dashed @border-color-base;
border-radius: @border-radius-base;
cursor: pointer;
transition: border-color 0.3s ease;
transition: border-color 0.3s;
> .@{upload-prefix-cls} {
display: table-cell;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: @padding-xs;
text-align: center;
vertical-align: middle;
}
&:hover {
@@ -408,17 +405,16 @@
}
&-container {
float: left;
display: inline-block;
width: @upload-picture-card-size;
height: @upload-picture-card-size;
margin: 0 @margin-xs @margin-xs 0;
vertical-align: top;
}
.@{upload-item} {
float: left;
width: @upload-picture-card-size;
height: @upload-picture-card-size;
margin: 0 @margin-xs @margin-xs 0;
height: 100%;
margin: 0;
}
.@{upload-item}-info {
@@ -515,6 +511,7 @@
.@{upload-item}-progress {
bottom: 32px;
width: calc(100% - 14px);
padding-left: 0;
}
}

View File

@@ -7,7 +7,7 @@ title: 结果页
结果页是用一个页面反馈操作结果,是反馈模式中最强的一种。
## 何时使用
## 何时使用
当完成一个流程操作后,需给与用户明确的结果反馈时,例如分步表单的最后一步。<br/> 当有大量的信息需要在结果页展示时。

View File

@@ -1,6 +1,6 @@
{
"name": "antd",
"version": "4.6.0",
"version": "4.6.2",
"description": "An enterprise-class UI design language and React components implementation",
"title": "Ant Design",
"keywords": [
@@ -147,8 +147,8 @@
"rc-tree": "~3.9.0",
"rc-tree-select": "~4.1.1",
"rc-trigger": "~4.4.0",
"rc-upload": "~3.2.0",
"rc-util": "^5.0.1",
"rc-upload": "~3.2.1",
"rc-util": "^5.1.0",
"scroll-into-view-if-needed": "^2.2.25",
"warning": "^4.0.3"
},

View File

@@ -90,7 +90,12 @@ module.exports = {
config.resolve.alias = { ...config.resolve.alias, react: require.resolve('react') };
} else if (process.env.ESBUILD) {
// use esbuild
config.optimization.minimizer = [new EsbuildPlugin(), new CssMinimizerPlugin()];
config.optimization.minimizer = [
new EsbuildPlugin({
target: 'chrome49',
}),
new CssMinimizerPlugin(),
];
}
alertBabelConfig(config.module.rules);

View File

@@ -135,7 +135,7 @@ class MainContent extends Component {
return (
<Menu.ItemGroup title={menuItem.title} key={menuItem.title}>
{menuItem.children
.sort((a, b) => a.title.charCodeAt(0) - b.title.charCodeAt(0))
.sort((a, b) => a.title.localeCompare(b.title))
.map(leaf => this.generateMenuItem(false, leaf, footerNavIcons))}
</Menu.ItemGroup>
);

View File

@@ -99,7 +99,9 @@ if (process.env.RUN_ENV === 'PRODUCTION') {
config.optimization.usedExports = true;
// use esbuild
if (process.env.ESBUILD || process.env.CSB_REPO) {
config.optimization.minimizer[0] = new EsbuildPlugin();
config.optimization.minimizer[0] = new EsbuildPlugin({
target: 'chrome49',
});
}
config.plugins.push(