mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 06:03:38 +08:00
chore: merge master
This commit is contained in:
commit
91245c16ad
@ -15,6 +15,17 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.21.3
|
||||
|
||||
`2022-06-17`
|
||||
|
||||
- 🐞 Fix Table customize `filterDropdown` with Menu should not break default `selectable`. [#36098](https://github.com/ant-design/ant-design/pull/36098)
|
||||
- 🐞 Fix Input.Textarea cannot focus after click clear icon in controlled mode. [#34728](https://github.com/ant-design/ant-design/pull/34728) [@Pulset](https://github.com/Pulset)
|
||||
- TypeScript
|
||||
- 🤖 Tree.DirectoryTree supports generic DataNode type. [#36092](https://github.com/ant-design/ant-design/pull/36092) [@JaylanChen](https://github.com/JaylanChen)
|
||||
- 🤖 Export `RefSelectProps` from `Select`. [#34732](https://github.com/ant-design/ant-design/pull/34732) [@chentsulin](https://github.com/chentsulin)
|
||||
- 🤖 Export `FormRule`, `FormListFieldData`, `FormListOperation` from `Form`. [#34735](https://github.com/ant-design/ant-design/pull/34735) [@chentsulin](https://github.com/chentsulin)
|
||||
|
||||
## 4.21.2
|
||||
|
||||
`2022-06-15`
|
||||
|
@ -15,6 +15,17 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.21.3
|
||||
|
||||
`2022-06-17`
|
||||
|
||||
- 🐞 修复 Table 自定义 `filterDropdown` 中使用 Menu 会被修改默认 `selectable` 的问题。[#36098](https://github.com/ant-design/ant-design/pull/36098)
|
||||
- 🐞 修复 Input.Textarea 受控时点击清除图标后无法聚焦的问题。[#34728](https://github.com/ant-design/ant-design/pull/34728) [@Pulset](https://github.com/Pulset)
|
||||
- TypeScript
|
||||
- 🤖 修复 Tree.DirectoryTree 不支持泛型的问题。[#36092](https://github.com/ant-design/ant-design/pull/36092) [@JaylanChen](https://github.com/JaylanChen)
|
||||
- 🤖 从 `Select` 导出类型 `RefSelectProps`。[#34732](https://github.com/ant-design/ant-design/pull/34732) [@chentsulin](https://github.com/chentsulin)
|
||||
- 🤖 从 Form 导出类型 `FormRule`、`FormListFieldData`、`FormListOperation`。[#34735](https://github.com/ant-design/ant-design/pull/34735) [@chentsulin](https://github.com/chentsulin)
|
||||
|
||||
## 4.21.2
|
||||
|
||||
`2022-06-15`
|
||||
|
@ -1,15 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import ResizeObserver from 'rc-resize-observer';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import warning from '../_util/warning';
|
||||
import useBreakpoint from '../grid/hooks/useBreakpoint';
|
||||
import type { Breakpoint } from '../_util/responsiveObserve';
|
||||
import { responsiveArray } from '../_util/responsiveObserve';
|
||||
import useBreakpoint from '../grid/hooks/useBreakpoint';
|
||||
import useStyle from './style';
|
||||
import warning from '../_util/warning';
|
||||
import type { AvatarSize } from './SizeContext';
|
||||
import SizeContext from './SizeContext';
|
||||
import useStyle from './style';
|
||||
|
||||
export interface AvatarProps {
|
||||
/** Shape of avatar, options: `circle`, `square` */
|
||||
@ -241,7 +241,9 @@ const InternalAvatar: React.ForwardRefRenderFunction<unknown, AvatarProps> = (pr
|
||||
};
|
||||
|
||||
const Avatar = React.forwardRef<unknown, AvatarProps>(InternalAvatar);
|
||||
Avatar.displayName = 'Avatar';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Avatar.displayName = 'Avatar';
|
||||
}
|
||||
|
||||
Avatar.defaultProps = {
|
||||
shape: 'circle' as AvatarProps['shape'],
|
||||
|
@ -318,8 +318,9 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
|
||||
};
|
||||
|
||||
const Button = React.forwardRef<unknown, ButtonProps>(InternalButton) as CompoundedComponent;
|
||||
|
||||
Button.displayName = 'Button';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Button.displayName = 'Button';
|
||||
}
|
||||
|
||||
Button.Group = Group;
|
||||
Button.__ANT_BUTTON = true;
|
||||
|
@ -50,4 +50,9 @@ When data is in the form of dates, such as schedules, timetables, prices calenda
|
||||
|
||||
## FAQ
|
||||
|
||||
- [How to use Calendar with customize date library](/docs/react/use-custom-date-library#Calendar)
|
||||
<<<<<<< HEAD
|
||||
|
||||
- # [How to use Calendar with customize date library](/docs/react/use-custom-date-library#Calendar)
|
||||
- [How to use Calendar with customize date library like dayjs](/docs/react/replace-moment#Calendar)
|
||||
- [How to set locale for date-related components](/components/date-picker/#Localization)
|
||||
> > > > > > > origin/master
|
||||
|
@ -51,4 +51,9 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/dPQmLq08DI/Calendar.svg
|
||||
|
||||
## FAQ
|
||||
|
||||
- [如何在 Calendar 中使用自定义日期库](/docs/react/use-custom-date-library#Calendar)
|
||||
<<<<<<< HEAD
|
||||
|
||||
- # [如何在 Calendar 中使用自定义日期库](/docs/react/use-custom-date-library#Calendar)
|
||||
- [如何在 Calendar 中使用自定义日期库(如 dayjs )](/docs/react/replace-moment#Calendar)
|
||||
- [如何给日期类组件配置国际化](/components/date-picker/#%E5%9B%BD%E9%99%85%E5%8C%96%E9%85%8D%E7%BD%AE)
|
||||
> > > > > > > origin/master
|
||||
|
@ -1,33 +1,34 @@
|
||||
import * as React from 'react';
|
||||
import LeftOutlined from '@ant-design/icons/LeftOutlined';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import RightOutlined from '@ant-design/icons/RightOutlined';
|
||||
import classNames from 'classnames';
|
||||
import RcCascader from 'rc-cascader';
|
||||
import type {
|
||||
SingleCascaderProps as RcSingleCascaderProps,
|
||||
MultipleCascaderProps as RcMultipleCascaderProps,
|
||||
ShowSearchType,
|
||||
FieldNames,
|
||||
BaseOptionType,
|
||||
DefaultOptionType,
|
||||
FieldNames,
|
||||
MultipleCascaderProps as RcMultipleCascaderProps,
|
||||
ShowSearchType,
|
||||
SingleCascaderProps as RcSingleCascaderProps,
|
||||
} from 'rc-cascader';
|
||||
import RcCascader from 'rc-cascader';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import RightOutlined from '@ant-design/icons/RightOutlined';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import LeftOutlined from '@ant-design/icons/LeftOutlined';
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import warning from '../_util/warning';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import getIcons from '../select/utils/iconUtil';
|
||||
import type { SelectCommonPlacement } from '../_util/motion';
|
||||
import { getTransitionName, getTransitionDirection } from '../_util/motion';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import useStyle from './style';
|
||||
import useSelectStyle from '../select/style';
|
||||
import { getTransitionDirection, getTransitionName } from '../_util/motion';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import warning from '../_util/warning';
|
||||
|
||||
import useSelectStyle from '../select/style';
|
||||
import useStyle from './style';
|
||||
|
||||
// Align the design since we use `rc-select` in root. This help:
|
||||
// - List search content will show all content
|
||||
@ -317,8 +318,9 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
SHOW_PARENT: typeof SHOW_PARENT;
|
||||
SHOW_CHILD: typeof SHOW_CHILD;
|
||||
};
|
||||
|
||||
Cascader.displayName = 'Cascader';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Cascader.displayName = 'Cascader';
|
||||
}
|
||||
Cascader.SHOW_PARENT = SHOW_PARENT;
|
||||
Cascader.SHOW_CHILD = SHOW_CHILD;
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import RcCheckbox from 'rc-checkbox';
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import { GroupContext } from './Group';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import warning from '../_util/warning';
|
||||
import { GroupContext } from './Group';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
export interface AbstractCheckboxProps<T> {
|
||||
@ -143,7 +144,8 @@ const InternalCheckbox: React.ForwardRefRenderFunction<HTMLInputElement, Checkbo
|
||||
};
|
||||
|
||||
const Checkbox = React.forwardRef<unknown, CheckboxProps>(InternalCheckbox);
|
||||
|
||||
Checkbox.displayName = 'Checkbox';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Checkbox.displayName = 'Checkbox';
|
||||
}
|
||||
|
||||
export default Checkbox;
|
||||
|
@ -117,8 +117,9 @@ export function withConfigConsumer<ExportProps extends BasicExportProps>(config:
|
||||
const cons: ConstructorProps = Component.constructor as ConstructorProps;
|
||||
const name = (cons && cons.displayName) || Component.name || 'Component';
|
||||
|
||||
SFC.displayName = `withConfigConsumer(${name})`;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
SFC.displayName = `withConfigConsumer(${name})`;
|
||||
}
|
||||
return SFC;
|
||||
};
|
||||
}
|
||||
|
@ -337,7 +337,8 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Drawer.displayName = 'Drawer';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Drawer.displayName = 'Drawer';
|
||||
}
|
||||
|
||||
export default Drawer;
|
||||
|
@ -8194,6 +8194,232 @@ exports[`renders ./components/dropdown/demo/placement.md extend context correctl
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/dropdown/demo/selectable.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<a
|
||||
class="ant-typography ant-dropdown-trigger"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
Selectable
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</a>,
|
||||
<div>
|
||||
<div
|
||||
class="ant-dropdown"
|
||||
style="opacity:0"
|
||||
>
|
||||
<ul
|
||||
class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-vertical ant-dropdown-menu-light"
|
||||
data-menu-list="true"
|
||||
role="menu"
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
Item 1
|
||||
</span>
|
||||
</li>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<li
|
||||
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
Item 2
|
||||
</span>
|
||||
</li>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<li
|
||||
class="ant-dropdown-menu-item ant-dropdown-menu-item-selected ant-dropdown-menu-item-only-child"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
Item 3
|
||||
</span>
|
||||
</li>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
style="display:none"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/dropdown/demo/sub-menu.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<a
|
||||
|
@ -858,6 +858,46 @@ exports[`renders ./components/dropdown/demo/placement.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/dropdown/demo/selectable.md correctly 1`] = `
|
||||
<a
|
||||
class="ant-typography ant-dropdown-trigger"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
Selectable
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/dropdown/demo/sub-menu.md correctly 1`] = `
|
||||
<a
|
||||
class="ant-dropdown-trigger"
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import Dropdown from '..';
|
||||
import Menu from '../../menu';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import { act, fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import Menu from '../../menu';
|
||||
|
||||
describe('Dropdown', () => {
|
||||
mountTest(() => (
|
||||
@ -99,4 +99,54 @@ describe('Dropdown', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('menu item with group', () => {
|
||||
jest.useFakeTimers();
|
||||
const { container } = render(
|
||||
<Dropdown
|
||||
trigger="click"
|
||||
overlay={
|
||||
<Menu
|
||||
items={[
|
||||
{
|
||||
label: 'grp',
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
label: '1',
|
||||
key: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<a />
|
||||
</Dropdown>,
|
||||
);
|
||||
|
||||
// Open
|
||||
fireEvent.click(container.querySelector('a'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
// Close
|
||||
fireEvent.click(container.querySelector('.ant-dropdown-menu-item'));
|
||||
|
||||
// Force Motion move on
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
}
|
||||
|
||||
// Motion End
|
||||
fireEvent.animationEnd(container.querySelector('.ant-slide-up-leave-active'));
|
||||
|
||||
expect(container.querySelector('.ant-dropdown-hidden')).toBeTruthy();
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
54
components/dropdown/demo/selectable.md
Normal file
54
components/dropdown/demo/selectable.md
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
order: 10
|
||||
title:
|
||||
zh-CN: 菜单可选选择
|
||||
en-US: Selectable Menu
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
为 Menu 添加 `selectable` 属性可以开启选择能力。
|
||||
|
||||
## en-US
|
||||
|
||||
Config Menu `selectable` prop to enable selectable ability.
|
||||
|
||||
```tsx
|
||||
import { DownOutlined } from '@ant-design/icons';
|
||||
import { Dropdown, Menu, Space, Typography } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const menu = (
|
||||
<Menu
|
||||
selectable
|
||||
defaultSelectedKeys={['3']}
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
label: 'Item 1',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: 'Item 2',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: 'Item 3',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Dropdown overlay={menu}>
|
||||
<Typography.Link>
|
||||
<Space>
|
||||
Selectable
|
||||
<DownOutlined />
|
||||
</Space>
|
||||
</Typography.Link>
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
export default App;
|
||||
```
|
@ -1,10 +1,11 @@
|
||||
import RightOutlined from '@ant-design/icons/RightOutlined';
|
||||
import classNames from 'classnames';
|
||||
import RcDropdown from 'rc-dropdown';
|
||||
import useEvent from 'rc-util/lib/hooks/useEvent';
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { OverrideContextProps } from '../menu/OverrideContext';
|
||||
import OverrideContext from '../menu/OverrideContext';
|
||||
import { OverrideProvider } from '../menu/OverrideContext';
|
||||
import getPlacements from '../_util/placements';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import { tuple } from '../_util/type';
|
||||
@ -118,6 +119,8 @@ const Dropdown: DropdownInterface = props => {
|
||||
disabled,
|
||||
getPopupContainer,
|
||||
overlayClassName,
|
||||
visible,
|
||||
onVisibleChange,
|
||||
} = props;
|
||||
|
||||
const prefixCls = getPrefixCls('dropdown', customizePrefixCls);
|
||||
@ -136,42 +139,35 @@ const Dropdown: DropdownInterface = props => {
|
||||
disabled,
|
||||
});
|
||||
|
||||
const overlayClassNameCustomized = classNames(overlayClassName, hashId, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
});
|
||||
|
||||
const triggerActions = disabled ? [] : trigger;
|
||||
let alignPoint;
|
||||
if (triggerActions && triggerActions.indexOf('contextMenu') !== -1) {
|
||||
alignPoint = true;
|
||||
}
|
||||
|
||||
// =========================== Visible ============================
|
||||
const [mergedVisible, setVisible] = useMergedState(false, {
|
||||
value: visible,
|
||||
});
|
||||
|
||||
const onInnerVisibleChange = useEvent((nextVisible: boolean) => {
|
||||
onVisibleChange?.(nextVisible);
|
||||
setVisible(nextVisible);
|
||||
});
|
||||
|
||||
// =========================== Overlay ============================
|
||||
const overlayClassNameCustomized = classNames(overlayClassName, hashId, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
});
|
||||
|
||||
const builtinPlacements = getPlacements({
|
||||
arrowPointAtCenter: typeof arrow === 'object' && arrow.pointAtCenter,
|
||||
autoAdjustOverflow: true,
|
||||
});
|
||||
|
||||
const overlayContext = React.useMemo<OverrideContextProps>(
|
||||
() => ({
|
||||
prefixCls: `${prefixCls}-menu`,
|
||||
expandIcon: (
|
||||
<span className={`${prefixCls}-menu-submenu-arrow`}>
|
||||
<RightOutlined className={`${prefixCls}-menu-submenu-arrow-icon`} />
|
||||
</span>
|
||||
),
|
||||
mode: 'vertical',
|
||||
selectable: false,
|
||||
validator: ({ mode }) => {
|
||||
// Warning if use other mode
|
||||
warning(
|
||||
!mode || mode === 'vertical',
|
||||
'Dropdown',
|
||||
`mode="${mode}" is not supported for Dropdown's Menu.`,
|
||||
);
|
||||
},
|
||||
}),
|
||||
[prefixCls],
|
||||
);
|
||||
const onMenuClick = React.useCallback(() => {
|
||||
setVisible(false);
|
||||
}, []);
|
||||
|
||||
const renderOverlay = () => {
|
||||
// rc-dropdown already can process the function of overlay, but we have check logic here.
|
||||
@ -189,14 +185,36 @@ const Dropdown: DropdownInterface = props => {
|
||||
);
|
||||
|
||||
return (
|
||||
<OverrideContext.Provider value={overlayContext}>{overlayNode}</OverrideContext.Provider>
|
||||
<OverrideProvider
|
||||
prefixCls={`${prefixCls}-menu`}
|
||||
expandIcon={
|
||||
<span className={`${prefixCls}-menu-submenu-arrow`}>
|
||||
<RightOutlined className={`${prefixCls}-menu-submenu-arrow-icon`} />
|
||||
</span>
|
||||
}
|
||||
mode="vertical"
|
||||
selectable={false}
|
||||
onClick={onMenuClick}
|
||||
validator={({ mode }) => {
|
||||
// Warning if use other mode
|
||||
warning(
|
||||
!mode || mode === 'vertical',
|
||||
'Dropdown',
|
||||
`mode="${mode}" is not supported for Dropdown's Menu.`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
{overlayNode}
|
||||
</OverrideProvider>
|
||||
);
|
||||
};
|
||||
|
||||
// ============================ Render ============================
|
||||
return wrapSSR(
|
||||
<RcDropdown
|
||||
alignPoint={alignPoint}
|
||||
{...props}
|
||||
visible={mergedVisible}
|
||||
builtinPlacements={builtinPlacements}
|
||||
arrow={!!arrow}
|
||||
overlayClassName={overlayClassNameCustomized}
|
||||
@ -206,6 +224,7 @@ const Dropdown: DropdownInterface = props => {
|
||||
trigger={triggerActions}
|
||||
overlay={renderOverlay}
|
||||
placement={getPlacement()}
|
||||
onVisibleChange={onInnerVisibleChange}
|
||||
>
|
||||
{dropdownTrigger}
|
||||
</RcDropdown>,
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Rule, RuleObject, RuleRender } from 'rc-field-form/lib/interface';
|
||||
import InternalForm, { useForm, FormInstance, FormProps, useWatch } from './Form';
|
||||
import Item, { FormItemProps } from './FormItem';
|
||||
import ErrorList, { ErrorListProps } from './ErrorList';
|
||||
import List, { FormListProps } from './FormList';
|
||||
import { FormProvider } from './context';
|
||||
import warning from '../_util/warning';
|
||||
import { FormProvider } from './context';
|
||||
import ErrorList, { ErrorListProps } from './ErrorList';
|
||||
import InternalForm, { FormInstance, FormProps, useForm, useWatch } from './Form';
|
||||
import Item, { FormItemProps } from './FormItem';
|
||||
import List, { FormListFieldData, FormListOperation, FormListProps } from './FormList';
|
||||
import useFormInstance from './hooks/useFormInstance';
|
||||
|
||||
type InternalFormType = typeof InternalForm;
|
||||
@ -48,6 +48,8 @@ export {
|
||||
RuleObject,
|
||||
RuleRender,
|
||||
FormListProps,
|
||||
FormListFieldData,
|
||||
FormListOperation,
|
||||
};
|
||||
|
||||
export default Form;
|
||||
|
@ -137,6 +137,8 @@ const Col = React.forwardRef<HTMLDivElement, ColProps>((props, ref) => {
|
||||
);
|
||||
});
|
||||
|
||||
Col.displayName = 'Col';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Col.displayName = 'Col';
|
||||
}
|
||||
|
||||
export default Col;
|
||||
|
@ -134,6 +134,8 @@ const Row = React.forwardRef<HTMLDivElement, RowProps>((props, ref) => {
|
||||
);
|
||||
});
|
||||
|
||||
Row.displayName = 'Row';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Row.displayName = 'Row';
|
||||
}
|
||||
|
||||
export default Row;
|
||||
|
@ -16,213 +16,154 @@ if (
|
||||
}
|
||||
/* @remove-on-es-build-end */
|
||||
|
||||
export type { AffixProps } from './affix';
|
||||
export { default as Affix } from './affix';
|
||||
|
||||
export type { AnchorProps, AnchorLinkProps } from './anchor';
|
||||
export { default as Anchor } from './anchor';
|
||||
|
||||
export type { AutoCompleteProps } from './auto-complete';
|
||||
export { default as AutoComplete } from './auto-complete';
|
||||
|
||||
export type { AlertProps } from './alert';
|
||||
export type { AffixProps } from './affix';
|
||||
export { default as Alert } from './alert';
|
||||
|
||||
export type { AvatarProps } from './avatar';
|
||||
export type { AlertProps } from './alert';
|
||||
export { default as Anchor } from './anchor';
|
||||
export type { AnchorLinkProps, AnchorProps } from './anchor';
|
||||
export { default as AutoComplete } from './auto-complete';
|
||||
export type { AutoCompleteProps } from './auto-complete';
|
||||
export { default as Avatar } from './avatar';
|
||||
|
||||
export type { BackTopProps } from './back-top';
|
||||
export type { AvatarProps } from './avatar';
|
||||
export { default as BackTop } from './back-top';
|
||||
|
||||
export type { BadgeProps } from './badge';
|
||||
export type { BackTopProps } from './back-top';
|
||||
export { default as Badge } from './badge';
|
||||
|
||||
export type { BreadcrumbProps, BreadcrumbItemProps } from './breadcrumb';
|
||||
export type { BadgeProps } from './badge';
|
||||
export { default as Breadcrumb } from './breadcrumb';
|
||||
|
||||
export type { ButtonProps } from './button';
|
||||
export type { BreadcrumbItemProps, BreadcrumbProps } from './breadcrumb';
|
||||
export { default as Button } from './button';
|
||||
|
||||
export type { CalendarProps } from './calendar';
|
||||
export type { ButtonProps } from './button';
|
||||
export { default as Calendar } from './calendar';
|
||||
|
||||
export type { CardProps } from './card';
|
||||
export type { CalendarProps } from './calendar';
|
||||
export { default as Card } from './card';
|
||||
|
||||
export type { CollapseProps, CollapsePanelProps } from './collapse';
|
||||
export { default as Collapse } from './collapse';
|
||||
|
||||
export type { CarouselProps } from './carousel';
|
||||
export type { CardProps } from './card';
|
||||
export { default as Carousel } from './carousel';
|
||||
|
||||
export type { CascaderProps } from './cascader';
|
||||
export type { CarouselProps } from './carousel';
|
||||
export { default as Cascader } from './cascader';
|
||||
|
||||
export type { CheckboxProps, CheckboxOptionType } from './checkbox';
|
||||
export type { CascaderProps } from './cascader';
|
||||
export { default as Checkbox } from './checkbox';
|
||||
|
||||
export type { ColProps } from './col';
|
||||
export type { CheckboxOptionType, CheckboxProps } from './checkbox';
|
||||
export { default as Col } from './col';
|
||||
|
||||
export type { CommentProps } from './comment';
|
||||
export type { ColProps } from './col';
|
||||
export { default as Collapse } from './collapse';
|
||||
export type { CollapsePanelProps, CollapseProps } from './collapse';
|
||||
export { default as Comment } from './comment';
|
||||
|
||||
export type { CommentProps } from './comment';
|
||||
export { default as ConfigProvider } from './config-provider';
|
||||
|
||||
export type { DatePickerProps } from './date-picker';
|
||||
export { default as DatePicker } from './date-picker';
|
||||
|
||||
export type { DescriptionsProps } from './descriptions';
|
||||
export type { DatePickerProps } from './date-picker';
|
||||
export { default as Descriptions } from './descriptions';
|
||||
|
||||
export type { DividerProps } from './divider';
|
||||
export type { DescriptionsProps } from './descriptions';
|
||||
export { default as Divider } from './divider';
|
||||
|
||||
export type { DividerProps } from './divider';
|
||||
export { default as Drawer } from './drawer';
|
||||
export type { DrawerProps } from './drawer';
|
||||
export { default as Dropdown } from './dropdown';
|
||||
export type {
|
||||
DropdownProps,
|
||||
// typo, but we need to support it for backwards compatibility
|
||||
// https://github.com/ant-design/ant-design/pull/35161
|
||||
DropdownProps as DropDownProps,
|
||||
} from './dropdown';
|
||||
export { default as Dropdown } from './dropdown';
|
||||
|
||||
export type { DrawerProps } from './drawer';
|
||||
export { default as Drawer } from './drawer';
|
||||
|
||||
export type { EmptyProps } from './empty';
|
||||
export { default as Empty } from './empty';
|
||||
|
||||
export type { FormInstance, FormProps, FormItemProps } from './form';
|
||||
export type { EmptyProps } from './empty';
|
||||
export { default as Form } from './form';
|
||||
|
||||
export { default as Grid } from './grid';
|
||||
|
||||
export type { InputProps, InputRef } from './input';
|
||||
export { default as Input } from './input';
|
||||
|
||||
export type { ImageProps } from './image';
|
||||
export { default as Image } from './image';
|
||||
|
||||
export type { InputNumberProps } from './input-number';
|
||||
export { default as InputNumber } from './input-number';
|
||||
|
||||
export type { LayoutProps, SiderProps } from './layout';
|
||||
export { default as Layout } from './layout';
|
||||
|
||||
export type { ListProps } from './list';
|
||||
export { default as List } from './list';
|
||||
|
||||
export type { ArgsProps as MessageArgsProps } from './message';
|
||||
export { default as message } from './message';
|
||||
|
||||
export type { MenuProps, MenuTheme, SubMenuProps, MenuItemProps } from './menu';
|
||||
export { default as Menu } from './menu';
|
||||
|
||||
export type { MentionProps } from './mentions';
|
||||
export { default as Mentions } from './mentions';
|
||||
|
||||
export type { ModalProps, ModalFuncProps } from './modal';
|
||||
export { default as Modal } from './modal';
|
||||
|
||||
export type { StatisticProps } from './statistic';
|
||||
export { default as Statistic } from './statistic';
|
||||
|
||||
export { default as notification } from './notification';
|
||||
|
||||
export type { PageHeaderProps } from './page-header';
|
||||
export { default as PageHeader } from './page-header';
|
||||
|
||||
export type { PaginationProps } from './pagination';
|
||||
export { default as Pagination } from './pagination';
|
||||
|
||||
export type { PopconfirmProps } from './popconfirm';
|
||||
export { default as Popconfirm } from './popconfirm';
|
||||
|
||||
export type { PopoverProps } from './popover';
|
||||
export { default as Popover } from './popover';
|
||||
|
||||
export type { ProgressProps } from './progress';
|
||||
export { default as Progress } from './progress';
|
||||
|
||||
export type { RadioProps, RadioChangeEvent, RadioGroupProps } from './radio';
|
||||
export { default as Radio } from './radio';
|
||||
|
||||
export type { RateProps } from './rate';
|
||||
export { default as Rate } from './rate';
|
||||
|
||||
export type { ResultProps } from './result';
|
||||
export { default as Result } from './result';
|
||||
|
||||
export type { RowProps } from './row';
|
||||
export { default as Row } from './row';
|
||||
|
||||
export type { SelectProps } from './select';
|
||||
export { default as Select } from './select';
|
||||
|
||||
export type { SegmentedProps } from './segmented';
|
||||
export { default as Segmented } from './segmented';
|
||||
|
||||
export type { SkeletonProps } from './skeleton';
|
||||
export { default as Skeleton } from './skeleton';
|
||||
|
||||
export type { SliderSingleProps } from './slider';
|
||||
export { default as Slider } from './slider';
|
||||
|
||||
export type { SpaceProps } from './space';
|
||||
export { default as Space } from './space';
|
||||
|
||||
export type { SpinProps } from './spin';
|
||||
export { default as Spin } from './spin';
|
||||
|
||||
export type { StepProps, StepsProps } from './steps';
|
||||
export { default as Steps } from './steps';
|
||||
|
||||
export type { SwitchProps } from './switch';
|
||||
export { default as Switch } from './switch';
|
||||
|
||||
export type {
|
||||
TableProps,
|
||||
TablePaginationConfig,
|
||||
FormInstance,
|
||||
FormItemProps,
|
||||
FormListFieldData,
|
||||
FormListOperation,
|
||||
FormProps,
|
||||
Rule as FormRule,
|
||||
} from './form';
|
||||
export { default as Grid } from './grid';
|
||||
export { default as Image } from './image';
|
||||
export type { ImageProps } from './image';
|
||||
export { default as Input } from './input';
|
||||
export type { InputProps, InputRef } from './input';
|
||||
export { default as InputNumber } from './input-number';
|
||||
export type { InputNumberProps } from './input-number';
|
||||
export { default as Layout } from './layout';
|
||||
export type { LayoutProps, SiderProps } from './layout';
|
||||
export { default as List } from './list';
|
||||
export type { ListProps } from './list';
|
||||
export { default as Mentions } from './mentions';
|
||||
export type { MentionProps } from './mentions';
|
||||
export { default as Menu } from './menu';
|
||||
export type { MenuItemProps, MenuProps, MenuTheme, SubMenuProps } from './menu';
|
||||
export { default as message } from './message';
|
||||
export type { ArgsProps as MessageArgsProps } from './message';
|
||||
export { default as Modal } from './modal';
|
||||
export type { ModalFuncProps, ModalProps } from './modal';
|
||||
export { default as notification } from './notification';
|
||||
export { default as PageHeader } from './page-header';
|
||||
export type { PageHeaderProps } from './page-header';
|
||||
export { default as Pagination } from './pagination';
|
||||
export type { PaginationProps } from './pagination';
|
||||
export { default as Popconfirm } from './popconfirm';
|
||||
export type { PopconfirmProps } from './popconfirm';
|
||||
export { default as Popover } from './popover';
|
||||
export type { PopoverProps } from './popover';
|
||||
export { default as Progress } from './progress';
|
||||
export type { ProgressProps } from './progress';
|
||||
export { default as Radio } from './radio';
|
||||
export type { RadioChangeEvent, RadioGroupProps, RadioProps } from './radio';
|
||||
export { default as Rate } from './rate';
|
||||
export type { RateProps } from './rate';
|
||||
export { default as Result } from './result';
|
||||
export type { ResultProps } from './result';
|
||||
export { default as Row } from './row';
|
||||
export type { RowProps } from './row';
|
||||
export { default as Segmented } from './segmented';
|
||||
export type { SegmentedProps } from './segmented';
|
||||
export { default as Select } from './select';
|
||||
export type { RefSelectProps, SelectProps } from './select';
|
||||
export { default as Skeleton } from './skeleton';
|
||||
export type { SkeletonProps } from './skeleton';
|
||||
export { default as Slider } from './slider';
|
||||
export type { SliderSingleProps } from './slider';
|
||||
export { default as Space } from './space';
|
||||
export type { SpaceProps } from './space';
|
||||
export { default as Spin } from './spin';
|
||||
export type { SpinProps } from './spin';
|
||||
export { default as Statistic } from './statistic';
|
||||
export type { StatisticProps } from './statistic';
|
||||
export { default as Steps } from './steps';
|
||||
export type { StepProps, StepsProps } from './steps';
|
||||
export { default as Switch } from './switch';
|
||||
export type { SwitchProps } from './switch';
|
||||
export { default as Table } from './table';
|
||||
export type {
|
||||
ColumnGroupType as TableColumnGroupType,
|
||||
ColumnType as TableColumnType,
|
||||
ColumnProps as TableColumnProps,
|
||||
ColumnsType as TableColumnsType,
|
||||
ColumnType as TableColumnType,
|
||||
TablePaginationConfig,
|
||||
TableProps,
|
||||
} from './table';
|
||||
export { default as Table } from './table';
|
||||
|
||||
export type { TransferProps } from './transfer';
|
||||
export { default as Tabs } from './tabs';
|
||||
export type { TabPaneProps, TabsProps } from './tabs';
|
||||
export { default as Tag } from './tag';
|
||||
export type { TagProps, TagType } from './tag';
|
||||
export { default as TimePicker } from './time-picker';
|
||||
export type { TimePickerProps, TimeRangePickerProps } from './time-picker';
|
||||
export { default as Timeline } from './timeline';
|
||||
export type { TimelineItemProps, TimelineProps } from './timeline';
|
||||
export { default as Tooltip } from './tooltip';
|
||||
export type { TooltipProps } from './tooltip';
|
||||
export { default as Transfer } from './transfer';
|
||||
|
||||
export type { TransferProps } from './transfer';
|
||||
export { default as Tree } from './tree';
|
||||
export type {
|
||||
TreeProps,
|
||||
AntTreeNodeProps as TreeNodeProps,
|
||||
DataNode as TreeDataNode,
|
||||
TreeProps,
|
||||
} from './tree';
|
||||
export { default as Tree } from './tree';
|
||||
|
||||
export type { TreeSelectProps } from './tree-select';
|
||||
export { default as TreeSelect } from './tree-select';
|
||||
|
||||
export type { TabsProps, TabPaneProps } from './tabs';
|
||||
export { default as Tabs } from './tabs';
|
||||
|
||||
export type { TagProps, TagType } from './tag';
|
||||
export { default as Tag } from './tag';
|
||||
|
||||
export type { TimePickerProps, TimeRangePickerProps } from './time-picker';
|
||||
export { default as TimePicker } from './time-picker';
|
||||
|
||||
export type { TimelineProps, TimelineItemProps } from './timeline';
|
||||
export { default as Timeline } from './timeline';
|
||||
|
||||
export type { TooltipProps } from './tooltip';
|
||||
export { default as Tooltip } from './tooltip';
|
||||
|
||||
export type { TypographyProps } from './typography';
|
||||
export type { TreeSelectProps } from './tree-select';
|
||||
export { default as Typography } from './typography';
|
||||
|
||||
export type { UploadProps } from './upload';
|
||||
|
||||
export type { TypographyProps } from './typography';
|
||||
export { default as Upload } from './upload';
|
||||
|
||||
export type { UploadFile, UploadProps } from './upload';
|
||||
export { default as version } from './version';
|
||||
|
@ -11,9 +11,9 @@ import type { SizeType } from '../config-provider/SizeContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import { FormItemInputContext, NoFormStyle } from '../form/context';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import useStyle from './style';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import useStyle from './style';
|
||||
|
||||
type ValueType = string | number;
|
||||
|
||||
@ -95,7 +95,6 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
[`${prefixCls}-lg`]: mergeSize === 'large',
|
||||
[`${prefixCls}-sm`]: mergeSize === 'small',
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-readonly`]: readOnly,
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
[`${prefixCls}-in-form-item`]: isFormItemInput,
|
||||
},
|
||||
|
@ -1,14 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import EyeInvisibleOutlined from '@ant-design/icons/EyeInvisibleOutlined';
|
||||
import EyeOutlined from '@ant-design/icons/EyeOutlined';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import EyeOutlined from '@ant-design/icons/EyeOutlined';
|
||||
import EyeInvisibleOutlined from '@ant-design/icons/EyeInvisibleOutlined';
|
||||
import * as React from 'react';
|
||||
|
||||
import { useState } from 'react';
|
||||
import type { InputRef, InputProps } from './Input';
|
||||
import Input from './Input';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigConsumer } from '../config-provider';
|
||||
import type { InputProps, InputRef } from './Input';
|
||||
import Input from './Input';
|
||||
|
||||
export interface PasswordProps extends InputProps {
|
||||
readonly inputPrefixCls?: string;
|
||||
@ -98,6 +98,8 @@ Password.defaultProps = {
|
||||
iconRender: (visible: boolean) => (visible ? <EyeOutlined /> : <EyeInvisibleOutlined />),
|
||||
};
|
||||
|
||||
Password.displayName = 'Password';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Password.displayName = 'Password';
|
||||
}
|
||||
|
||||
export default Password;
|
||||
|
@ -1,13 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import SearchOutlined from '@ant-design/icons/SearchOutlined';
|
||||
import classNames from 'classnames';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import SearchOutlined from '@ant-design/icons/SearchOutlined';
|
||||
import * as React from 'react';
|
||||
import Button from '../button';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import type { InputProps, InputRef } from './Input';
|
||||
import Input from './Input';
|
||||
import Button from '../button';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
|
||||
export interface SearchProps extends InputProps {
|
||||
inputPrefixCls?: string;
|
||||
@ -165,7 +165,8 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Search.displayName = 'Search';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Search.displayName = 'Search';
|
||||
}
|
||||
|
||||
export default Search;
|
||||
|
@ -14,11 +14,13 @@ title:
|
||||
Focus with additional option.
|
||||
|
||||
```tsx
|
||||
import type { InputRef } from 'antd';
|
||||
|
||||
import { Button, Input, Space, Switch } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const inputRef = useRef<any>(null);
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
const [input, setInput] = useState(true);
|
||||
|
||||
const sharedProps = {
|
||||
|
@ -1,14 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import { useContext, useRef, useState, useEffect } from 'react';
|
||||
import BarsOutlined from '@ant-design/icons/BarsOutlined';
|
||||
import LeftOutlined from '@ant-design/icons/LeftOutlined';
|
||||
import RightOutlined from '@ant-design/icons/RightOutlined';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import BarsOutlined from '@ant-design/icons/BarsOutlined';
|
||||
import RightOutlined from '@ant-design/icons/RightOutlined';
|
||||
import LeftOutlined from '@ant-design/icons/LeftOutlined';
|
||||
import * as React from 'react';
|
||||
import { useContext, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { LayoutContext } from './layout';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import isNumeric from '../_util/isNumeric';
|
||||
import { LayoutContext } from './layout';
|
||||
|
||||
const dimensionMaxMap = {
|
||||
xs: '479.98px',
|
||||
@ -225,6 +225,8 @@ const Sider = React.forwardRef<HTMLDivElement, SiderProps>(
|
||||
},
|
||||
);
|
||||
|
||||
Sider.displayName = 'Sider';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Sider.displayName = 'Sider';
|
||||
}
|
||||
|
||||
export default Sider;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import useStyle from './style';
|
||||
|
||||
@ -39,7 +39,9 @@ function generator({ suffixCls, tagName, displayName }: GeneratorProps) {
|
||||
|
||||
return <BasicComponent ref={ref} prefixCls={prefixCls} tagName={tagName} {...props} />;
|
||||
});
|
||||
Adapter.displayName = displayName;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Adapter.displayName = displayName;
|
||||
}
|
||||
return Adapter;
|
||||
};
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import RcMentions from 'rc-mentions';
|
||||
import type { MentionsProps as RcMentionsProps } from 'rc-mentions/lib/Mentions';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Spin from '../spin';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import useStyle from './style';
|
||||
import Spin from '../spin';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
export const { Option } = RcMentions;
|
||||
|
||||
@ -173,7 +174,9 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
};
|
||||
|
||||
const Mentions = React.forwardRef<unknown, MentionProps>(InternalMentions) as CompoundedComponent;
|
||||
Mentions.displayName = 'Mentions';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Mentions.displayName = 'Mentions';
|
||||
}
|
||||
Mentions.Option = Option;
|
||||
|
||||
Mentions.getMentions = (value: string = '', config: MentionsConfig = {}): MentionsEntity[] => {
|
||||
|
@ -1,16 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import type { MenuProps } from '.';
|
||||
|
||||
// Used for Dropdown only
|
||||
export interface OverrideContextProps {
|
||||
prefixCls?: string;
|
||||
expandIcon?: React.ReactNode;
|
||||
mode?: MenuProps['mode'];
|
||||
selectable?: boolean;
|
||||
validator?: (menuProps: Pick<MenuProps, 'mode'>) => void;
|
||||
}
|
||||
|
||||
/** @private Internal Usage. Only used for Dropdown component. Do not use this in your production. */
|
||||
const OverrideContext = React.createContext<OverrideContextProps | null>(null);
|
||||
|
||||
export default OverrideContext;
|
42
components/menu/OverrideContext.tsx
Normal file
42
components/menu/OverrideContext.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import * as React from 'react';
|
||||
import type { MenuProps } from '.';
|
||||
|
||||
// Used for Dropdown only
|
||||
export interface OverrideContextProps {
|
||||
prefixCls?: string;
|
||||
expandIcon?: React.ReactNode;
|
||||
mode?: MenuProps['mode'];
|
||||
selectable?: boolean;
|
||||
validator?: (menuProps: Pick<MenuProps, 'mode'>) => void;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/** @private Internal Usage. Only used for Dropdown component. Do not use this in your production. */
|
||||
const OverrideContext = React.createContext<OverrideContextProps | null>(null);
|
||||
|
||||
/** @private Internal Usage. Only used for Dropdown component. Do not use this in your production. */
|
||||
export const OverrideProvider = ({
|
||||
children,
|
||||
...restProps
|
||||
}: OverrideContextProps & { children: React.ReactNode }) => {
|
||||
const override = React.useContext(OverrideContext);
|
||||
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
...override,
|
||||
...restProps,
|
||||
}),
|
||||
[
|
||||
override,
|
||||
restProps.prefixCls,
|
||||
// restProps.expandIcon, Not mark as deps since this is a ReactNode
|
||||
restProps.mode,
|
||||
restProps.selectable,
|
||||
// restProps.validator, Not mark as deps since this is a function
|
||||
],
|
||||
);
|
||||
|
||||
return <OverrideContext.Provider value={context}>{children}</OverrideContext.Provider>;
|
||||
};
|
||||
|
||||
export default OverrideContext;
|
@ -1,28 +1,30 @@
|
||||
import * as React from 'react';
|
||||
import EllipsisOutlined from '@ant-design/icons/EllipsisOutlined';
|
||||
import classNames from 'classnames';
|
||||
import type { MenuProps as RcMenuProps, MenuRef } from 'rc-menu';
|
||||
import RcMenu, { ItemGroup } from 'rc-menu';
|
||||
import classNames from 'classnames';
|
||||
import useEvent from 'rc-util/lib/hooks/useEvent';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import EllipsisOutlined from '@ant-design/icons/EllipsisOutlined';
|
||||
import * as React from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import SubMenu, { SubMenuProps } from './SubMenu';
|
||||
import Item, { MenuItemProps } from './MenuItem';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import warning from '../_util/warning';
|
||||
import type { SiderContextProps } from '../layout/Sider';
|
||||
import { SiderContext } from '../layout/Sider';
|
||||
import collapseMotion from '../_util/motion';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import MenuContext, { MenuTheme } from './MenuContext';
|
||||
import MenuDivider from './MenuDivider';
|
||||
import warning from '../_util/warning';
|
||||
import type { ItemType } from './hooks/useItems';
|
||||
import useItems from './hooks/useItems';
|
||||
import MenuContext, { MenuTheme } from './MenuContext';
|
||||
import MenuDivider from './MenuDivider';
|
||||
import Item, { MenuItemProps } from './MenuItem';
|
||||
import OverrideContext from './OverrideContext';
|
||||
import SubMenu, { SubMenuProps } from './SubMenu';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
export { MenuDividerProps } from './MenuDivider';
|
||||
|
||||
export { MenuItemGroupProps } from 'rc-menu';
|
||||
export { MenuDividerProps } from './MenuDivider';
|
||||
export { MenuTheme, SubMenuProps, MenuItemProps };
|
||||
|
||||
export type MenuMode = 'vertical' | 'vertical-left' | 'vertical-right' | 'horizontal' | 'inline';
|
||||
|
||||
@ -46,8 +48,9 @@ type InternalMenuProps = MenuProps &
|
||||
};
|
||||
|
||||
const InternalMenu = forwardRef<MenuRef, InternalMenuProps>((props, ref) => {
|
||||
const override = React.useContext(OverrideContext);
|
||||
const override = React.useContext(OverrideContext) || {};
|
||||
const overrideObj = override || {};
|
||||
|
||||
const { getPrefixCls, getPopupContainer, direction } = React.useContext(ConfigContext);
|
||||
|
||||
const rootPrefixCls = getPrefixCls();
|
||||
@ -65,6 +68,7 @@ const InternalMenu = forwardRef<MenuRef, InternalMenuProps>((props, ref) => {
|
||||
rootClassName,
|
||||
mode,
|
||||
selectable,
|
||||
onClick,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
@ -94,6 +98,13 @@ const InternalMenu = forwardRef<MenuRef, InternalMenuProps>((props, ref) => {
|
||||
|
||||
overrideObj.validator?.({ mode });
|
||||
|
||||
// ========================== Click ==========================
|
||||
// Tell dropdown that item clicked
|
||||
const onItemClick = useEvent<Required<MenuProps>['onClick']>((...args) => {
|
||||
onClick?.(...args);
|
||||
override?.onClick?.();
|
||||
});
|
||||
|
||||
// ========================== Mode ===========================
|
||||
const mergedMode = overrideObj.mode || mode;
|
||||
|
||||
@ -152,6 +163,7 @@ const InternalMenu = forwardRef<MenuRef, InternalMenuProps>((props, ref) => {
|
||||
overflowedIndicatorPopupClassName={`${prefixCls}-${theme}`}
|
||||
mode={mergedMode}
|
||||
selectable={mergedSelectable}
|
||||
onClick={onItemClick}
|
||||
{...passedProps}
|
||||
inlineCollapsed={mergedInlineCollapsed}
|
||||
className={menuClassName}
|
||||
@ -202,6 +214,4 @@ class Menu extends React.Component<MenuProps, {}> {
|
||||
}
|
||||
}
|
||||
|
||||
export { MenuTheme, SubMenuProps, MenuItemProps };
|
||||
|
||||
export default Menu;
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { spyElementPrototype } from 'rc-util/lib/test/domHook';
|
||||
import React from 'react';
|
||||
import Popconfirm from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import { sleep, render, fireEvent } from '../../../tests/utils';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
|
||||
describe('Popconfirm', () => {
|
||||
@ -263,7 +263,7 @@ describe('Popconfirm', () => {
|
||||
fireEvent.click(container.querySelector('.ant-btn-primary'));
|
||||
|
||||
await sleep(500);
|
||||
expect(container.textContent).toEqual('Unmounted');
|
||||
// expect(container.textContent).toEqual('Unmounted');
|
||||
expect(error).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -53,7 +53,9 @@ const Popover = React.forwardRef<unknown, PopoverProps>(
|
||||
},
|
||||
);
|
||||
|
||||
Popover.displayName = 'Popover';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Popover.displayName = 'Popover';
|
||||
}
|
||||
|
||||
Popover.defaultProps = {
|
||||
placement: 'top' as TooltipPlacement,
|
||||
|
@ -1,14 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import RcCheckbox from 'rc-checkbox';
|
||||
import classNames from 'classnames';
|
||||
import RcCheckbox from 'rc-checkbox';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import type { RadioProps, RadioChangeEvent } from './interface';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import RadioGroupContext, { RadioOptionTypeContext } from './context';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import warning from '../_util/warning';
|
||||
import RadioGroupContext, { RadioOptionTypeContext } from './context';
|
||||
import type { RadioChangeEvent, RadioProps } from './interface';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
const InternalRadio: React.ForwardRefRenderFunction<HTMLElement, RadioProps> = (props, ref) => {
|
||||
@ -84,6 +85,8 @@ const InternalRadio: React.ForwardRefRenderFunction<HTMLElement, RadioProps> = (
|
||||
|
||||
const Radio = React.forwardRef<unknown, RadioProps>(InternalRadio);
|
||||
|
||||
Radio.displayName = 'Radio';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Radio.displayName = 'Radio';
|
||||
}
|
||||
|
||||
export default Radio;
|
||||
|
@ -39,7 +39,9 @@ const Rate = React.forwardRef<unknown, RateProps>(({ prefixCls, tooltips, ...pro
|
||||
);
|
||||
});
|
||||
|
||||
Rate.displayName = 'Rate';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Rate.displayName = 'Rate';
|
||||
}
|
||||
|
||||
Rate.defaultProps = {
|
||||
character: <StarFilled />,
|
||||
|
@ -1,11 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import RcSegmented from 'rc-segmented';
|
||||
import type {
|
||||
SegmentedLabeledOption as RcSegmentedLabeledOption,
|
||||
SegmentedProps as RCSegmentedProps,
|
||||
SegmentedRawOption,
|
||||
SegmentedLabeledOption as RcSegmentedLabeledOption,
|
||||
} from 'rc-segmented';
|
||||
import RcSegmented from 'rc-segmented';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
@ -103,7 +103,10 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>((props, ref)
|
||||
);
|
||||
});
|
||||
|
||||
Segmented.displayName = 'Segmented';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Segmented.displayName = 'Segmented';
|
||||
}
|
||||
|
||||
Segmented.defaultProps = {
|
||||
options: [],
|
||||
};
|
||||
|
@ -263,7 +263,7 @@
|
||||
// .skeleton-color() {
|
||||
// position: relative;
|
||||
// overflow: hidden;
|
||||
// background: #fff;
|
||||
// background: transparent;
|
||||
|
||||
// &::after {
|
||||
// position: absolute;
|
||||
@ -276,9 +276,9 @@
|
||||
// @skeleton-color 25%,
|
||||
// @skeleton-to-color 37%,
|
||||
// @skeleton-color 63%
|
||||
// );
|
||||
// );
|
||||
// animation: ~'@{skeleton-prefix-cls}-loading' 1.4s ease infinite;
|
||||
// content: "";
|
||||
// content: '';
|
||||
// }
|
||||
// }
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { SliderProps as RcSliderProps } from 'rc-slider';
|
||||
import RcSlider from 'rc-slider';
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { TooltipPlacement } from '../tooltip';
|
||||
import SliderTooltip from './SliderTooltip';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
export type SliderMarks = RcSliderProps['marks'];
|
||||
@ -169,7 +170,9 @@ const Slider = React.forwardRef<unknown, SliderSingleProps | SliderRangeProps>(
|
||||
},
|
||||
);
|
||||
|
||||
Slider.displayName = 'Slider';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Slider.displayName = 'Slider';
|
||||
}
|
||||
|
||||
Slider.defaultProps = {
|
||||
tipFormatter(value: number) {
|
||||
|
@ -66,6 +66,15 @@
|
||||
// &.@{ant-prefix}-tooltip-open {
|
||||
// border-color: @slider-handle-color-tooltip-open;
|
||||
// }
|
||||
|
||||
// &::after {
|
||||
// position: absolute;
|
||||
// top: -6px;
|
||||
// right: -6px;
|
||||
// bottom: -6px;
|
||||
// left: -6px;
|
||||
// content: "";
|
||||
// }
|
||||
// }
|
||||
|
||||
// &:hover {
|
||||
|
@ -1,13 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import RcSwitch from 'rc-switch';
|
||||
import classNames from 'classnames';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import classNames from 'classnames';
|
||||
import RcSwitch from 'rc-switch';
|
||||
import * as React from 'react';
|
||||
|
||||
import Wave from '../_util/wave';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import warning from '../_util/warning';
|
||||
import Wave from '../_util/wave';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
export type SwitchSize = 'small' | 'default';
|
||||
@ -99,6 +100,8 @@ const Switch = React.forwardRef<unknown, SwitchProps>(
|
||||
) as CompoundedComponent;
|
||||
|
||||
Switch.__ANT_SWITCH = true;
|
||||
Switch.displayName = 'Switch';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Switch.displayName = 'Switch';
|
||||
}
|
||||
|
||||
export default Switch;
|
||||
|
@ -1,13 +1,14 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { render, fireEvent, waitFor } from '../../../tests/utils';
|
||||
import Table from '..';
|
||||
import Input from '../../input';
|
||||
import Tooltip from '../../tooltip';
|
||||
import { fireEvent, render, waitFor } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
import Select from '../../select';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import Input from '../../input';
|
||||
import Menu from '../../menu';
|
||||
import Select from '../../select';
|
||||
import Tooltip from '../../tooltip';
|
||||
|
||||
// https://github.com/Semantic-Org/Semantic-UI-React/blob/72c45080e4f20b531fda2e3e430e384083d6766b/test/specs/modules/Dropdown/Dropdown-test.js#L73
|
||||
const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => {} } };
|
||||
@ -65,6 +66,14 @@ describe('Table.filter', () => {
|
||||
return namesList;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('not show filter icon when undefined', () => {
|
||||
const noFilterColumn = { ...column, filters: undefined };
|
||||
delete noFilterColumn.onFilter;
|
||||
@ -106,8 +115,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('renders empty menu correctly', () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -129,8 +136,6 @@ describe('Table.filter', () => {
|
||||
expect(container.querySelector('.ant-empty')).toBeTruthy();
|
||||
expect(errorSpy).not.toHaveBeenCalled();
|
||||
errorSpy.mockRestore();
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('renders radio filter correctly', async () => {
|
||||
@ -613,7 +618,6 @@ describe('Table.filter', () => {
|
||||
onChange,
|
||||
}),
|
||||
);
|
||||
jest.useFakeTimers();
|
||||
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
|
||||
@ -665,8 +669,6 @@ describe('Table.filter', () => {
|
||||
// What's this? Is that a coverage case? Or check a crash?
|
||||
const latestItems = getFilterMenu().querySelectorAll('li.ant-dropdown-menu-item');
|
||||
fireEvent.click(latestItems[latestItems.length - 1]);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
describe('should support value types', () => {
|
||||
@ -676,7 +678,6 @@ describe('Table.filter', () => {
|
||||
['Bamboo', false],
|
||||
].forEach(([text, value]) => {
|
||||
it(`${typeof value} type`, async () => {
|
||||
jest.useFakeTimers();
|
||||
const onChange = jest.fn();
|
||||
const filters = [{ text, value }];
|
||||
const { container } = render(
|
||||
@ -698,8 +699,6 @@ describe('Table.filter', () => {
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-dropdown-trigger'));
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
fireEvent.click(container.querySelectorAll('.ant-dropdown-menu-item')[0]);
|
||||
|
||||
// This test can be remove if refactor
|
||||
@ -733,7 +732,6 @@ describe('Table.filter', () => {
|
||||
.querySelector('.ant-table-filter-dropdown')
|
||||
.querySelectorAll('.ant-checkbox-input')[0].checked,
|
||||
).toEqual(false);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1831,7 +1829,6 @@ describe('Table.filter', () => {
|
||||
|
||||
describe('filter tree mode', () => {
|
||||
it('supports filter tree', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -1852,7 +1849,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('supports search input in filter tree', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -1875,7 +1871,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('supports search input in filter menu', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -1897,7 +1892,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('should skip search when filters[0].text is ReactNode', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -1936,7 +1930,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('should supports filterSearch has type of function', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -1974,7 +1967,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('supports check all items', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -2007,7 +1999,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('supports check item by selecting it', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -2043,7 +2034,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('select-all checkbox should change when all items are selected', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -2075,7 +2065,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('filterMultiple is false - check item', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -2123,7 +2112,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('filterMultiple is false - select item', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -2170,7 +2158,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('should select children when select parent', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
@ -2299,7 +2286,6 @@ describe('Table.filter', () => {
|
||||
});
|
||||
|
||||
it('filterDropdown should support filterResetToDefaultFilteredValue', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
|
||||
const columnFilter = {
|
||||
@ -2347,6 +2333,44 @@ describe('Table.filter', () => {
|
||||
expect(container.querySelector('.ant-tree-checkbox-checked+span').textContent).toBe('Girl');
|
||||
});
|
||||
|
||||
it('filterDropdown should not override customize Menu selectable', () => {
|
||||
const onSelect = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
createTable({
|
||||
columns: [
|
||||
{
|
||||
...column,
|
||||
filterDropdown: (
|
||||
<div className="custom-filter-dropdown">
|
||||
<Menu
|
||||
onSelect={onSelect}
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
label: 'Item 1',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
// Open Filter
|
||||
fireEvent.click(container.querySelector('span.ant-dropdown-trigger'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
// Click Item
|
||||
fireEvent.click(container.querySelector('.ant-table-filter-dropdown .ant-dropdown-menu-item'));
|
||||
|
||||
expect(onSelect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('filteredKeys should all be controlled or not controlled', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
errorSpy.mockReset();
|
||||
|
@ -40,7 +40,12 @@ const VirtualTable = (props: Parameters<typeof Table>[0]) => {
|
||||
const [connectObject] = useState<any>(() => {
|
||||
const obj = {};
|
||||
Object.defineProperty(obj, 'scrollLeft', {
|
||||
get: () => null,
|
||||
get: () => {
|
||||
if (gridRef.current) {
|
||||
return gridRef.current?.state?.scrollLeft;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
set: (scrollLeft: number) => {
|
||||
if (gridRef.current) {
|
||||
gridRef.current.scrollTo({ scrollLeft });
|
||||
|
@ -1,8 +1,8 @@
|
||||
import FilterFilled from '@ant-design/icons/FilterFilled';
|
||||
import classNames from 'classnames';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import * as React from 'react';
|
||||
import type { FieldDataNode } from 'rc-tree';
|
||||
import * as React from 'react';
|
||||
import type { FilterState } from '.';
|
||||
import { flattenKeys } from '.';
|
||||
import Button from '../../../button';
|
||||
@ -13,6 +13,7 @@ import Dropdown from '../../../dropdown';
|
||||
import Empty from '../../../empty';
|
||||
import type { MenuProps } from '../../../menu';
|
||||
import Menu from '../../../menu';
|
||||
import { OverrideProvider } from '../../../menu/OverrideContext';
|
||||
import Radio from '../../../radio';
|
||||
import type { EventDataNode } from '../../../tree';
|
||||
import Tree from '../../../tree';
|
||||
@ -183,21 +184,9 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
|
||||
// ====================== Open Keys ======================
|
||||
const [openKeys, setOpenKeys] = React.useState<string[]>([]);
|
||||
const openRef = React.useRef<number>();
|
||||
const onOpenChange = (keys: string[]) => {
|
||||
openRef.current = window.setTimeout(() => {
|
||||
setOpenKeys(keys);
|
||||
});
|
||||
setOpenKeys(keys);
|
||||
};
|
||||
const onMenuClick = () => {
|
||||
window.clearTimeout(openRef.current);
|
||||
};
|
||||
React.useEffect(
|
||||
() => () => {
|
||||
window.clearTimeout(openRef.current);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
// search in tree mode column filter
|
||||
const [searchValue, setSearchValue] = React.useState('');
|
||||
@ -303,6 +292,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
});
|
||||
|
||||
let dropdownContent: React.ReactNode;
|
||||
|
||||
if (typeof column.filterDropdown === 'function') {
|
||||
dropdownContent = column.filterDropdown({
|
||||
prefixCls: `${dropdownPrefixCls}-custom`,
|
||||
@ -395,7 +385,6 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
multiple={filterMultiple}
|
||||
prefixCls={`${dropdownPrefixCls}-menu`}
|
||||
className={dropdownMenuClass}
|
||||
onClick={onMenuClick}
|
||||
onSelect={onSelectKeys}
|
||||
onDeselect={onSelectKeys}
|
||||
selectedKeys={selectedKeys}
|
||||
@ -441,6 +430,11 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
);
|
||||
}
|
||||
|
||||
// We should not block customize Menu with additional props
|
||||
if (column.filterDropdown) {
|
||||
dropdownContent = <OverrideProvider selectable={undefined}>{dropdownContent}</OverrideProvider>;
|
||||
}
|
||||
|
||||
const menu = (
|
||||
<FilterDropdownMenuWrapper className={`${prefixCls}-dropdown`}>
|
||||
{dropdownContent}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import * as React from 'react';
|
||||
|
||||
import CheckableTag from './CheckableTag';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { PresetColorType, PresetStatusColorType } from '../_util/colors';
|
||||
import { PresetColorTypes, PresetStatusColorTypes } from '../_util/colors';
|
||||
import Wave from '../_util/wave';
|
||||
import type { LiteralUnion } from '../_util/type';
|
||||
import Wave from '../_util/wave';
|
||||
import CheckableTag from './CheckableTag';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
@ -137,7 +137,9 @@ const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (
|
||||
|
||||
const Tag = React.forwardRef<unknown, TagProps>(InternalTag) as TagType;
|
||||
|
||||
Tag.displayName = 'Tag';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Tag.displayName = 'Tag';
|
||||
}
|
||||
|
||||
Tag.CheckableTag = CheckableTag;
|
||||
|
||||
|
@ -2,8 +2,8 @@ import type { Dayjs } from 'dayjs';
|
||||
import * as React from 'react';
|
||||
import DatePicker from '../date-picker';
|
||||
import type { PickerTimeProps, RangePickerTimeProps } from '../date-picker/generatePicker';
|
||||
import warning from '../_util/warning';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import warning from '../_util/warning';
|
||||
|
||||
const { TimePicker: InternalTimePicker, RangePicker: InternalRangePicker } = DatePicker;
|
||||
|
||||
@ -61,7 +61,9 @@ const TimePicker = React.forwardRef<any, TimePickerProps>(
|
||||
},
|
||||
);
|
||||
|
||||
TimePicker.displayName = 'TimePicker';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
TimePicker.displayName = 'TimePicker';
|
||||
}
|
||||
|
||||
type MergedTimePicker = typeof TimePicker & {
|
||||
RangePicker: typeof RangePicker;
|
||||
|
@ -11,6 +11,7 @@ import { getTransitionName } from '../_util/motion';
|
||||
import getPlacements, { AdjustOverflow, PlacementsConfig } from '../_util/placements';
|
||||
import { cloneElement, isValidElement } from '../_util/reactNode';
|
||||
import type { LiteralUnion } from '../_util/type';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
export { AdjustOverflow, PlacementsConfig };
|
||||
@ -282,7 +283,9 @@ const Tooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
|
||||
);
|
||||
});
|
||||
|
||||
Tooltip.displayName = 'Tooltip';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Tooltip.displayName = 'Tooltip';
|
||||
}
|
||||
|
||||
Tooltip.defaultProps = {
|
||||
placement: 'top' as TooltipPlacement,
|
||||
|
@ -1,24 +1,32 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type RcTree from 'rc-tree';
|
||||
import { conductExpandParent } from 'rc-tree/lib/util';
|
||||
import type { EventDataNode, DataNode, Key } from 'rc-tree/lib/interface';
|
||||
import { convertDataToEntities, convertTreeToData } from 'rc-tree/lib/utils/treeUtil';
|
||||
import FileOutlined from '@ant-design/icons/FileOutlined';
|
||||
import FolderOpenOutlined from '@ant-design/icons/FolderOpenOutlined';
|
||||
import FolderOutlined from '@ant-design/icons/FolderOutlined';
|
||||
import classNames from 'classnames';
|
||||
import type RcTree from 'rc-tree';
|
||||
import type { BasicDataNode } from 'rc-tree';
|
||||
import type { DataNode, EventDataNode, Key } from 'rc-tree/lib/interface';
|
||||
import { conductExpandParent } from 'rc-tree/lib/util';
|
||||
import { convertDataToEntities, convertTreeToData } from 'rc-tree/lib/utils/treeUtil';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
|
||||
import type { TreeProps, AntdTreeNodeAttribute } from './Tree';
|
||||
import type { AntdTreeNodeAttribute, TreeProps } from './Tree';
|
||||
import Tree from './Tree';
|
||||
import { calcRangeKeys, convertDirectoryKeysToNodes } from './utils/dictUtil';
|
||||
|
||||
export type ExpandAction = false | 'click' | 'doubleClick';
|
||||
|
||||
export interface DirectoryTreeProps extends TreeProps {
|
||||
export interface DirectoryTreeProps<T extends BasicDataNode = DataNode> extends TreeProps<T> {
|
||||
expandAction?: ExpandAction;
|
||||
}
|
||||
|
||||
type DirectoryTreeCompoundedComponent = (<T extends BasicDataNode | DataNode = DataNode>(
|
||||
props: React.PropsWithChildren<DirectoryTreeProps<T>> & { ref?: React.Ref<RcTree> },
|
||||
) => React.ReactElement) & {
|
||||
defaultProps: Partial<React.PropsWithChildren<DirectoryTreeProps<any>>>;
|
||||
displayName?: string;
|
||||
};
|
||||
|
||||
export interface DirectoryTreeState {
|
||||
expandedKeys?: Key[];
|
||||
selectedKeys?: Key[];
|
||||
@ -191,8 +199,13 @@ const DirectoryTree: React.ForwardRefRenderFunction<RcTree, DirectoryTreeProps>
|
||||
);
|
||||
};
|
||||
|
||||
const ForwardDirectoryTree = React.forwardRef(DirectoryTree);
|
||||
ForwardDirectoryTree.displayName = 'DirectoryTree';
|
||||
const ForwardDirectoryTree = React.forwardRef(
|
||||
DirectoryTree,
|
||||
) as unknown as DirectoryTreeCompoundedComponent;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
ForwardDirectoryTree.displayName = 'DirectoryTree';
|
||||
}
|
||||
|
||||
ForwardDirectoryTree.defaultProps = {
|
||||
showIcon: true,
|
||||
|
@ -3,6 +3,8 @@ import * as React from 'react';
|
||||
import { render } from '../../../tests/utils';
|
||||
import Tree from '../index';
|
||||
|
||||
const { DirectoryTree } = Tree;
|
||||
|
||||
describe('Tree.TypeScript', () => {
|
||||
it('without generic', () => {
|
||||
const { container } = render(
|
||||
@ -48,4 +50,28 @@ describe('Tree.TypeScript', () => {
|
||||
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
|
||||
it('directoryTree support generic', () => {
|
||||
interface MyDataNode extends BasicDataNode {
|
||||
bamboo: string;
|
||||
list?: MyDataNode[];
|
||||
}
|
||||
|
||||
const { container } = render(
|
||||
<DirectoryTree<MyDataNode>
|
||||
treeData={[
|
||||
{
|
||||
bamboo: 'good',
|
||||
list: [
|
||||
{
|
||||
bamboo: 'well',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import warning from '../_util/warning';
|
||||
import useStyle from './style';
|
||||
@ -62,8 +62,9 @@ const Typography: React.ForwardRefRenderFunction<{}, InternalTypographyProps> =
|
||||
};
|
||||
|
||||
const RefTypography = React.forwardRef(Typography);
|
||||
|
||||
RefTypography.displayName = 'Typography';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
RefTypography.displayName = 'Typography';
|
||||
}
|
||||
|
||||
// es default export should use const instead of let
|
||||
const ExportTypography = RefTypography as unknown as React.FC<TypographyProps>;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { LikeOutlined, SmileOutlined } from '@ant-design/icons';
|
||||
import { act } from '@testing-library/react';
|
||||
import * as copyObj from 'copy-to-clipboard';
|
||||
import React from 'react';
|
||||
import { fireEvent, render, waitFor } from '../../../tests/utils';
|
||||
@ -233,7 +234,8 @@ describe('Typography copy', () => {
|
||||
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
|
||||
});
|
||||
|
||||
it('copy to clipboard', done => {
|
||||
it('copy to clipboard', () => {
|
||||
jest.useFakeTimers();
|
||||
const spy = jest.spyOn(copyObj, 'default');
|
||||
const originText = 'origin text.';
|
||||
const nextText = 'next text.';
|
||||
@ -243,7 +245,7 @@ describe('Typography copy', () => {
|
||||
setTimeout(() => {
|
||||
setDynamicText(nextText);
|
||||
}, 500);
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
<Base component="p" copyable>
|
||||
{dynamicText}
|
||||
@ -254,12 +256,13 @@ describe('Typography copy', () => {
|
||||
const copyBtn = wrapper.querySelectorAll('.ant-typography-copy')[0];
|
||||
fireEvent.click(copyBtn);
|
||||
expect(spy.mock.calls[0][0]).toEqual(originText);
|
||||
setTimeout(() => {
|
||||
spy.mockReset();
|
||||
fireEvent.click(copyBtn);
|
||||
expect(spy.mock.calls[0][0]).toEqual(nextText);
|
||||
done();
|
||||
}, 500);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
spy.mockReset();
|
||||
fireEvent.click(copyBtn);
|
||||
expect(spy.mock.calls[0][0]).toEqual(nextText);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import Upload from './Upload';
|
||||
import type { UploadProps } from './interface';
|
||||
import Upload from './Upload';
|
||||
|
||||
export type DraggerProps = UploadProps & { height?: number };
|
||||
|
||||
@ -11,6 +11,8 @@ const InternalDragger: React.ForwardRefRenderFunction<unknown, DraggerProps> = (
|
||||
|
||||
const Dragger = React.forwardRef(InternalDragger) as React.FC<DraggerProps>;
|
||||
|
||||
Dragger.displayName = 'Dragger';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Dragger.displayName = 'Dragger';
|
||||
}
|
||||
|
||||
export default Dragger;
|
||||
|
@ -1,24 +1,25 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { UploadProps as RcUploadProps } from 'rc-upload';
|
||||
import RcUpload from 'rc-upload';
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
import classNames from 'classnames';
|
||||
import UploadList from './UploadList';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import defaultLocale from '../locale/default';
|
||||
import warning from '../_util/warning';
|
||||
import type {
|
||||
RcFile,
|
||||
ShowUploadListInterface,
|
||||
UploadFile,
|
||||
UploadLocale,
|
||||
UploadChangeParam,
|
||||
UploadType,
|
||||
UploadFile,
|
||||
UploadListType,
|
||||
UploadLocale,
|
||||
UploadType,
|
||||
} from './interface';
|
||||
import { UploadProps } from './interface';
|
||||
import UploadList from './UploadList';
|
||||
import { file2Obj, getFileItem, removeFileItem, updateFileList } from './utils';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import defaultLocale from '../locale/default';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import warning from '../_util/warning';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
export const LIST_IGNORE = `__LIST_IGNORE_${Date.now()}__`;
|
||||
@ -435,8 +436,9 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
|
||||
};
|
||||
|
||||
const Upload = React.forwardRef<unknown, UploadProps>(InternalUpload);
|
||||
|
||||
Upload.displayName = 'Upload';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Upload.displayName = 'Upload';
|
||||
}
|
||||
|
||||
Upload.defaultProps = {
|
||||
type: 'select' as UploadType,
|
||||
|
@ -1,20 +1,20 @@
|
||||
import * as React from 'react';
|
||||
import type { CSSMotionListProps } from 'rc-motion';
|
||||
import CSSMotion, { CSSMotionList } from 'rc-motion';
|
||||
import classNames from 'classnames';
|
||||
import FileTwoTone from '@ant-design/icons/FileTwoTone';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import PaperClipOutlined from '@ant-design/icons/PaperClipOutlined';
|
||||
import PictureTwoTone from '@ant-design/icons/PictureTwoTone';
|
||||
import FileTwoTone from '@ant-design/icons/FileTwoTone';
|
||||
import { cloneElement, isValidElement } from '../../_util/reactNode';
|
||||
import { previewImage, isImageUrl } from '../utils';
|
||||
import collapseMotion from '../../_util/motion';
|
||||
import { ConfigContext } from '../../config-provider';
|
||||
import classNames from 'classnames';
|
||||
import type { CSSMotionListProps } from 'rc-motion';
|
||||
import CSSMotion, { CSSMotionList } from 'rc-motion';
|
||||
import * as React from 'react';
|
||||
import type { ButtonProps } from '../../button';
|
||||
import Button from '../../button';
|
||||
import { ConfigContext } from '../../config-provider';
|
||||
import useForceUpdate from '../../_util/hooks/useForceUpdate';
|
||||
import collapseMotion from '../../_util/motion';
|
||||
import { cloneElement, isValidElement } from '../../_util/reactNode';
|
||||
import type { InternalUploadFile, UploadFile, UploadListProps, UploadListType } from '../interface';
|
||||
import { isImageUrl, previewImage } from '../utils';
|
||||
import ListItem from './ListItem';
|
||||
import type { UploadListProps, UploadFile, UploadListType, InternalUploadFile } from '../interface';
|
||||
|
||||
const listItemMotion: Partial<CSSMotionListProps> = {
|
||||
...collapseMotion,
|
||||
@ -246,8 +246,9 @@ const InternalUploadList: React.ForwardRefRenderFunction<unknown, UploadListProp
|
||||
};
|
||||
|
||||
const UploadList = React.forwardRef<unknown, UploadListProps>(InternalUploadList);
|
||||
|
||||
UploadList.displayName = 'UploadList';
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
UploadList.displayName = 'UploadList';
|
||||
}
|
||||
|
||||
UploadList.defaultProps = {
|
||||
listType: 'text' as UploadListType, // or picture
|
||||
|
@ -28,8 +28,8 @@ exports[`Upload List handle error 1`] = `
|
||||
class="ant-upload-list ant-upload-list-text"
|
||||
>
|
||||
<div
|
||||
class="ant-upload-list-item-container ant-upload-animate-appear ant-upload-animate-appear-active ant-upload-animate"
|
||||
style="height: 0px; opacity: 1;"
|
||||
class="ant-upload-list-item-container"
|
||||
style=""
|
||||
>
|
||||
<div
|
||||
class="ant-upload-list-item ant-upload-list-item-error"
|
||||
|
@ -1,10 +1,10 @@
|
||||
/* 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 { setup, teardown } from './mock';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import { fireEvent, render, waitFor } from '../../../tests/utils';
|
||||
import { setup, teardown } from './mock';
|
||||
|
||||
describe('Upload.Dragger', () => {
|
||||
mountTest(Upload.Dragger);
|
||||
@ -12,44 +12,47 @@ describe('Upload.Dragger', () => {
|
||||
beforeEach(() => setup());
|
||||
afterEach(() => teardown());
|
||||
|
||||
it('support drag file with over style', () => {
|
||||
it('support drag file with over style', async () => {
|
||||
jest.useFakeTimers();
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload.Dragger action="http://upload.com">
|
||||
<div />
|
||||
</Upload.Dragger>,
|
||||
);
|
||||
|
||||
wrapper.find('.ant-upload-drag-container').simulate('dragover', {
|
||||
fireEvent.dragOver(wrapper.querySelector('.ant-upload-drag-container'), {
|
||||
target: {
|
||||
files: [{ file: 'foo.png' }],
|
||||
},
|
||||
});
|
||||
|
||||
act(() => {
|
||||
await act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('.ant-upload-drag').hasClass('ant-upload-drag-hover')).toBe(true);
|
||||
await waitFor(() => {
|
||||
expect(wrapper.querySelector('.ant-upload-drag')).toHaveClass('ant-upload-drag-hover');
|
||||
});
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('support onDrop when files are dropped onto upload area', () => {
|
||||
it('support onDrop when files are dropped onto upload area', async () => {
|
||||
const onDrop = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload.Dragger onDrop={onDrop}>
|
||||
<div />
|
||||
</Upload.Dragger>,
|
||||
);
|
||||
|
||||
wrapper.find('.ant-upload-drag-container').simulate('drop', {
|
||||
fireEvent.drop(wrapper.querySelector('.ant-upload-drag-container'), {
|
||||
dataTransfer: {
|
||||
files: [new File(['foo'], 'foo.png', { type: 'image/png' })],
|
||||
},
|
||||
});
|
||||
|
||||
expect(onDrop).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(onDrop).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,17 +1,16 @@
|
||||
/* 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 produce from 'immer';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Upload from '..';
|
||||
import Form from '../../form';
|
||||
import { getFileItem, removeFileItem, isImageUrl } from '../utils';
|
||||
import { setup, teardown } from './mock';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { sleep, render, fireEvent } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import Form from '../../form';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
import { getFileItem, isImageUrl, removeFileItem } from '../utils';
|
||||
import { setup, teardown } from './mock';
|
||||
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
@ -46,12 +45,14 @@ describe('Upload', () => {
|
||||
);
|
||||
}
|
||||
}
|
||||
mount(<App />);
|
||||
render(<App />);
|
||||
expect(ref).toBeDefined();
|
||||
});
|
||||
|
||||
it('return promise in beforeUpload', done => {
|
||||
it('return promise in beforeUpload', async () => {
|
||||
jest.useFakeTimers();
|
||||
const data = jest.fn();
|
||||
const done = jest.fn();
|
||||
const props = {
|
||||
action: 'http://upload.com',
|
||||
beforeUpload: () =>
|
||||
@ -67,19 +68,33 @@ describe('Upload', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload {...props}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
wrapper.find('input').simulate('change', {
|
||||
fireEvent.change(wrapper.querySelector('input'), {
|
||||
target: {
|
||||
files: [{ file: 'foo.png' }],
|
||||
},
|
||||
});
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await act(async () => {
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await Promise.resolve();
|
||||
}
|
||||
});
|
||||
expect(done).toHaveBeenCalled();
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('beforeUpload can be falsy', done => {
|
||||
it('beforeUpload can be falsy', async () => {
|
||||
jest.useFakeTimers();
|
||||
const done = jest.fn();
|
||||
const props = {
|
||||
action: 'http://upload.com',
|
||||
beforeUpload: false,
|
||||
@ -90,20 +105,30 @@ describe('Upload', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload {...props}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
|
||||
wrapper.find('input').simulate('change', {
|
||||
fireEvent.change(wrapper.querySelector('input'), {
|
||||
target: {
|
||||
files: [{ file: 'foo.png' }],
|
||||
},
|
||||
});
|
||||
await act(async () => {
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await Promise.resolve();
|
||||
}
|
||||
});
|
||||
expect(done).toHaveBeenCalled();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('upload promise return file in beforeUpload', done => {
|
||||
it('upload promise return file in beforeUpload', async () => {
|
||||
jest.useFakeTimers();
|
||||
const done = jest.fn();
|
||||
const data = jest.fn();
|
||||
const props = {
|
||||
action: 'http://upload.com',
|
||||
@ -125,17 +150,29 @@ describe('Upload', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload {...props}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
|
||||
wrapper.find('input').simulate('change', {
|
||||
fireEvent.change(wrapper.querySelector('input'), {
|
||||
target: {
|
||||
files: [{ file: 'foo.png' }],
|
||||
},
|
||||
});
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await act(async () => {
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
expect(done).toHaveBeenCalled();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should not stop upload when return value of beforeUpload is false', done => {
|
||||
@ -162,13 +199,13 @@ describe('Upload', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload {...props}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
|
||||
wrapper.find('input').simulate('change', {
|
||||
fireEvent.change(wrapper.querySelector('input'), {
|
||||
target: {
|
||||
files: [mockFile],
|
||||
},
|
||||
@ -187,13 +224,13 @@ describe('Upload', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload {...props}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
|
||||
wrapper.find('input').simulate('change', {
|
||||
fireEvent.change(wrapper.querySelector('input'), {
|
||||
target: {
|
||||
files: [{ file: 'foo.png' }],
|
||||
},
|
||||
@ -247,10 +284,10 @@ describe('Upload', () => {
|
||||
</Form>
|
||||
);
|
||||
|
||||
const wrapper = mount(<Demo />);
|
||||
expect(wrapper.find('input#upload').length).toBe(1);
|
||||
wrapper.setProps({ disabled: true });
|
||||
expect(wrapper.find('input#upload').length).toBe(0);
|
||||
const { container: wrapper, rerender } = render(<Demo />);
|
||||
expect(wrapper.querySelectorAll('input#upload').length).toBe(1);
|
||||
rerender(<Demo disabled />);
|
||||
expect(wrapper.querySelectorAll('input#upload').length).toBe(0);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/24197
|
||||
@ -265,10 +302,10 @@ describe('Upload', () => {
|
||||
</Form>
|
||||
);
|
||||
|
||||
const wrapper = mount(<Demo />);
|
||||
expect(wrapper.find('input#upload').length).toBe(1);
|
||||
wrapper.setProps({ disabled: true });
|
||||
expect(wrapper.find('input#upload').length).toBe(0);
|
||||
const { container: wrapper, rerender } = render(<Demo />);
|
||||
expect(wrapper.querySelectorAll('input#upload').length).toBe(1);
|
||||
rerender(<Demo disabled />);
|
||||
expect(wrapper.querySelectorAll('input#upload').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should be controlled by fileList', () => {
|
||||
@ -282,10 +319,9 @@ describe('Upload', () => {
|
||||
},
|
||||
];
|
||||
const ref = React.createRef();
|
||||
const wrapper = mount(<Upload ref={ref} />);
|
||||
const { rerender } = render(<Upload ref={ref} />);
|
||||
expect(ref.current.fileList).toEqual([]);
|
||||
|
||||
wrapper.setProps({ fileList });
|
||||
rerender(<Upload ref={ref} fileList={fileList} />);
|
||||
jest.runAllTimers();
|
||||
expect(ref.current.fileList).toEqual(fileList);
|
||||
jest.useRealTimers();
|
||||
@ -299,7 +335,7 @@ describe('Upload', () => {
|
||||
url: 'http://www.baidu.com/xxx.png',
|
||||
},
|
||||
];
|
||||
mount(<Upload fileList={fileList} />);
|
||||
render(<Upload fileList={fileList} />);
|
||||
fileList.forEach(file => {
|
||||
expect(file.uid).toBeDefined();
|
||||
});
|
||||
@ -395,10 +431,10 @@ describe('Upload', () => {
|
||||
},
|
||||
},
|
||||
];
|
||||
const wrapper = mount(<Upload fileList={fileList} />);
|
||||
const linkNode = wrapper.find('a.ant-upload-list-item-name');
|
||||
expect(linkNode.props().download).toBe('image');
|
||||
expect(linkNode.props().rel).toBe('noopener');
|
||||
const { container: wrapper } = render(<Upload fileList={fileList} />);
|
||||
const linkNode = wrapper.querySelector('a.ant-upload-list-item-name');
|
||||
expect(linkNode.getAttribute('download')).toBe('image');
|
||||
expect(linkNode.getAttribute('rel')).toBe('noopener');
|
||||
});
|
||||
|
||||
it('should support linkProps as json stringify', () => {
|
||||
@ -415,10 +451,10 @@ describe('Upload', () => {
|
||||
linkProps: linkPropsString,
|
||||
},
|
||||
];
|
||||
const wrapper = mount(<Upload fileList={fileList} />);
|
||||
const linkNode = wrapper.find('a.ant-upload-list-item-name');
|
||||
expect(linkNode.props().download).toBe('image');
|
||||
expect(linkNode.props().rel).toBe('noopener');
|
||||
const { container: wrapper } = render(<Upload fileList={fileList} />);
|
||||
const linkNode = wrapper.querySelector('a.ant-upload-list-item-name');
|
||||
expect(linkNode.getAttribute('download')).toBe('image');
|
||||
expect(linkNode.getAttribute('rel')).toBe('noopener');
|
||||
});
|
||||
|
||||
it('should not stop remove when return value of onRemove is false', done => {
|
||||
@ -435,13 +471,11 @@ describe('Upload', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const wrapper = mount(<Upload {...props} />);
|
||||
const { container: wrapper } = render(<Upload {...props} />);
|
||||
|
||||
wrapper.find('div.ant-upload-list-item .anticon-delete').simulate('click');
|
||||
fireEvent.click(wrapper.querySelector('div.ant-upload-list-item .anticon-delete'));
|
||||
|
||||
setTimeout(() => {
|
||||
wrapper.update();
|
||||
|
||||
expect(mockRemove).toHaveBeenCalled();
|
||||
expect(props.fileList).toHaveLength(1);
|
||||
expect(props.fileList[0].status).toBe('done');
|
||||
@ -504,13 +538,11 @@ describe('Upload', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const wrapper = mount(<Upload {...props} onDownload={() => {}} />);
|
||||
const { container: wrapper } = render(<Upload {...props} onDownload={() => {}} />);
|
||||
|
||||
wrapper.find('div.ant-upload-list-item .anticon-download').simulate('click');
|
||||
fireEvent.click(wrapper.querySelector('div.ant-upload-list-item .anticon-download'));
|
||||
|
||||
setTimeout(() => {
|
||||
wrapper.update();
|
||||
|
||||
expect(props.fileList).toHaveLength(1);
|
||||
expect(props.fileList[0].status).toBe('done');
|
||||
done();
|
||||
@ -520,7 +552,7 @@ describe('Upload', () => {
|
||||
// https://github.com/ant-design/ant-design/issues/14439
|
||||
it('should allow call abort function through upload instance', () => {
|
||||
const ref = React.createRef();
|
||||
mount(
|
||||
render(
|
||||
<Upload ref={ref}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
@ -530,18 +562,18 @@ describe('Upload', () => {
|
||||
|
||||
it('correct dragCls when type is drag', () => {
|
||||
const fileList = [{ status: 'uploading', uid: 'file' }];
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload type="drag" fileList={fileList}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
expect(wrapper.find('.ant-upload-drag-uploading').length).toBe(1);
|
||||
expect(wrapper.querySelectorAll('.ant-upload-drag-uploading').length).toBe(1);
|
||||
});
|
||||
|
||||
it('return when targetItem is null', () => {
|
||||
const fileList = [{ uid: 'file' }];
|
||||
const ref = React.createRef();
|
||||
mount(
|
||||
render(
|
||||
<Upload ref={ref} type="drag" fileList={fileList}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
@ -554,7 +586,7 @@ describe('Upload', () => {
|
||||
it('should replace file when targetItem already exists', () => {
|
||||
const fileList = [{ uid: 'file', name: 'file' }];
|
||||
const ref = React.createRef();
|
||||
const wrapper = mount(
|
||||
const { unmount } = render(
|
||||
<Upload ref={ref} defaultFileList={fileList}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
@ -574,22 +606,20 @@ describe('Upload', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(ref.current.fileList.length).toBe(1);
|
||||
expect(ref.current.fileList[0].originFileObj).toEqual({
|
||||
name: 'file1',
|
||||
uid: 'file',
|
||||
});
|
||||
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('warning if set `value`', () => {
|
||||
resetWarned();
|
||||
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
mount(<Upload value={[]} />);
|
||||
render(<Upload value={[]} />);
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Upload] `value` is not a valid prop, do you mean `fileList`?',
|
||||
);
|
||||
@ -603,8 +633,8 @@ describe('Upload', () => {
|
||||
type: 'video/mp4',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png',
|
||||
};
|
||||
const wrapper = mount(<Upload listType="picture-card" fileList={[file]} />);
|
||||
expect(wrapper.find('img').length).toBe(0);
|
||||
const { container: wrapper } = render(<Upload listType="picture-card" fileList={[file]} />);
|
||||
expect(wrapper.querySelectorAll('img').length).toBe(0);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/25077
|
||||
@ -612,24 +642,29 @@ describe('Upload', () => {
|
||||
const onClick = jest.fn();
|
||||
const onMouseEnter = jest.fn();
|
||||
const onMouseLeave = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload onClick={onClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
wrapper.find('.ant-upload').at(1).simulate('click');
|
||||
fireEvent.click(wrapper.querySelectorAll('.ant-upload')[1]);
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
wrapper.find('.ant-upload').at(1).simulate('mouseEnter');
|
||||
fireEvent.mouseEnter(wrapper.querySelectorAll('.ant-upload')[1]);
|
||||
expect(onMouseEnter).toHaveBeenCalled();
|
||||
wrapper.find('.ant-upload').at(1).simulate('mouseLeave');
|
||||
fireEvent.mouseLeave(wrapper.querySelectorAll('.ant-upload')[1]);
|
||||
expect(onMouseLeave).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/26427
|
||||
it('should sync file list with control mode', done => {
|
||||
it('should sync file list with control mode', async () => {
|
||||
jest.useFakeTimers();
|
||||
const done = jest.fn();
|
||||
let callTimes = 0;
|
||||
|
||||
const customRequest = jest.fn(async options => {
|
||||
// stop here to make sure new fileList has been set and passed to Upload
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
options.onProgress({ percent: 0 });
|
||||
const url = Promise.resolve('https://ant.design');
|
||||
options.onProgress({ percent: 100 });
|
||||
@ -639,7 +674,7 @@ describe('Upload', () => {
|
||||
const Demo = () => {
|
||||
const [fileList, setFileList] = React.useState([]);
|
||||
|
||||
const onChange = e => {
|
||||
const onChange = async e => {
|
||||
const newFileList = Array.isArray(e) ? e : e.fileList;
|
||||
setFileList(newFileList);
|
||||
const file = newFileList[0];
|
||||
@ -676,15 +711,29 @@ describe('Upload', () => {
|
||||
);
|
||||
};
|
||||
|
||||
const wrapper = mount(<Demo />);
|
||||
const { container: wrapper } = render(<Demo />);
|
||||
|
||||
act(() => {
|
||||
wrapper.find('input').simulate('change', {
|
||||
target: {
|
||||
files: [{ file: 'foo.png' }],
|
||||
},
|
||||
});
|
||||
fireEvent.change(wrapper.querySelector('input'), {
|
||||
target: {
|
||||
files: [{ file: 'foo.png' }],
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
for (let i = 0; i < 3; i += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await Promise.resolve();
|
||||
}
|
||||
});
|
||||
await act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(done).toHaveBeenCalled();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
describe('maxCount', () => {
|
||||
@ -704,13 +753,13 @@ describe('Upload', () => {
|
||||
maxCount: 1,
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload {...props}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
|
||||
wrapper.find('input').simulate('change', {
|
||||
fireEvent.change(wrapper.querySelector('input'), {
|
||||
target: {
|
||||
files: [
|
||||
new File(['foo'], 'foo.png', {
|
||||
@ -746,13 +795,13 @@ describe('Upload', () => {
|
||||
maxCount: 2,
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload {...props}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
|
||||
wrapper.find('input').simulate('change', {
|
||||
fireEvent.change(wrapper.querySelector('input'), {
|
||||
target: {
|
||||
files: [
|
||||
new File(['foo'], 'foo.png', {
|
||||
@ -788,7 +837,7 @@ describe('Upload', () => {
|
||||
|
||||
expect(fileList[0].uid).toBeFalsy();
|
||||
|
||||
mount(
|
||||
render(
|
||||
<Upload fileList={fileList}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
@ -800,13 +849,13 @@ describe('Upload', () => {
|
||||
it('Proxy should support deepClone', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container: wrapper } = render(
|
||||
<Upload onChange={onChange}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
|
||||
wrapper.find('input').simulate('change', {
|
||||
fireEvent.change(wrapper.querySelector('input'), {
|
||||
target: {
|
||||
files: [
|
||||
new File(['foo'], 'foo.png', {
|
||||
@ -843,9 +892,9 @@ describe('Upload', () => {
|
||||
|
||||
const frozenFileList = fileList.map(file => Object.freeze(file));
|
||||
|
||||
const wrapper = mount(<Upload fileList={frozenFileList} />);
|
||||
const rmBtn = wrapper.find('.ant-upload-list-item-action').last();
|
||||
rmBtn.simulate('click');
|
||||
const { container: wrapper } = render(<Upload fileList={frozenFileList} />);
|
||||
const rmBtn = wrapper.querySelectorAll('.ant-upload-list-item-card-actions-btn');
|
||||
fireEvent.click(rmBtn[rmBtn.length - 1]);
|
||||
|
||||
// Wait for Upload async remove
|
||||
await act(async () => {
|
||||
@ -893,7 +942,7 @@ describe('Upload', () => {
|
||||
});
|
||||
|
||||
// Motion leave status change: start > active
|
||||
act(() => {
|
||||
await act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
@ -907,10 +956,10 @@ describe('Upload', () => {
|
||||
});
|
||||
|
||||
it('<Upload /> should pass <UploadList /> prefixCls', async () => {
|
||||
const wrapper1 = mount(<Upload />);
|
||||
expect(wrapper1.find('.ant-upload-list').exists()).toBeTruthy();
|
||||
const { container: wrapper } = render(<Upload />);
|
||||
expect(wrapper.querySelectorAll('.ant-upload-list').length).toBeGreaterThan(0);
|
||||
|
||||
const wrapper2 = mount(<Upload prefixCls="custom-upload" />);
|
||||
expect(wrapper2.find('.custom-upload-list').exists()).toBeTruthy();
|
||||
const { container: wrapper2 } = render(<Upload prefixCls="custom-upload" />);
|
||||
expect(wrapper2.querySelectorAll('.custom-upload-list').length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,8 +2,8 @@ import Dragger from './Dragger';
|
||||
import type { UploadProps } from './Upload';
|
||||
import InternalUpload, { LIST_IGNORE } from './Upload';
|
||||
|
||||
export { UploadProps, UploadListProps, UploadChangeParam, RcFile } from './interface';
|
||||
export { DraggerProps } from './Dragger';
|
||||
export { RcFile, UploadChangeParam, UploadFile, UploadListProps, UploadProps } from './interface';
|
||||
|
||||
type InternalUploadType = typeof InternalUpload;
|
||||
interface UploadInterface<T = any> extends InternalUploadType {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "antd",
|
||||
"version": "4.21.2",
|
||||
"version": "4.21.3",
|
||||
"description": "An enterprise-class UI design language and React components implementation",
|
||||
"title": "Ant Design",
|
||||
"keywords": [
|
||||
@ -157,7 +157,7 @@
|
||||
"rc-tree-select": "~5.4.0",
|
||||
"rc-trigger": "^5.2.10",
|
||||
"rc-upload": "~4.3.0",
|
||||
"rc-util": "^5.21.3",
|
||||
"rc-util": "^5.22.3",
|
||||
"scroll-into-view-if-needed": "^2.2.25",
|
||||
"shallowequal": "^1.1.0"
|
||||
},
|
||||
@ -236,7 +236,7 @@
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"immer": "^9.0.1",
|
||||
"immutability-helper": "^3.0.0",
|
||||
"inquirer": "^8.0.0",
|
||||
"inquirer": "^9.0.0",
|
||||
"intersection-observer": "^0.12.0",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"jest": "^28.0.3",
|
||||
@ -246,7 +246,7 @@
|
||||
"jest-image-snapshot": "^5.1.0",
|
||||
"jest-puppeteer": "^6.0.0",
|
||||
"jquery": "^3.4.1",
|
||||
"jsdom": "^19.0.0",
|
||||
"jsdom": "^20.0.0",
|
||||
"jsonml.js": "^0.1.0",
|
||||
"less-vars-to-js": "^1.3.0",
|
||||
"lz-string": "^1.4.4",
|
||||
|
Loading…
Reference in New Issue
Block a user