mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-04 21:18:01 +08:00
Merge pull request #35932 from ant-design/next-merge-master
chore: Next merge master
This commit is contained in:
commit
3ad20e57f5
2
.github/workflows/pr-check-ci.yml
vendored
2
.github/workflows/pr-check-ci.yml
vendored
@ -28,4 +28,4 @@ jobs:
|
||||
success-review: true
|
||||
success-merge: true
|
||||
merge-method: 'merge'
|
||||
merge-title: 'chore: auto merge branchs (#${number})'
|
||||
merge-title: 'chore: auto merge branches (#${number})'
|
||||
|
@ -15,6 +15,52 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.21.0
|
||||
|
||||
`2022-06-06`
|
||||
|
||||
- 🔥 Add Form level control input component `disabled`. [#35210](https://github.com/ant-design/ant-design/pull/35210) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🆕 Tabs support `popupClassName` for more Dropdown. [#35892](https://github.com/ant-design/ant-design/pull/35892)
|
||||
- 🆕 Table `rowSelection.onChange` support `info.type` param. [#35598](https://github.com/ant-design/ant-design/pull/35598)
|
||||
- 🆕 The `copyable` prop of Typography.Paragraph supports to reset the mime type of the clipboardData by the `format`. [#35219](https://github.com/ant-design/ant-design/pull/35219) [@kanweiwei](https://github.com/kanweiwei)
|
||||
- 🆕 TreeSelect support `treeExpandAction`. [#35618](https://github.com/ant-design/ant-design/pull/35618) [@NE-SmallTown](https://github.com/NE-SmallTown)
|
||||
- ConfigProvider
|
||||
- 🆕 ConfigProvider support config Pagination `showSizeChanger`. [#35750](https://github.com/ant-design/ant-design/pull/35750)
|
||||
- 🆕 ConfigProvider support `componentDisabled` to config components `disabled` status. [#35718](https://github.com/ant-design/ant-design/pull/35718)
|
||||
- 🛠 Refactor ConfigProvider removing default `renderEmpty` to resolve dist circle deps. [#35570](https://github.com/ant-design/ant-design/pull/35570)
|
||||
- Collapse
|
||||
- 🛠 Refactor Collapse to keep stable dom structure with header content. [#35781](https://github.com/ant-design/ant-design/pull/35781)
|
||||
- 🛠 Refactor Collapse `expandIconPosition` with logical position `start` or `end` to resolve RTL style issue. [#35770](https://github.com/ant-design/ant-design/pull/35770)
|
||||
- Progress
|
||||
- 🆕 Progress steps support custom strokeColor for each step. [#35855](https://github.com/ant-design/ant-design/pull/35855)
|
||||
- 🛠 Refactor Progress `type="circle"` and `type="dashboard"` for simpler dom structure and better rendering. [#35433](https://github.com/ant-design/ant-design/pull/35433)
|
||||
- 🛠 Refactor Progress to React hooks. [#35393](https://github.com/ant-design/ant-design/pull/35393) [@shuaijiumei](https://github.com/shuaijiumei)
|
||||
- 🐞 Fix Progress percent accuracy issue when near 100%. [#35433](https://github.com/ant-design/ant-design/pull/35433)
|
||||
- 🐞 Fix Progress `gapDegree` displayed with wrong degree when `type="dashboard"`. [#35433](https://github.com/ant-design/ant-design/pull/35433)
|
||||
- 💄 Fix Progress border radius style when `type="line"` and `strokeLinecap="butt"`. [#35822](https://github.com/ant-design/ant-design/pull/35822)
|
||||
- Dropdown
|
||||
- 🆕 Dropdown support `autoFocus` to focus elements in `overlay` automaticly when opened. [#35391](https://github.com/ant-design/ant-design/pull/35391)
|
||||
- 🛠 Fix Dropdown nesting menu injection logic. [#35810](https://github.com/ant-design/ant-design/pull/35810)
|
||||
- Card
|
||||
- 🛠 Refactor Card `loading` with Skeleton. [#35525](https://github.com/ant-design/ant-design/pull/35525)
|
||||
- 🛠 Refactor Card style to use flex instead of float. [#35236](https://github.com/ant-design/ant-design/pull/35236) [@miracles1919](https://github.com/miracles1919)
|
||||
- 🛠 DatePicker refactored to React hooks. [#35425](https://github.com/ant-design/ant-design/pull/35425) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🛠 Rename className of Pagination `mini` mode with `ant-pagination-mini`. [#35881](https://github.com/ant-design/ant-design/pull/35881)
|
||||
- 🛠 Refactor Popconfirm internal realize with Popover component. [#35676](https://github.com/ant-design/ant-design/pull/35676)
|
||||
- 🛠 Change the implementation of the bottom button layout of the Modal confirm component. [#35530](https://github.com/ant-design/ant-design/pull/35530) [@foryuki](https://github.com/foryuki)
|
||||
- 🐞 Fix the Wave effect does not work in React 18 strict mode. [#35889](https://github.com/ant-design/ant-design/pull/35889) [@Carrotzpc](https://github.com/Carrotzpc)
|
||||
- 🐞 Fix Drawer close twice `children` will undefined. [#35853](https://github.com/ant-design/ant-design/pull/35853) [@crazyair](https://github.com/crazyair)
|
||||
- Skeleton
|
||||
- 💄 Remove Skeleton default `margin-top` style. [#35848](https://github.com/ant-design/ant-design/pull/35848)
|
||||
- 💄 Optimize Skeleton `active` animation perfermance. [#35836](https://github.com/ant-design/ant-design/pull/35836) [@slotDumpling](https://github.com/slotDumpling)
|
||||
- 💄 Remove `!important` in Radio style with `disabled`. [#35920](https://github.com/ant-design/ant-design/pull/35920)
|
||||
- TypeScript
|
||||
- 🤖 Fix Form.List type `FormListFieldData` missing property `fieldKey`. [#35884](https://github.com/ant-design/ant-design/pull/35884) [@nanianlisao](https://github.com/nanianlisao)
|
||||
- 🌐 Localization
|
||||
- 🇹🇲 Add a new locale `tk_TK`. [#35605](https://github.com/ant-design/ant-design/pull/35605)
|
||||
- RTL
|
||||
- 💄 Fix the wrong direction of `border` and `border-radius` for InputNumber and Input in RTL mode. [#35876](https://github.com/ant-design/ant-design/pull/35876) [@yykoypj](https://github.com/yykoypj)
|
||||
|
||||
## 4.20.7
|
||||
|
||||
`2022-05-30`
|
||||
|
@ -15,6 +15,52 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.21.0
|
||||
|
||||
`2022-06-06`
|
||||
|
||||
- 🔥 新增 Form 级别控制输入组件 `disabled`。[#35210](https://github.com/ant-design/ant-design/pull/35210) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🆕 Tabs 组件支持 `popupClassName` 用于更多菜单。[#35892](https://github.com/ant-design/ant-design/pull/35892)
|
||||
- 🆕 Table 组件 `rowSelection.onChange` 新增 `info.type` 参数。[#35598](https://github.com/ant-design/ant-design/pull/35598)
|
||||
- 🆕 Typography.Paragraph 的 `copyable` 属性支持 `format` 以重置剪切板数据的 Mime Type。[#35219](https://github.com/ant-design/ant-design/pull/35219) [@kanweiwei](https://github.com/kanweiwei)
|
||||
- 🆕 TreeSelect 支持 `treeExpandAction` 定义展开操作。[#35618](https://github.com/ant-design/ant-design/pull/35618) [@NE-SmallTown](https://github.com/NE-SmallTown)
|
||||
- ConfigProvider
|
||||
- 🆕 ConfigProvider 支持全局配置 Pagination `showSizeChanger` 属性。[#35750](https://github.com/ant-design/ant-design/pull/35750)
|
||||
- 🆕 ConfigProvider 支持 `componentDisabled` 来配置组件禁用状态。[#35718](https://github.com/ant-design/ant-design/pull/35718)
|
||||
- 🛠 重构 ConfigProvider 移除默认的 `renderEmpty` 方法以解决打包循环依赖的问题。[#35570](https://github.com/ant-design/ant-design/pull/35570)
|
||||
- Collapse
|
||||
- 🛠 重构 Collapse 标题部分以确保其稳定的 DOM 结构易于样式选择。[#35781](https://github.com/ant-design/ant-design/pull/35781)
|
||||
- 🛠 重构 Collapse `expandIconPosition` 为逻辑位置 `start` 与 `end` 以解决 RTL 下的样式问题。[#35770](https://github.com/ant-design/ant-design/pull/35770)
|
||||
- Progress
|
||||
- 🆕 Progress 分步进度条支持单独自定义色彩。[#35855](https://github.com/ant-design/ant-design/pull/35855)
|
||||
- 🛠 重构 Progress `type="circle"` 和 `type="dashboard"` 以简化 dom 结构和带来更好的渲染效果。[#35433](https://github.com/ant-design/ant-design/pull/35433)
|
||||
- 🛠 重构 Progress 成 React hooks。[#35393](https://github.com/ant-design/ant-design/pull/35393) [@shuaijiumei](https://github.com/shuaijiumei)
|
||||
- 🐞 修复 Progress 进度接近 100% 间距几乎消失的问题。[#35433](https://github.com/ant-design/ant-design/pull/35433)
|
||||
- 🐞 修复 Progress `type="dashboard"` 的 `gapDegree` 角度不准确的问题。[#35433](https://github.com/ant-design/ant-design/pull/35433)
|
||||
- 💄 修复 Progress `type="line"` 和 `strokeLinecap="butt"` 时的圆角样式。[#35822](https://github.com/ant-design/ant-design/pull/35822)
|
||||
- Dropdown
|
||||
- 🆕 Dropdown 支持 `autoFocus` 属性,打开时自动聚焦下拉单。[#35391](https://github.com/ant-design/ant-design/pull/35391)
|
||||
- 🛠 修复 Dropdown 嵌套菜单注入逻辑。[#35810](https://github.com/ant-design/ant-design/pull/35810)
|
||||
- Card
|
||||
- 🛠 使用 Skeleton 重构 Card `loading` 属性。[#35525](https://github.com/ant-design/ant-design/pull/35525)
|
||||
- 🛠 重构 Card 样式用 flex 代替 float。[#35236](https://github.com/ant-design/ant-design/pull/35236) [@miracles1919](https://github.com/miracles1919)
|
||||
- 🛠 DatePicker 重构成 React hooks。[#35425](https://github.com/ant-design/ant-design/pull/35425) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🛠 将 Pagination `mini` 模式的 className 重命名为 `ant-pagination-mini`。[#35881](https://github.com/ant-design/ant-design/pull/35881)
|
||||
- 🛠 重构 Popconfirm 内部实现为 Popover 组件。[#35676](https://github.com/ant-design/ant-design/pull/35676)
|
||||
- 🛠 改变 Modal confirm 组件底部按钮布局实现方式。[#35530](https://github.com/ant-design/ant-design/pull/35530) [@foryuki](https://github.com/foryuki)
|
||||
- 🐞 修复波浪效果在 React 18 严格模式不生效的问题。[#35889](https://github.com/ant-design/ant-design/pull/35889) [@Carrotzpc](https://github.com/Carrotzpc)
|
||||
- 🐞 修复 Drawer 关闭 2 次后 `children` 为 undefined 的问题。[#35853](https://github.com/ant-design/ant-design/pull/35853) [@crazyair](https://github.com/crazyair)
|
||||
- Skeleton
|
||||
- 💄 移除 Skeleton 默认的 `margin-top` 以便在默认情况下更对称。[#35848](https://github.com/ant-design/ant-design/pull/35848)
|
||||
- 💄 优化 Skeleton `active` 的动画性能。[#35836](https://github.com/ant-design/ant-design/pull/35836) [@slotDumpling](https://github.com/slotDumpling)
|
||||
- 💄 移除 Radio 禁用状态时样式中的 `!important`。[#35920](https://github.com/ant-design/ant-design/pull/35920)
|
||||
- TypeScript
|
||||
- 🤖 修复 Form.List 类型 `FormListFieldData` 缺失属性 `fieldKey`。[#35884](https://github.com/ant-design/ant-design/pull/35884) [@nanianlisao](https://github.com/nanianlisao)
|
||||
- 🌐 国际化
|
||||
- 🇹🇲 添加土库曼语国际化。[#35605](https://github.com/ant-design/ant-design/pull/35605)
|
||||
- RTL
|
||||
- 💄 修正 Input 和 InputNumber 的 `border` 和 `border-radius` 在 RTL 模式下的方向问题。[#35876](https://github.com/ant-design/ant-design/pull/35876) [@yykoypj](https://github.com/yykoypj)
|
||||
|
||||
## 4.20.7
|
||||
|
||||
`2022-05-30`
|
||||
|
@ -55,6 +55,7 @@ export default class Wave extends React.Component<WaveProps> {
|
||||
context: ConfigConsumerProps;
|
||||
|
||||
componentDidMount() {
|
||||
this.destroyed = false;
|
||||
const node = this.containerRef.current as HTMLDivElement;
|
||||
if (!node || node.nodeType !== 1) {
|
||||
return;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import type { ReactWrapper } from 'enzyme';
|
||||
import { mount } from 'enzyme';
|
||||
import type { AffixProps, AffixState } from '..';
|
||||
import type { AffixProps, AffixState, InternalAffixClass } from '..';
|
||||
import Affix from '..';
|
||||
import { getObserverEntities } from '../utils';
|
||||
import Button from '../../button';
|
||||
@ -60,7 +60,6 @@ describe('Affix Render', () => {
|
||||
|
||||
const domMock = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect');
|
||||
let affixMounterWrapper: ReactWrapper<unknown, unknown, AffixMounter>;
|
||||
let affixWrapper: ReactWrapper<AffixProps, AffixState, React.Component<AffixProps, AffixState>>;
|
||||
|
||||
const classRect: Record<string, DOMRect> = {
|
||||
container: {
|
||||
@ -143,15 +142,35 @@ describe('Affix Render', () => {
|
||||
});
|
||||
|
||||
describe('updatePosition when target changed', () => {
|
||||
it('function change', () => {
|
||||
it('function change', async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
const container = document.querySelector('#id') as HTMLDivElement;
|
||||
const getTarget = () => container;
|
||||
affixWrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||
affixWrapper.setProps({ target: () => null });
|
||||
expect(affixWrapper.find('Affix').last().state().status).toBe(0);
|
||||
expect(affixWrapper.find('Affix').last().state().affixStyle).toBe(undefined);
|
||||
expect(affixWrapper.find('Affix').last().state().placeholderStyle).toBe(undefined);
|
||||
let affixInstance: InternalAffixClass;
|
||||
const { rerender } = render(
|
||||
<Affix
|
||||
ref={node => {
|
||||
affixInstance = node as InternalAffixClass;
|
||||
}}
|
||||
target={getTarget}
|
||||
>
|
||||
{null}
|
||||
</Affix>,
|
||||
);
|
||||
rerender(
|
||||
<Affix
|
||||
ref={node => {
|
||||
affixInstance = node as InternalAffixClass;
|
||||
}}
|
||||
target={() => null}
|
||||
>
|
||||
{null}
|
||||
</Affix>,
|
||||
);
|
||||
expect(affixInstance!.state.status).toBe(0);
|
||||
expect(affixInstance!.state.affixStyle).toBe(undefined);
|
||||
expect(affixInstance!.state.placeholderStyle).toBe(undefined);
|
||||
await sleep(100);
|
||||
});
|
||||
|
||||
it('instance change', async () => {
|
||||
@ -163,13 +182,12 @@ describe('Affix Render', () => {
|
||||
|
||||
const originLength = getObserverLength();
|
||||
const getTarget = () => target;
|
||||
affixWrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||
const { rerender } = render(<Affix target={getTarget}>{null}</Affix>);
|
||||
await sleep(100);
|
||||
|
||||
expect(getObserverLength()).toBe(originLength + 1);
|
||||
target = null;
|
||||
affixWrapper.setProps({});
|
||||
affixWrapper.update();
|
||||
rerender(<Affix>{null}</Affix>);
|
||||
await sleep(100);
|
||||
expect(getObserverLength()).toBe(originLength);
|
||||
});
|
||||
|
@ -300,6 +300,8 @@ class Affix extends React.Component<InternalAffixProps, AffixState> {
|
||||
);
|
||||
}
|
||||
}
|
||||
// just use in test
|
||||
export type InternalAffixClass = Affix;
|
||||
|
||||
const AffixFC = React.forwardRef<Affix, AffixProps>((props, ref) => {
|
||||
const { prefixCls: customizePrefixCls } = props;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Button from '../button';
|
||||
import { render, fireEvent } from '../../../tests/utils';
|
||||
|
||||
const specialDelay = 9529;
|
||||
const Content = () => {
|
||||
@ -33,7 +33,7 @@ it('Delay loading timer in Button component', () => {
|
||||
jest.spyOn(window, 'setTimeout').mockReturnValue(otherTimer);
|
||||
jest.restoreAllMocks();
|
||||
|
||||
const wrapper = mount(<Content />);
|
||||
const wrapper = render(<Content />);
|
||||
|
||||
const btnTimer: any = 9527;
|
||||
jest.spyOn(window, 'setTimeout').mockReturnValue(btnTimer);
|
||||
@ -52,7 +52,7 @@ it('Delay loading timer in Button component', () => {
|
||||
};
|
||||
|
||||
// switch loading state to true
|
||||
wrapper.find('#toggle_loading').at(0).simulate('click');
|
||||
fireEvent.click(wrapper.container.querySelectorAll('#toggle_loading')[0]);
|
||||
expect(setTimeoutCount()).toBe(1);
|
||||
expect(clearTimeoutCount()).toBe(0);
|
||||
|
||||
@ -64,24 +64,24 @@ it('Delay loading timer in Button component', () => {
|
||||
expect(clearTimeoutCount()).toBe(0);
|
||||
|
||||
// switch loading state to false
|
||||
wrapper.find('#toggle_loading').at(0).simulate('click');
|
||||
fireEvent.click(wrapper.container.querySelectorAll('#toggle_loading')[0]);
|
||||
expect(setTimeoutCount()).toBe(1);
|
||||
expect(clearTimeoutCount()).toBe(0);
|
||||
|
||||
// switch loading state to true
|
||||
wrapper.find('#toggle_loading').at(0).simulate('click');
|
||||
fireEvent.click(wrapper.container.querySelectorAll('#toggle_loading')[0]);
|
||||
expect(setTimeoutCount()).toBe(2);
|
||||
expect(clearTimeoutCount()).toBe(0);
|
||||
|
||||
// switch loading state to false
|
||||
wrapper.find('#toggle_loading').at(0).simulate('click');
|
||||
fireEvent.click(wrapper.container.querySelectorAll('#toggle_loading')[0]);
|
||||
expect(setTimeoutCount()).toBe(2);
|
||||
expect(clearTimeoutCount()).toBe(1);
|
||||
|
||||
// switch loading state to true
|
||||
wrapper.find('#toggle_loading').at(0).simulate('click');
|
||||
fireEvent.click(wrapper.container.querySelectorAll('#toggle_loading')[0]);
|
||||
// remove Button component
|
||||
wrapper.find('#toggle_visible').at(0).simulate('click');
|
||||
fireEvent.click(wrapper.container.querySelectorAll('#toggle_visible')[0]);
|
||||
expect(setTimeoutCount()).toBe(3);
|
||||
expect(clearTimeoutCount()).toBe(2);
|
||||
|
||||
|
@ -28,11 +28,12 @@ describe('Button', () => {
|
||||
rtlTest(() => <Button.Group size="middle" />);
|
||||
|
||||
it('renders correctly', () => {
|
||||
expect(mount(<Button>Follow</Button>).render()).toMatchSnapshot();
|
||||
const { container } = render(<Button>Follow</Button>);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('mount correctly', () => {
|
||||
expect(() => mount(<Button>Follow</Button>)).not.toThrow();
|
||||
expect(() => render(<Button>Follow</Button>)).not.toThrow();
|
||||
});
|
||||
|
||||
it('warns if size is wrong', () => {
|
||||
@ -46,38 +47,42 @@ describe('Button', () => {
|
||||
});
|
||||
|
||||
it('renders Chinese characters correctly', () => {
|
||||
expect(mount(<Button>按钮</Button>).render()).toMatchSnapshot();
|
||||
// should not insert space when there is icon
|
||||
expect(mount(<Button icon={<SearchOutlined />}>按钮</Button>).render()).toMatchSnapshot();
|
||||
expect(render(<Button>按钮</Button>).container.firstChild).toMatchSnapshot();
|
||||
// should not insert space when there is icon
|
||||
expect(
|
||||
mount(
|
||||
render(<Button icon={<SearchOutlined />}>按钮</Button>).container.firstChild,
|
||||
).toMatchSnapshot();
|
||||
// should not insert space when there is icon
|
||||
expect(
|
||||
render(
|
||||
<Button>
|
||||
<SearchOutlined />
|
||||
按钮
|
||||
</Button>,
|
||||
).render(),
|
||||
).container.firstChild,
|
||||
).toMatchSnapshot();
|
||||
// should not insert space when there is icon
|
||||
expect(mount(<Button icon={<SearchOutlined />}>按钮</Button>).render()).toMatchSnapshot();
|
||||
expect(
|
||||
render(<Button icon={<SearchOutlined />}>按钮</Button>).container.firstChild,
|
||||
).toMatchSnapshot();
|
||||
// should not insert space when there is icon while loading
|
||||
expect(
|
||||
mount(
|
||||
render(
|
||||
<Button icon={<SearchOutlined />} loading>
|
||||
按钮
|
||||
</Button>,
|
||||
).render(),
|
||||
).container.firstChild,
|
||||
).toMatchSnapshot();
|
||||
// should insert space while loading
|
||||
expect(mount(<Button loading>按钮</Button>).render()).toMatchSnapshot();
|
||||
expect(render(<Button loading>按钮</Button>).container.firstChild).toMatchSnapshot();
|
||||
|
||||
// should insert space while only one nested element
|
||||
expect(
|
||||
mount(
|
||||
render(
|
||||
<Button>
|
||||
<span>按钮</span>
|
||||
</Button>,
|
||||
).render(),
|
||||
).container.firstChild,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -107,20 +112,21 @@ describe('Button', () => {
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/18118
|
||||
it('should not insert space to link or text button', () => {
|
||||
const wrapper1 = mount(<Button type="link">按钮</Button>);
|
||||
expect(wrapper1.text()).toBe('按钮');
|
||||
const wrapper2 = mount(<Button type="text">按钮</Button>);
|
||||
expect(wrapper2.text()).toBe('按钮');
|
||||
const wrapper1 = render(<Button type="link">按钮</Button>);
|
||||
expect(wrapper1.getByRole('button')).toHaveTextContent('按钮');
|
||||
wrapper1.unmount();
|
||||
const wrapper2 = render(<Button type="text">按钮</Button>);
|
||||
expect(wrapper2.getByRole('button')).toHaveTextContent('按钮');
|
||||
});
|
||||
|
||||
it('should render empty button without errors', () => {
|
||||
const wrapper = mount(
|
||||
const wrapper = render(
|
||||
<Button>
|
||||
{null}
|
||||
{undefined}
|
||||
</Button>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapper.container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('have static property for type detecting', () => {
|
||||
@ -147,9 +153,9 @@ describe('Button', () => {
|
||||
);
|
||||
}
|
||||
}
|
||||
const wrapper = mount(<DefaultButton />);
|
||||
wrapper.simulate('click');
|
||||
expect(wrapper.find('.ant-btn-loading').length).toBe(1);
|
||||
const wrapper = render(<DefaultButton />);
|
||||
fireEvent.click(wrapper.container.firstChild!);
|
||||
expect(wrapper.container.querySelectorAll('.ant-btn-loading').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should change loading state with delay', () => {
|
||||
@ -171,25 +177,22 @@ describe('Button', () => {
|
||||
);
|
||||
}
|
||||
}
|
||||
const wrapper = mount(<DefaultButton />);
|
||||
wrapper.simulate('click');
|
||||
expect(wrapper.hasClass('ant-btn-loading')).toBe(false);
|
||||
const wrapper = render(<DefaultButton />);
|
||||
fireEvent.click(wrapper.container.firstChild!);
|
||||
expect(wrapper.container.firstChild).not.toHaveClass('ant-btn-loading');
|
||||
});
|
||||
|
||||
it('reset when loading back of delay', () => {
|
||||
jest.useFakeTimers();
|
||||
const wrapper = mount(<Button loading={{ delay: 1000 }} />);
|
||||
wrapper.update();
|
||||
wrapper.setProps({ loading: { delay: 2000 } });
|
||||
wrapper.update();
|
||||
wrapper.setProps({ loading: false });
|
||||
const { rerender, container } = render(<Button loading={{ delay: 1000 }} />);
|
||||
rerender(<Button loading={{ delay: 2000 }} />);
|
||||
rerender(<Button loading={false} />);
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
});
|
||||
|
||||
expect(wrapper.find('.ant-btn-loading')).toHaveLength(0);
|
||||
expect(container.querySelectorAll('.ant-btn-loading')).toHaveLength(0);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
@ -206,60 +209,55 @@ describe('Button', () => {
|
||||
});
|
||||
|
||||
it('should support link button', () => {
|
||||
const wrapper = mount(
|
||||
const wrapper = render(
|
||||
<Button target="_blank" href="https://ant.design">
|
||||
link button
|
||||
</Button>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapper.container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('fixbug renders {0} , 0 and {false}', () => {
|
||||
expect(mount(<Button>{0}</Button>).render()).toMatchSnapshot();
|
||||
expect(mount(<Button>0</Button>).render()).toMatchSnapshot();
|
||||
expect(mount(<Button>{false}</Button>).render()).toMatchSnapshot();
|
||||
expect(render(<Button>{0}</Button>).container.firstChild).toMatchSnapshot();
|
||||
expect(render(<Button>0</Button>).container.firstChild).toMatchSnapshot();
|
||||
expect(render(<Button>{false}</Button>).container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not render as link button when href is undefined', async () => {
|
||||
const wrapper = mount(
|
||||
const wrapper = render(
|
||||
<Button type="primary" href={undefined}>
|
||||
button
|
||||
</Button>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapper.container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/15342
|
||||
// // https://github.com/ant-design/ant-design/issues/15342
|
||||
it('should merge text if children using variable', () => {
|
||||
const wrapper = mount(
|
||||
const wrapper = render(
|
||||
<Button>
|
||||
{/* eslint-disable-next-line react/jsx-curly-brace-presence */}
|
||||
This {'is'} a test {1}
|
||||
</Button>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapper.container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support to change loading', async () => {
|
||||
const wrapper = mount(<Button>Button</Button>);
|
||||
wrapper.setProps({ loading: true });
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-btn-loading').length).toBe(1);
|
||||
wrapper.setProps({ loading: false });
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-btn-loading').length).toBe(0);
|
||||
wrapper.setProps({ loading: { delay: 50 } });
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-btn-loading').length).toBe(0);
|
||||
const { rerender, container, unmount } = render(<Button>Button</Button>);
|
||||
rerender(<Button loading />);
|
||||
expect(container.querySelectorAll('.ant-btn-loading').length).toBe(1);
|
||||
rerender(<Button loading={false} />);
|
||||
expect(container.querySelectorAll('.ant-btn-loading').length).toBe(0);
|
||||
rerender(<Button loading={{ delay: 50 }} />);
|
||||
expect(container.querySelectorAll('.ant-btn-loading').length).toBe(0);
|
||||
await sleep(50);
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-btn-loading').length).toBe(1);
|
||||
wrapper.setProps({ loading: false });
|
||||
expect(container.querySelectorAll('.ant-btn-loading').length).toBe(1);
|
||||
rerender(<Button loading={false} />);
|
||||
await sleep(50);
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-btn-loading').length).toBe(0);
|
||||
expect(container.querySelectorAll('.ant-btn-loading').length).toBe(0);
|
||||
expect(() => {
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
@ -281,7 +279,7 @@ describe('Button', () => {
|
||||
it('should warning when pass type=link and ghost=true', () => {
|
||||
resetWarned();
|
||||
const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
mount(<Button type="link" ghost />);
|
||||
render(<Button type="link" ghost />);
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
"Warning: [antd: Button] `link` or `text` button can't be a `ghost` button.",
|
||||
);
|
||||
@ -291,7 +289,7 @@ describe('Button', () => {
|
||||
it('should warning when pass type=text and ghost=true', () => {
|
||||
resetWarned();
|
||||
const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
mount(<Button type="text" ghost />);
|
||||
render(<Button type="text" ghost />);
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
"Warning: [antd: Button] `link` or `text` button can't be a `ghost` button.",
|
||||
);
|
||||
@ -299,14 +297,20 @@ describe('Button', () => {
|
||||
});
|
||||
|
||||
it('skip check 2 words when ConfigProvider disable this', () => {
|
||||
const wrapper = mount(
|
||||
let buttonInstance: any;
|
||||
render(
|
||||
<ConfigProvider autoInsertSpaceInButton={false}>
|
||||
<Button>test</Button>
|
||||
<Button
|
||||
ref={node => {
|
||||
buttonInstance = node;
|
||||
}}
|
||||
>
|
||||
test
|
||||
</Button>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
const btn = wrapper.find('button').instance();
|
||||
Object.defineProperty(btn, 'textContent', {
|
||||
Object.defineProperty(buttonInstance, 'textContent', {
|
||||
get() {
|
||||
throw new Error('Should not called!!!');
|
||||
},
|
||||
@ -326,12 +330,12 @@ describe('Button', () => {
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/30953
|
||||
it('should handle fragment as children', () => {
|
||||
const wrapper = mount(
|
||||
const wrapper = render(
|
||||
<Button>
|
||||
{/* eslint-disable-next-line react/jsx-no-useless-fragment */}
|
||||
<>text</>
|
||||
</Button>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapper.container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -1,93 +1,116 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Button from '..';
|
||||
import Wave from '../../_util/wave';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import { sleep, render, fireEvent } from '../../../tests/utils';
|
||||
|
||||
// Mock Wave ref
|
||||
let waveInstanceMock: any;
|
||||
jest.mock('../../_util/wave', () => {
|
||||
const Wave = jest.requireActual('../../_util/wave');
|
||||
const WaveComponent = Wave.default;
|
||||
return {
|
||||
...Wave,
|
||||
__esModule: true,
|
||||
default: (props: any) => (
|
||||
<WaveComponent
|
||||
ref={(node: any) => {
|
||||
waveInstanceMock = node;
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
describe('click wave effect', () => {
|
||||
async function clickButton(wrapper: any) {
|
||||
wrapper.find('.ant-btn').getDOMNode().click();
|
||||
wrapper.find('.ant-btn').getDOMNode().dispatchEvent(new Event('transitionstart'));
|
||||
const element = wrapper.container.firstChild;
|
||||
fireEvent.click(element);
|
||||
fireEvent(element, new Event('transitionstart'));
|
||||
await sleep(20);
|
||||
wrapper.find('.ant-btn').getDOMNode().dispatchEvent(new Event('animationend'));
|
||||
fireEvent(element, new Event('animationend'));
|
||||
await sleep(20);
|
||||
}
|
||||
|
||||
it('should have click wave effect for primary button', async () => {
|
||||
const wrapper = mount(<Button type="primary">button</Button>);
|
||||
const wrapper = render(<Button type="primary">button</Button>);
|
||||
await clickButton(wrapper);
|
||||
expect(
|
||||
wrapper.find('.ant-btn').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
|
||||
).toBe(true);
|
||||
expect(wrapper.container.querySelector('.ant-btn')).toHaveAttribute(
|
||||
'ant-click-animating-without-extra-node',
|
||||
);
|
||||
});
|
||||
|
||||
it('should have click wave effect for default button', async () => {
|
||||
const wrapper = mount(<Button>button</Button>);
|
||||
const wrapper = render(<Button>button</Button>);
|
||||
await clickButton(wrapper);
|
||||
expect(
|
||||
wrapper.find('.ant-btn').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
|
||||
).toBe(true);
|
||||
expect(wrapper.container.querySelector('.ant-btn')).toHaveAttribute(
|
||||
'ant-click-animating-without-extra-node',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not have click wave effect for link type button', async () => {
|
||||
const wrapper = mount(<Button type="link">button</Button>);
|
||||
const wrapper = render(<Button type="link">button</Button>);
|
||||
await clickButton(wrapper);
|
||||
expect(
|
||||
wrapper.find('.ant-btn').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
|
||||
).toBe(false);
|
||||
expect(wrapper.container.querySelector('.ant-btn')).not.toHaveAttribute(
|
||||
'ant-click-animating-without-extra-node',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not have click wave effect for text type button', async () => {
|
||||
const wrapper = mount(<Button type="text">button</Button>);
|
||||
const wrapper = render(<Button type="text">button</Button>);
|
||||
await clickButton(wrapper);
|
||||
expect(
|
||||
wrapper.find('.ant-btn').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
|
||||
).toBe(false);
|
||||
expect(wrapper.container.querySelector('.ant-btn')).not.toHaveAttribute(
|
||||
'ant-click-animating-without-extra-node',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle transitionstart', async () => {
|
||||
const wrapper = mount(<Button type="primary">button</Button>);
|
||||
const wrapper = render(<Button type="primary">button</Button>);
|
||||
await clickButton(wrapper);
|
||||
const buttonNode = wrapper.find('.ant-btn').getDOMNode();
|
||||
buttonNode.dispatchEvent(new Event('transitionstart'));
|
||||
expect(
|
||||
wrapper.find('.ant-btn').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
|
||||
).toBe(true);
|
||||
const buttonNode = wrapper.container.querySelector('.ant-btn')!;
|
||||
fireEvent(buttonNode, new Event('transitionstart'));
|
||||
expect(wrapper.container.querySelector('.ant-btn')).toHaveAttribute(
|
||||
'ant-click-animating-without-extra-node',
|
||||
);
|
||||
wrapper.unmount();
|
||||
buttonNode.dispatchEvent(new Event('transitionstart'));
|
||||
fireEvent(buttonNode, new Event('transitionstart'));
|
||||
});
|
||||
|
||||
it('should run resetEffect in transitionstart', async () => {
|
||||
const wrapper = mount(<Button type="primary">button</Button>);
|
||||
const waveInstance = wrapper.find(Wave).instance() as any;
|
||||
const resetEffect = jest.spyOn(waveInstance, 'resetEffect');
|
||||
const wrapper = render(<Button type="primary">button</Button>);
|
||||
const resetEffect = jest.spyOn(waveInstanceMock, 'resetEffect');
|
||||
await clickButton(wrapper);
|
||||
expect(resetEffect).toHaveBeenCalledTimes(1);
|
||||
wrapper.find('.ant-btn').getDOMNode<HTMLButtonElement>().click();
|
||||
fireEvent.click(wrapper.container.querySelector('.ant-btn')!);
|
||||
await sleep(10);
|
||||
expect(resetEffect).toHaveBeenCalledTimes(2);
|
||||
waveInstance.animationStart = false;
|
||||
wrapper.find('.ant-btn').getDOMNode().dispatchEvent(new Event('transitionstart'));
|
||||
waveInstanceMock.animationStart = false;
|
||||
fireEvent(wrapper.container.querySelector('.ant-btn')!, new Event('transitionstart'));
|
||||
expect(resetEffect).toHaveBeenCalledTimes(3);
|
||||
resetEffect.mockRestore();
|
||||
});
|
||||
|
||||
it('should handle transitionend', async () => {
|
||||
const wrapper = mount(<Button type="primary">button</Button>);
|
||||
const waveInstance = wrapper.find(Wave).instance() as any;
|
||||
const resetEffect = jest.spyOn(waveInstance, 'resetEffect');
|
||||
const wrapper = render(<Button type="primary">button</Button>);
|
||||
const resetEffect = jest.spyOn(waveInstanceMock, 'resetEffect');
|
||||
await clickButton(wrapper);
|
||||
expect(resetEffect).toHaveBeenCalledTimes(1);
|
||||
const event = new Event('animationend');
|
||||
Object.assign(event, { animationName: 'fadeEffect' });
|
||||
wrapper.find('.ant-btn').getDOMNode().dispatchEvent(event);
|
||||
fireEvent(wrapper.container.querySelector('.ant-btn')!, event);
|
||||
expect(resetEffect).toHaveBeenCalledTimes(2);
|
||||
resetEffect.mockRestore();
|
||||
});
|
||||
|
||||
it('Wave on falsy element', async () => {
|
||||
const wrapper = mount(<Wave />);
|
||||
const waveInstance = wrapper.find(Wave).instance() as any;
|
||||
const { default: Wave } = jest.requireActual('../../_util/wave');
|
||||
let waveInstance: any;
|
||||
render(
|
||||
<Wave
|
||||
ref={(node: any) => {
|
||||
waveInstance = node;
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
waveInstance.resetEffect();
|
||||
});
|
||||
});
|
||||
|
@ -32,21 +32,21 @@ When data is in the form of dates, such as schedules, timetables, prices calenda
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| dateCellRender | Customize the display of the date cell, the returned content will be appended to the cell | function(date: moment): ReactNode | - | |
|
||||
| dateFullCellRender | Customize the display of the date cell, the returned content will override the cell | function(date: moment): ReactNode | - | |
|
||||
| dateCellRender | Customize the display of the date cell, the returned content will be appended to the cell | function(date: Moment): ReactNode | - | |
|
||||
| dateFullCellRender | Customize the display of the date cell, the returned content will override the cell | function(date: Moment): ReactNode | - | |
|
||||
| defaultValue | The date selected by default | [moment](http://momentjs.com/) | - | |
|
||||
| disabledDate | Function that specifies the dates that cannot be selected, `currentDate` is same moment object as `value` prop which you shouldn't mutate it](https://github.com/ant-design/ant-design/issues/30987) | (currentDate: moment) => boolean | - | |
|
||||
| disabledDate | Function that specifies the dates that cannot be selected, `currentDate` is same moment object as `value` prop which you shouldn't mutate it](https://github.com/ant-design/ant-design/issues/30987) | (currentDate: Moment) => boolean | - | |
|
||||
| fullscreen | Whether to display in full-screen | boolean | true | |
|
||||
| headerRender | Render custom header in panel | function(object:{value: moment, type: string, onChange: f(), onTypeChange: f()}) | - | |
|
||||
| headerRender | Render custom header in panel | function(object:{value: Moment, type: string, onChange: f(), onTypeChange: f()}) | - | |
|
||||
| locale | The calendar's locale | object | [(default)](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) | |
|
||||
| mode | The display mode of the calendar | `month` \| `year` | `month` | |
|
||||
| monthCellRender | Customize the display of the month cell, the returned content will be appended to the cell | function(date: moment): ReactNode | - | |
|
||||
| monthFullCellRender | Customize the display of the month cell, the returned content will override the cell | function(date: moment): ReactNode | - | |
|
||||
| monthCellRender | Customize the display of the month cell, the returned content will be appended to the cell | function(date: Moment): ReactNode | - | |
|
||||
| monthFullCellRender | Customize the display of the month cell, the returned content will override the cell | function(date: Moment): ReactNode | - | |
|
||||
| validRange | To set valid range | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - | |
|
||||
| value | The current selected date | [moment](http://momentjs.com/) | - | |
|
||||
| onChange | Callback for when date changes | function(date: moment) | - | |
|
||||
| onPanelChange | Callback for when panel changes | function(date: moment, mode: string) | - | |
|
||||
| onSelect | Callback for when a date is selected | function(date: moment) | - | |
|
||||
| onChange | Callback for when date changes | function(date: Moment) | - | |
|
||||
| onPanelChange | Callback for when panel changes | function(date: Moment, mode: string) | - | |
|
||||
| onSelect | Callback for when a date is selected | function(date: Moment) | - | |
|
||||
|
||||
## FAQ
|
||||
|
||||
|
@ -33,21 +33,21 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/dPQmLq08DI/Calendar.svg
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| dateCellRender | 自定义渲染日期单元格,返回内容会被追加到单元格 | function(date: moment): ReactNode | - | |
|
||||
| dateFullCellRender | 自定义渲染日期单元格,返回内容覆盖单元格 | function(date: moment): ReactNode | - | |
|
||||
| dateCellRender | 自定义渲染日期单元格,返回内容会被追加到单元格 | function(date: Moment): ReactNode | - | |
|
||||
| dateFullCellRender | 自定义渲染日期单元格,返回内容覆盖单元格 | function(date: Moment): ReactNode | - | |
|
||||
| defaultValue | 默认展示的日期 | [moment](http://momentjs.com/) | - | |
|
||||
| disabledDate | 不可选择的日期,参数为当前 `value`,注意使用时[不要直接修改](https://github.com/ant-design/ant-design/issues/30987) | (currentDate: moment) => boolean | - | |
|
||||
| disabledDate | 不可选择的日期,参数为当前 `value`,注意使用时[不要直接修改](https://github.com/ant-design/ant-design/issues/30987) | (currentDate: Moment) => boolean | - | |
|
||||
| fullscreen | 是否全屏显示 | boolean | true | |
|
||||
| headerRender | 自定义头部内容 | function(object:{value: moment, type: string, onChange: f(), onTypeChange: f()}) | - | |
|
||||
| headerRender | 自定义头部内容 | function(object:{value: Moment, type: string, onChange: f(), onTypeChange: f()}) | - | |
|
||||
| locale | 国际化配置 | object | [(默认配置)](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) | |
|
||||
| mode | 初始模式 | `month` \| `year` | `month` | |
|
||||
| monthCellRender | 自定义渲染月单元格,返回内容会被追加到单元格 | function(date: moment): ReactNode | - | |
|
||||
| monthFullCellRender | 自定义渲染月单元格,返回内容覆盖单元格 | function(date: moment): ReactNode | - | |
|
||||
| monthCellRender | 自定义渲染月单元格,返回内容会被追加到单元格 | function(date: Moment): ReactNode | - | |
|
||||
| monthFullCellRender | 自定义渲染月单元格,返回内容覆盖单元格 | function(date: Moment): ReactNode | - | |
|
||||
| validRange | 设置可以显示的日期 | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - | |
|
||||
| value | 展示日期 | [moment](http://momentjs.com/) | - | |
|
||||
| onChange | 日期变化回调 | function(date: moment) | - | |
|
||||
| onPanelChange | 日期面板变化回调 | function(date: moment, mode: string) | - | |
|
||||
| onSelect | 点击选择日期回调 | function(date: moment) | - | |
|
||||
| onChange | 日期变化回调 | function(date: Moment) | - | |
|
||||
| onPanelChange | 日期面板变化回调 | function(date: Moment, mode: string) | - | |
|
||||
| onSelect | 点击选择日期回调 | function(date: Moment) | - | |
|
||||
|
||||
## FAQ
|
||||
|
||||
|
@ -3,6 +3,7 @@ import classNames from 'classnames';
|
||||
import RcDrawer from 'rc-drawer';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { NoFormStyle } from '../form/context';
|
||||
import { tuple } from '../_util/type';
|
||||
// CSSINJS
|
||||
import useStyle from './style';
|
||||
@ -299,37 +300,39 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
|
||||
|
||||
return wrapSSR(
|
||||
<DrawerContext.Provider value={operations}>
|
||||
<RcDrawer
|
||||
handler={false}
|
||||
{...{
|
||||
placement,
|
||||
prefixCls,
|
||||
maskClosable,
|
||||
level,
|
||||
keyboard,
|
||||
children,
|
||||
onClose,
|
||||
forceRender,
|
||||
...rest,
|
||||
}}
|
||||
{...offsetStyle}
|
||||
open={visible || propsVisible}
|
||||
showMask={mask}
|
||||
style={getRcDrawerStyle()}
|
||||
className={drawerClassName}
|
||||
getContainer={getContainer}
|
||||
afterVisibleChange={open => {
|
||||
if (open) {
|
||||
destroyCloseRef.current = false;
|
||||
} else if (destroyOnClose) {
|
||||
destroyCloseRef.current = true;
|
||||
setLoad(false);
|
||||
}
|
||||
afterVisibleChange?.(open);
|
||||
}}
|
||||
>
|
||||
{renderBody()}
|
||||
</RcDrawer>
|
||||
<NoFormStyle status override>
|
||||
<RcDrawer
|
||||
handler={false}
|
||||
{...{
|
||||
placement,
|
||||
prefixCls,
|
||||
maskClosable,
|
||||
level,
|
||||
keyboard,
|
||||
children,
|
||||
onClose,
|
||||
forceRender,
|
||||
...rest,
|
||||
}}
|
||||
{...offsetStyle}
|
||||
open={visible || propsVisible}
|
||||
showMask={mask}
|
||||
style={getRcDrawerStyle()}
|
||||
className={drawerClassName}
|
||||
getContainer={getContainer}
|
||||
afterVisibleChange={open => {
|
||||
if (open) {
|
||||
destroyCloseRef.current = false;
|
||||
} else if (destroyOnClose) {
|
||||
destroyCloseRef.current = true;
|
||||
setLoad(false);
|
||||
}
|
||||
afterVisibleChange?.(open);
|
||||
}}
|
||||
>
|
||||
{renderBody()}
|
||||
</RcDrawer>
|
||||
</NoFormStyle>
|
||||
</DrawerContext.Provider>,
|
||||
);
|
||||
},
|
||||
|
@ -1,34 +1,34 @@
|
||||
import * as React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import classNames from 'classnames';
|
||||
import type { FormInstance } from 'rc-field-form';
|
||||
import { Field, FieldContext, ListContext } from 'rc-field-form';
|
||||
import type { FieldProps } from 'rc-field-form/lib/Field';
|
||||
import type { Meta, NamePath } from 'rc-field-form/lib/interface';
|
||||
import { supportRef } from 'rc-util/lib/ref';
|
||||
import useState from 'rc-util/lib/hooks/useState';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
|
||||
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import useStyle from './style';
|
||||
import Row from '../grid/row';
|
||||
import { supportRef } from 'rc-util/lib/ref';
|
||||
import type { ReactNode } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import Row from '../grid/row';
|
||||
import { cloneElement, isValidElement } from '../_util/reactNode';
|
||||
import { tuple } from '../_util/type';
|
||||
import warning from '../_util/warning';
|
||||
import type { FormItemLabelProps, LabelTooltipType } from './FormItemLabel';
|
||||
import FormItemLabel from './FormItemLabel';
|
||||
import type { FormItemInputProps } from './FormItemInput';
|
||||
import FormItemInput from './FormItemInput';
|
||||
import type { FormItemStatusContextProps } from './context';
|
||||
import { FormContext, FormItemInputContext, NoStyleItemContext } from './context';
|
||||
import { toArray, getFieldId } from './util';
|
||||
import { cloneElement, isValidElement } from '../_util/reactNode';
|
||||
import useFrameState from './hooks/useFrameState';
|
||||
import type { FormItemInputProps } from './FormItemInput';
|
||||
import FormItemInput from './FormItemInput';
|
||||
import type { FormItemLabelProps, LabelTooltipType } from './FormItemLabel';
|
||||
import FormItemLabel from './FormItemLabel';
|
||||
import useDebounce from './hooks/useDebounce';
|
||||
import useFrameState from './hooks/useFrameState';
|
||||
import useItemRef from './hooks/useItemRef';
|
||||
import { getFieldId, toArray } from './util';
|
||||
import useStyle from './style';
|
||||
|
||||
const NAME_SPLIT = '__SPLIT__';
|
||||
|
||||
@ -221,9 +221,14 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
const getItemRef = useItemRef();
|
||||
|
||||
// ======================== Status ========================
|
||||
let mergedValidateStatus: ValidateStatus = '';
|
||||
const { status: contextStatus, hasFeedback: contextHasFeedback } =
|
||||
useContext(FormItemInputContext);
|
||||
|
||||
let mergedValidateStatus: ValidateStatus | undefined;
|
||||
if (validateStatus !== undefined) {
|
||||
mergedValidateStatus = validateStatus;
|
||||
} else if (contextStatus !== undefined) {
|
||||
mergedValidateStatus = contextStatus;
|
||||
} else if (meta?.validating) {
|
||||
mergedValidateStatus = 'validating';
|
||||
} else if (debounceErrors.length) {
|
||||
@ -234,9 +239,11 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
mergedValidateStatus = 'success';
|
||||
}
|
||||
|
||||
const mergedHasFeedback = hasFeedback || contextHasFeedback;
|
||||
|
||||
const formItemStatusContext = useMemo<FormItemStatusContextProps>(() => {
|
||||
let feedbackIcon: ReactNode;
|
||||
if (hasFeedback) {
|
||||
if (mergedHasFeedback) {
|
||||
const IconNode = mergedValidateStatus && iconMap[mergedValidateStatus];
|
||||
feedbackIcon = IconNode ? (
|
||||
<span
|
||||
@ -252,11 +259,19 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
|
||||
return {
|
||||
status: mergedValidateStatus,
|
||||
hasFeedback,
|
||||
hasFeedback: mergedHasFeedback,
|
||||
feedbackIcon,
|
||||
isFormItemInput: true,
|
||||
};
|
||||
}, [mergedValidateStatus, hasFeedback]);
|
||||
}, [mergedValidateStatus, mergedHasFeedback]);
|
||||
|
||||
const noOverrideFormItemContext = useMemo<FormItemStatusContextProps>(
|
||||
() => ({
|
||||
...formItemStatusContext,
|
||||
isFormItemInput: false,
|
||||
}),
|
||||
[formItemStatusContext],
|
||||
);
|
||||
|
||||
// ======================== Render ========================
|
||||
function renderLayout(
|
||||
@ -265,7 +280,11 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
isRequired?: boolean,
|
||||
): React.ReactNode {
|
||||
if (noStyle && !hidden) {
|
||||
return baseChildren;
|
||||
return (
|
||||
<FormItemInputContext.Provider value={noOverrideFormItemContext}>
|
||||
{baseChildren}
|
||||
</FormItemInputContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const itemClassName = {
|
||||
@ -275,7 +294,7 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
[`${className}`]: !!className,
|
||||
|
||||
// Status
|
||||
[`${prefixCls}-item-has-feedback`]: mergedValidateStatus && hasFeedback,
|
||||
[`${prefixCls}-item-has-feedback`]: mergedValidateStatus && mergedHasFeedback,
|
||||
[`${prefixCls}-item-has-success`]: mergedValidateStatus === 'success',
|
||||
[`${prefixCls}-item-has-warning`]: mergedValidateStatus === 'warning',
|
||||
[`${prefixCls}-item-has-error`]: mergedValidateStatus === 'error',
|
||||
|
@ -8,6 +8,8 @@ import { FormItemPrefixContext } from './context';
|
||||
export interface FormListFieldData {
|
||||
name: number;
|
||||
key: number;
|
||||
/** @deprecated No need anymore Use key instead */
|
||||
fieldKey?: number;
|
||||
}
|
||||
|
||||
export interface FormListOperation {
|
||||
|
@ -6836,7 +6836,7 @@ exports[`renders ./components/form/demo/normal-login.md extend context correctly
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-in-form-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-checkbox-checked"
|
||||
@ -18031,7 +18031,7 @@ exports[`renders ./components/form/demo/validate-other.md extend context correct
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number ant-input-number-in-form-item"
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
|
@ -4451,7 +4451,7 @@ exports[`renders ./components/form/demo/normal-login.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-in-form-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-checkbox-checked"
|
||||
@ -7257,7 +7257,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number ant-input-number-in-form-item"
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
|
@ -1,27 +1,29 @@
|
||||
import React, { Component, useState } from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import React, { Component, useState } from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import Form from '..';
|
||||
import * as Util from '../util';
|
||||
|
||||
import Input from '../../input';
|
||||
import Button from '../../button';
|
||||
import Input from '../../input';
|
||||
import Select from '../../select';
|
||||
|
||||
import Checkbox from '../../checkbox';
|
||||
import Radio from '../../radio';
|
||||
import TreeSelect from '../../tree-select';
|
||||
import Cascader from '../../cascader';
|
||||
import Checkbox from '../../checkbox';
|
||||
import DatePicker from '../../date-picker';
|
||||
import InputNumber from '../../input-number';
|
||||
import Radio from '../../radio';
|
||||
import Switch from '../../switch';
|
||||
import TreeSelect from '../../tree-select';
|
||||
|
||||
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 ConfigProvider from '../../config-provider';
|
||||
import Drawer from '../../drawer';
|
||||
import zhCN from '../../locale/zh_CN';
|
||||
import Modal from '../../modal';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
const { TextArea } = Input;
|
||||
@ -1204,4 +1206,64 @@ describe('Form', () => {
|
||||
render(<Demo />);
|
||||
expect(subFormInstance).toBe(formInstance);
|
||||
});
|
||||
|
||||
it('noStyle should not be affected by parent', () => {
|
||||
const Demo = () => (
|
||||
<Form>
|
||||
<Form.Item>
|
||||
<Form.Item noStyle>
|
||||
<Select className="custom-select" />
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
const { container } = render(<Demo />);
|
||||
expect(container.querySelector('.custom-select')?.className).not.toContain('in-form-item');
|
||||
});
|
||||
|
||||
it('noStyle should not affect status', () => {
|
||||
const Demo = () => (
|
||||
<Form>
|
||||
<Form.Item validateStatus="error" noStyle>
|
||||
<Select className="custom-select" />
|
||||
</Form.Item>
|
||||
<Form.Item validateStatus="error">
|
||||
<Form.Item noStyle>
|
||||
<Select className="custom-select-b" />
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
<Form.Item validateStatus="error">
|
||||
<Form.Item noStyle validateStatus="warning">
|
||||
<Select className="custom-select-c" />
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
const { container } = render(<Demo />);
|
||||
expect(container.querySelector('.custom-select')?.className).toContain('status-error');
|
||||
expect(container.querySelector('.custom-select-b')?.className).toContain('status-error');
|
||||
expect(container.querySelector('.custom-select-c')?.className).toContain('status-warning');
|
||||
});
|
||||
|
||||
it('should not affect Popup children style', () => {
|
||||
const Demo = () => (
|
||||
<Form>
|
||||
<Form.Item labelCol={4} validateStatus="error">
|
||||
<Modal visible>
|
||||
<Select className="modal-select" />
|
||||
</Modal>
|
||||
</Form.Item>
|
||||
<Form.Item validateStatus="error">
|
||||
<Drawer visible>
|
||||
<Select className="drawer-select" />
|
||||
</Drawer>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
const { container } = render(<Demo />, { container: document.body });
|
||||
expect(container.querySelector('.modal-select')?.className).not.toContain('in-form-item');
|
||||
expect(container.querySelector('.modal-select')?.className).not.toContain('status-error');
|
||||
expect(container.querySelector('.drawer-select')?.className).not.toContain('in-form-item');
|
||||
expect(container.querySelector('.drawer-select')?.className).not.toContain('status-error');
|
||||
});
|
||||
});
|
||||
|
@ -1,14 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import type { Meta } from 'rc-field-form/lib/interface';
|
||||
import { FormProvider as RcFormProvider } from 'rc-field-form';
|
||||
import type { FormProviderProps as RcFormProviderProps } from 'rc-field-form/lib/FormContext';
|
||||
import type { Meta } from 'rc-field-form/lib/interface';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import type { FC, PropsWithChildren, ReactNode } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import type { ColProps } from '../grid/col';
|
||||
import type { FormLabelAlign } from './interface';
|
||||
import type { FormInstance, RequiredMark } from './Form';
|
||||
import type { ValidateStatus } from './FormItem';
|
||||
import type { FormLabelAlign } from './interface';
|
||||
|
||||
/** Form Context. Set top form style and pass to Form Item usage. */
|
||||
export interface FormContextProps {
|
||||
@ -63,10 +63,30 @@ export interface FormItemStatusContextProps {
|
||||
|
||||
export const FormItemInputContext = React.createContext<FormItemStatusContextProps>({});
|
||||
|
||||
export const NoFormStatus: FC<PropsWithChildren<{}>> = ({ children }: PropsWithChildren<{}>) => {
|
||||
const emptyContext = useMemo(() => ({}), []);
|
||||
export type NoFormStyleProps = PropsWithChildren<{
|
||||
status?: boolean;
|
||||
override?: boolean;
|
||||
}>;
|
||||
|
||||
export const NoFormStyle: FC<NoFormStyleProps> = ({ children, status, override }) => {
|
||||
const formItemInputContext = useContext(FormItemInputContext);
|
||||
|
||||
const newFormItemInputContext = useMemo(() => {
|
||||
const newContext = { ...formItemInputContext };
|
||||
if (override) {
|
||||
delete newContext.isFormItemInput;
|
||||
}
|
||||
if (status) {
|
||||
delete newContext.status;
|
||||
delete newContext.hasFeedback;
|
||||
delete newContext.feedbackIcon;
|
||||
}
|
||||
return newContext;
|
||||
}, [status, override, formItemInputContext]);
|
||||
|
||||
return (
|
||||
<FormItemInputContext.Provider value={emptyContext}>{children}</FormItemInputContext.Provider>
|
||||
<FormItemInputContext.Provider value={newFormItemInputContext}>
|
||||
{children}
|
||||
</FormItemInputContext.Provider>
|
||||
);
|
||||
};
|
||||
|
@ -6,14 +6,14 @@ import RcInputNumber from 'rc-input-number';
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
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, NoFormStatus } from '../form/context';
|
||||
import { FormItemInputContext, NoFormStyle } from '../form/context';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import useStyle from './style';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
|
||||
type ValueType = string | number;
|
||||
|
||||
@ -186,9 +186,17 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
element = (
|
||||
<div className={mergedGroupClassName} style={props.style}>
|
||||
<div className={mergedWrapperClassName}>
|
||||
{addonBeforeNode && <NoFormStatus>{addonBeforeNode}</NoFormStatus>}
|
||||
{addonBeforeNode && (
|
||||
<NoFormStyle status override>
|
||||
{addonBeforeNode}
|
||||
</NoFormStyle>
|
||||
)}
|
||||
{cloneElement(element, { style: null, disabled: mergedDisabled })}
|
||||
{addonAfterNode && <NoFormStatus>{addonAfterNode}</NoFormStatus>}
|
||||
{addonAfterNode && (
|
||||
<NoFormStyle status override>
|
||||
{addonAfterNode}
|
||||
</NoFormStyle>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -46,3 +46,26 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/35870
|
||||
.input-group(@input-number-prefix-cls) {
|
||||
> .@{input-number-prefix-cls}-rtl:first-child {
|
||||
border-radius: 0 @border-radius-base @border-radius-base 0;
|
||||
}
|
||||
> .@{input-number-prefix-cls}-rtl:last-child {
|
||||
border-radius: @border-radius-base 0 0 @border-radius-base;
|
||||
}
|
||||
|
||||
&-addon {
|
||||
.@{input-number-prefix-cls}-group-rtl &:first-child {
|
||||
border-right: @border-width-base @border-style-base @input-border-color;
|
||||
border-left: 0;
|
||||
border-radius: 0 @border-radius-base @border-radius-base 0;
|
||||
}
|
||||
.@{input-number-prefix-cls}-group-rtl &:last-child {
|
||||
border-right: 0;
|
||||
border-left: @border-width-base @border-style-base @input-border-color;
|
||||
border-radius: @border-radius-base 0 0 @border-radius-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import React, { forwardRef, useContext, useEffect, useRef } from 'react';
|
||||
import type { InputProps as RcInputProps, InputRef } from 'rc-input';
|
||||
import RcInput from 'rc-input';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import classNames from 'classnames';
|
||||
import type { InputProps as RcInputProps, InputRef } from 'rc-input';
|
||||
import RcInput from 'rc-input';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import React, { forwardRef, useContext, useEffect, useRef } from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import { FormItemInputContext, NoFormStyle } from '../form/context';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { FormItemInputContext, NoFormStatus } from '../form/context';
|
||||
import { hasPrefixSuffix } from './utils';
|
||||
import warning from '../_util/warning';
|
||||
import { hasPrefixSuffix } from './utils';
|
||||
|
||||
// CSSINJS
|
||||
import useStyle from './style';
|
||||
@ -230,8 +230,20 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
|
||||
onFocus={handleFocus}
|
||||
suffix={suffixNode}
|
||||
allowClear={mergedAllowClear}
|
||||
addonAfter={addonAfter && <NoFormStatus>{addonAfter}</NoFormStatus>}
|
||||
addonBefore={addonBefore && <NoFormStatus>{addonBefore}</NoFormStatus>}
|
||||
addonAfter={
|
||||
addonAfter && (
|
||||
<NoFormStyle override status>
|
||||
{addonAfter}
|
||||
</NoFormStyle>
|
||||
)
|
||||
}
|
||||
addonBefore={
|
||||
addonBefore && (
|
||||
<NoFormStyle override status>
|
||||
{addonBefore}
|
||||
</NoFormStyle>
|
||||
)
|
||||
}
|
||||
inputClassName={classNames(
|
||||
{
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
|
@ -1,11 +1,10 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import Input from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import { sleep, render, fireEvent } from '../../../tests/utils';
|
||||
import Password from '../Password';
|
||||
|
||||
describe('Input.Password', () => {
|
||||
@ -17,101 +16,94 @@ describe('Input.Password', () => {
|
||||
const ref = React.createRef();
|
||||
const onSelect = jest.fn();
|
||||
|
||||
const wrapper = mount(<Input.Password onSelect={onSelect} ref={ref} />);
|
||||
const { container } = render(<Input.Password onSelect={onSelect} ref={ref} />);
|
||||
expect(ref.current.input instanceof HTMLInputElement).toBe(true);
|
||||
wrapper.find('input').simulate('select');
|
||||
fireEvent.select(container.querySelector('input'));
|
||||
expect(onSelect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should support size', () => {
|
||||
const wrapper = mount(<Password size="large" />);
|
||||
expect(wrapper.find('.ant-input-affix-wrapper-lg')).toBeTruthy();
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment, container } = render(<Password size="large" />);
|
||||
expect(container.querySelector('.ant-input-affix-wrapper-lg')).toBeTruthy();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should change type when click', () => {
|
||||
const wrapper = mount(<Input.Password />);
|
||||
wrapper.find('input').simulate('change', { target: { value: '111' } });
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
wrapper.find('.ant-input-password-icon').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
wrapper.find('.ant-input-password-icon').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment, container } = render(<Input.Password />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: '111' } });
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon'));
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon'));
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('visibilityToggle should work', () => {
|
||||
const wrapper = mount(<Input.Password visibilityToggle={false} />);
|
||||
expect(wrapper.find('.anticon-eye').length).toBe(0);
|
||||
wrapper.setProps({ visibilityToggle: true });
|
||||
expect(wrapper.find('.anticon-eye-invisible').length).toBe(1);
|
||||
const { container, rerender } = render(<Input.Password visibilityToggle={false} />);
|
||||
expect(container.querySelectorAll('.anticon-eye').length).toBe(0);
|
||||
rerender(<Input.Password visibilityToggle />);
|
||||
expect(container.querySelectorAll('.anticon-eye-invisible').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should not toggle visibility when disabled prop is true', () => {
|
||||
const wrapper = mount(<Input.Password disabled />);
|
||||
expect(wrapper.find('.anticon-eye-invisible').length).toBe(1);
|
||||
wrapper.find('.anticon-eye-invisible').simulate('click');
|
||||
expect(wrapper.find('.anticon-eye').length).toBe(0);
|
||||
const { container } = render(<Input.Password disabled />);
|
||||
expect(container.querySelectorAll('.anticon-eye-invisible').length).toBe(1);
|
||||
fireEvent.click(container.querySelector('.anticon-eye-invisible'));
|
||||
expect(container.querySelectorAll('.anticon-eye').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should keep focus state', () => {
|
||||
const wrapper = mount(<Input.Password defaultValue="111" autoFocus />, {
|
||||
attachTo: document.body,
|
||||
const { container, unmount } = render(<Input.Password defaultValue="111" autoFocus />, {
|
||||
container: document.body,
|
||||
});
|
||||
expect(document.activeElement).toBe(wrapper.find('input').at(0).getDOMNode());
|
||||
expect(document.activeElement).toBe(container.querySelector('input'));
|
||||
document.activeElement.setSelectionRange(2, 2);
|
||||
expect(document.activeElement.selectionStart).toBe(2);
|
||||
wrapper.find('.ant-input-password-icon').at(0).simulate('mousedown');
|
||||
wrapper.find('.ant-input-password-icon').at(0).simulate('mouseup');
|
||||
wrapper.find('.ant-input-password-icon').at(0).simulate('click');
|
||||
expect(document.activeElement).toBe(wrapper.find('input').at(0).getDOMNode());
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-password-icon'));
|
||||
fireEvent.mouseUp(container.querySelector('.ant-input-password-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon'));
|
||||
expect(document.activeElement).toBe(container.querySelector('input'));
|
||||
expect(document.activeElement.selectionStart).toBe(2);
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/20541
|
||||
it('should not show value attribute in input element', async () => {
|
||||
const wrapper = mount(<Input.Password />);
|
||||
wrapper
|
||||
.find('input')
|
||||
.at('0')
|
||||
.simulate('change', { target: { value: 'value' } });
|
||||
const { container } = render(<Input.Password />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'value' } });
|
||||
await sleep();
|
||||
expect(wrapper.find('input').at('0').getDOMNode().getAttribute('value')).toBeFalsy();
|
||||
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/24526
|
||||
it('should not show value attribute in input element after blur it', async () => {
|
||||
const wrapper = mount(<Input.Password />);
|
||||
wrapper
|
||||
.find('input')
|
||||
.at('0')
|
||||
.simulate('change', { target: { value: 'value' } });
|
||||
const { container } = render(<Input.Password />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'value' } });
|
||||
await sleep();
|
||||
expect(wrapper.find('input').at('0').getDOMNode().getAttribute('value')).toBeFalsy();
|
||||
wrapper.find('input').at('0').simulate('blur');
|
||||
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
|
||||
fireEvent.blur(container.querySelector('input'));
|
||||
await sleep();
|
||||
expect(wrapper.find('input').at('0').getDOMNode().getAttribute('value')).toBeFalsy();
|
||||
wrapper.find('input').at('0').simulate('focus');
|
||||
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
|
||||
fireEvent.focus(container.querySelector('input'));
|
||||
await sleep();
|
||||
expect(wrapper.find('input').at('0').getDOMNode().getAttribute('value')).toBeFalsy();
|
||||
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/20541
|
||||
it('could be unmount without errors', () => {
|
||||
expect(() => {
|
||||
const wrapper = mount(<Input.Password />);
|
||||
wrapper
|
||||
.find('input')
|
||||
.at('0')
|
||||
.simulate('change', { target: { value: 'value' } });
|
||||
wrapper.unmount();
|
||||
const { container, unmount } = render(<Input.Password />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'value' } });
|
||||
unmount();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/pull/20544#issuecomment-569861679
|
||||
it('should not contain value attribute in input element with defaultValue', async () => {
|
||||
const wrapper = mount(<Input.Password defaultValue="value" />);
|
||||
const { container } = render(<Input.Password defaultValue="value" />);
|
||||
await sleep();
|
||||
expect(wrapper.find('input').at('0').getDOMNode().getAttribute('value')).toBeFalsy();
|
||||
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import Search from '../Search';
|
||||
import Button from '../../button';
|
||||
@ -13,29 +12,29 @@ describe('Input.Search', () => {
|
||||
rtlTest(Search);
|
||||
|
||||
it('should support custom button', () => {
|
||||
const wrapper = mount(<Search enterButton={<button type="button">ok</button>} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<Search enterButton={<button type="button">ok</button>} />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support custom Button', () => {
|
||||
const wrapper = mount(<Search enterButton={<Button>ok</Button>} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<Search enterButton={<Button>ok</Button>} />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support enterButton null', () => {
|
||||
expect(() => {
|
||||
mount(<Search enterButton={null} />);
|
||||
render(<Search enterButton={null} />);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should support ReactNode suffix without error', () => {
|
||||
const wrapper = mount(<Search suffix={<div>ok</div>} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<Search suffix={<div>ok</div>} />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should disable enter button when disabled prop is true', () => {
|
||||
const wrapper = mount(<Search placeholder="input search text" enterButton disabled />);
|
||||
expect(wrapper.find('.ant-btn-primary[disabled]')).toHaveLength(1);
|
||||
const { container } = render(<Search placeholder="input search text" enterButton disabled />);
|
||||
expect(container.querySelectorAll('.ant-btn-primary[disabled]')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should disable search icon when disabled prop is true', () => {
|
||||
@ -124,7 +123,7 @@ describe('Input.Search', () => {
|
||||
it('should trigger onSearch when click search button of native', () => {
|
||||
const onSearch = jest.fn();
|
||||
const onButtonClick = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Search
|
||||
defaultValue="search text"
|
||||
enterButton={
|
||||
@ -135,128 +134,134 @@ describe('Input.Search', () => {
|
||||
onSearch={onSearch}
|
||||
/>,
|
||||
);
|
||||
wrapper.find('button').simulate('click');
|
||||
fireEvent.click(container.querySelector('button'));
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith(
|
||||
'search text',
|
||||
expect.objectContaining({
|
||||
type: 'click',
|
||||
preventDefault: expect.any(Function),
|
||||
}),
|
||||
expect.anything(),
|
||||
// FIXME: should use following code
|
||||
// expect.objectContaining({
|
||||
// type: 'click',
|
||||
// preventDefault: expect.any(Function),
|
||||
// }),
|
||||
);
|
||||
expect(onButtonClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should trigger onSearch when press enter', () => {
|
||||
const onSearch = jest.fn();
|
||||
const wrapper = mount(<Search defaultValue="search text" onSearch={onSearch} />);
|
||||
wrapper.find('input').simulate('keydown', { key: 'Enter', keyCode: 13 });
|
||||
const { container } = render(<Search defaultValue="search text" onSearch={onSearch} />);
|
||||
fireEvent.keyDown(container.querySelector('input'), { key: 'Enter', keyCode: 13 });
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith(
|
||||
'search text',
|
||||
expect.objectContaining({
|
||||
type: 'keydown',
|
||||
preventDefault: expect.any(Function),
|
||||
}),
|
||||
expect.anything(),
|
||||
// FIXME: should use following code
|
||||
// expect.objectContaining({
|
||||
// type: 'keydown',
|
||||
// preventDefault: expect.any(Function),
|
||||
// }),
|
||||
);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/34844
|
||||
it('should not trigger onSearch when press enter using chinese inputting method', () => {
|
||||
const onSearch = jest.fn();
|
||||
const wrapper = mount(<Search defaultValue="search text" onSearch={onSearch} />);
|
||||
wrapper.find('input').simulate('compositionStart');
|
||||
wrapper.find('input').simulate('keydown', { key: 'Enter', keyCode: 13 });
|
||||
const { container } = render(<Search defaultValue="search text" onSearch={onSearch} />);
|
||||
fireEvent.compositionStart(container.querySelector('input'));
|
||||
fireEvent.keyDown(container.querySelector('input'), { key: 'Enter', keyCode: 13 });
|
||||
expect(onSearch).not.toHaveBeenCalled();
|
||||
|
||||
wrapper.find('input').simulate('compositionEnd');
|
||||
wrapper.find('input').simulate('keydown', { key: 'Enter', keyCode: 13 });
|
||||
fireEvent.compositionEnd(container.querySelector('input'));
|
||||
fireEvent.keyDown(container.querySelector('input'), { key: 'Enter', keyCode: 13 });
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith(
|
||||
'search text',
|
||||
expect.objectContaining({
|
||||
type: 'keydown',
|
||||
preventDefault: expect.any(Function),
|
||||
}),
|
||||
expect.anything(),
|
||||
// FIXME: should use following code
|
||||
// expect.objectContaining({
|
||||
// type: 'keydown',
|
||||
// preventDefault: expect.any(Function),
|
||||
// }),
|
||||
);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/14785
|
||||
it('should support addonAfter', () => {
|
||||
const addonAfter = <span>Addon After</span>;
|
||||
const wrapper = mount(<Search addonAfter={addonAfter} />);
|
||||
const wrapperWithEnterButton = mount(<Search enterButton addonAfter={addonAfter} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapperWithEnterButton.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<Search addonAfter={addonAfter} />);
|
||||
const { asFragment: asFragmentWithEnterButton } = render(
|
||||
<Search enterButton addonAfter={addonAfter} />,
|
||||
);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(asFragmentWithEnterButton().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/18729
|
||||
it('should trigger onSearch when click clear icon', () => {
|
||||
const onSearch = jest.fn();
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Search allowClear defaultValue="value" onSearch={onSearch} onChange={onChange} />,
|
||||
);
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
expect(onSearch).toHaveBeenLastCalledWith('', expect.anything());
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should support loading', () => {
|
||||
const wrapper = mount(<Search loading />);
|
||||
const wrapperWithEnterButton = mount(<Search loading enterButton />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapperWithEnterButton.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<Search loading />);
|
||||
const { asFragment: asFragmentWithEnterButton } = render(<Search loading enterButton />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(asFragmentWithEnterButton().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support addonAfter and suffix for loading', () => {
|
||||
const wrapper = mount(<Search loading suffix="suffix" addonAfter="addonAfter" />);
|
||||
const wrapperWithEnterButton = mount(
|
||||
const { asFragment } = render(<Search loading suffix="suffix" addonAfter="addonAfter" />);
|
||||
const { asFragment: asFragmentWithEnterButton } = render(
|
||||
<Search loading enterButton suffix="suffix" addonAfter="addonAfter" />,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapperWithEnterButton.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(asFragmentWithEnterButton().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support invalid suffix', () => {
|
||||
const wrapper = mount(<Search suffix={[]} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<Search suffix={[]} />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support invalid addonAfter', () => {
|
||||
const wrapper = mount(<Search addonAfter={[]} enterButton />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<Search addonAfter={[]} enterButton />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should prevent search button mousedown event', () => {
|
||||
const ref = React.createRef();
|
||||
const wrapper = mount(<Search ref={ref} enterButton="button text" />, {
|
||||
attachTo: document.body,
|
||||
const { container } = render(<Search ref={ref} enterButton="button text" />, {
|
||||
container: document.body,
|
||||
});
|
||||
let prevented = false;
|
||||
ref.current.focus();
|
||||
expect(document.activeElement).toBe(wrapper.find('input').at(0).getDOMNode());
|
||||
wrapper.find('button').simulate('mousedown', {
|
||||
preventDefault: () => {
|
||||
prevented = true;
|
||||
},
|
||||
});
|
||||
expect(prevented).toBeTruthy();
|
||||
expect(document.activeElement).toBe(wrapper.find('input').at(0).getDOMNode());
|
||||
expect(document.activeElement).toBe(container.querySelector('input'));
|
||||
fireEvent.mouseDown(container.querySelector('button'));
|
||||
expect(document.activeElement).toBe(container.querySelector('input'));
|
||||
});
|
||||
|
||||
it('not crash when use function ref', () => {
|
||||
const ref = jest.fn();
|
||||
const wrapper = mount(<Search ref={ref} enterButton />);
|
||||
const { container } = render(<Search ref={ref} enterButton />);
|
||||
expect(() => {
|
||||
wrapper.find('button').simulate('mousedown');
|
||||
fireEvent.mouseDown(container.querySelector('button'));
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/27258
|
||||
it('Search with allowClear should have one className only', () => {
|
||||
const wrapper = mount(<Search allowClear className="className" />);
|
||||
expect(wrapper.find('.ant-input-group-wrapper').hasClass('className')).toBe(true);
|
||||
expect(wrapper.find('.ant-input-affix-wrapper').hasClass('className')).toBe(false);
|
||||
const { container } = render(<Search allowClear className="className" />);
|
||||
expect(
|
||||
container.querySelector('.ant-input-group-wrapper').classList.contains('className'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
container.querySelector('.ant-input-affix-wrapper').classList.contains('className'),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
@ -43,7 +43,7 @@ exports[`Input allowClear should change type when click 1`] = `
|
||||
|
||||
exports[`Input allowClear should change type when click 2`] = `
|
||||
<span
|
||||
class="ant-input-affix-wrapper"
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-focused"
|
||||
>
|
||||
<input
|
||||
class="ant-input"
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
|
||||
import type { InputRef } from '../Input';
|
||||
import Input from '..';
|
||||
@ -33,7 +32,7 @@ describe('Input.Focus', () => {
|
||||
|
||||
it('start', () => {
|
||||
const ref = React.createRef<InputRef>();
|
||||
mount(<TextArea ref={ref} defaultValue="light" />);
|
||||
render(<TextArea ref={ref} defaultValue="light" />);
|
||||
ref.current!.focus({ cursor: 'start' });
|
||||
|
||||
expect(focus).toHaveBeenCalled();
|
||||
@ -42,7 +41,7 @@ describe('Input.Focus', () => {
|
||||
|
||||
it('end', () => {
|
||||
const ref = React.createRef<InputRef>();
|
||||
mount(<TextArea ref={ref} defaultValue="light" />);
|
||||
render(<TextArea ref={ref} defaultValue="light" />);
|
||||
ref.current!.focus({ cursor: 'end' });
|
||||
|
||||
expect(focus).toHaveBeenCalled();
|
||||
@ -51,7 +50,7 @@ describe('Input.Focus', () => {
|
||||
|
||||
it('all', () => {
|
||||
const ref = React.createRef<any>();
|
||||
mount(<TextArea ref={ref} defaultValue="light" />);
|
||||
render(<TextArea ref={ref} defaultValue="light" />);
|
||||
ref.current!.focus({ cursor: 'all' });
|
||||
|
||||
expect(focus).toHaveBeenCalled();
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { render, fireEvent } from '../../../tests/utils';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
@ -27,32 +26,32 @@ describe('Input', () => {
|
||||
rtlTest(Input.Group);
|
||||
|
||||
it('should support maxLength', () => {
|
||||
const wrapper = mount(<Input maxLength={3} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<Input maxLength={3} />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('select()', () => {
|
||||
const ref = React.createRef<InputRef>();
|
||||
mount(<Input ref={ref} />);
|
||||
render(<Input ref={ref} />);
|
||||
ref.current?.select();
|
||||
});
|
||||
|
||||
it('should support size', () => {
|
||||
const wrapper = mount(<Input size="large" />);
|
||||
expect(wrapper.find('input').hasClass('ant-input-lg')).toBe(true);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment, container } = render(<Input size="large" />);
|
||||
expect(container.querySelector('input')?.classList.contains('ant-input-lg')).toBe(true);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support size in form', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment, container } = render(
|
||||
<Form size="large">
|
||||
<Form.Item>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
expect(wrapper.find('input').hasClass('ant-input-lg')).toBe(true);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(container.querySelector('input')?.classList.contains('ant-input-lg')).toBe(true);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('focus trigger warning', () => {
|
||||
@ -65,15 +64,13 @@ describe('Input', () => {
|
||||
expect(errorSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
it('trigger warning', () => {
|
||||
const wrapper = mount(<Input />);
|
||||
wrapper.find('input').first().getDOMNode<HTMLInputElement>().focus();
|
||||
wrapper.setProps({
|
||||
suffix: 'light',
|
||||
});
|
||||
const { container, rerender, unmount } = render(<Input />);
|
||||
container.querySelector('input')?.focus();
|
||||
rerender(<Input suffix="light" />);
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Input] When Input is focused, dynamic add or remove prefix / suffix will make it lose focus caused by dom structure change. Read more: https://ant.design/components/input/#FAQ',
|
||||
);
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
||||
@ -115,10 +112,10 @@ describe('Input', () => {
|
||||
const defaultValue = '11111';
|
||||
const valLength = defaultValue.length;
|
||||
const ref = React.createRef<InputRef>();
|
||||
const wrapper = mount(<Input ref={ref} autoFocus defaultValue={defaultValue} />);
|
||||
const { container } = render(<Input ref={ref} autoFocus defaultValue={defaultValue} />);
|
||||
ref.current?.setSelectionRange(valLength, valLength);
|
||||
expect(wrapper.find('input').first().getDOMNode<HTMLInputElement>().selectionStart).toEqual(5);
|
||||
expect(wrapper.find('input').first().getDOMNode<HTMLInputElement>().selectionEnd).toEqual(5);
|
||||
expect(container.querySelector('input')?.selectionStart).toEqual(5);
|
||||
expect(container.querySelector('input')?.selectionEnd).toEqual(5);
|
||||
});
|
||||
});
|
||||
|
||||
@ -230,133 +227,134 @@ describe('As Form Control', () => {
|
||||
);
|
||||
};
|
||||
|
||||
const wrapper = mount(<Demo />);
|
||||
wrapper.find('input').simulate('change', { target: { value: '111' } });
|
||||
wrapper.find('textarea').simulate('change', { target: { value: '222' } });
|
||||
expect(wrapper.find('input').prop('value')).toBe('111');
|
||||
expect(wrapper.find('textarea').prop('value')).toBe('222');
|
||||
wrapper.find('button').simulate('click');
|
||||
expect(wrapper.find('input').prop('value')).toBe('');
|
||||
expect(wrapper.find('textarea').prop('value')).toBe('');
|
||||
const { container } = render(<Demo />);
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: '111' } });
|
||||
fireEvent.change(container.querySelector('textarea')!, { target: { value: '222' } });
|
||||
expect(container.querySelector('input')?.value).toBe('111');
|
||||
expect(container.querySelector('textarea')?.value).toBe('222');
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
expect(container.querySelector('input')?.value).toBe('');
|
||||
expect(container.querySelector('textarea')?.value).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('should support showCount', () => {
|
||||
it('maxLength', () => {
|
||||
const wrapper = mount(<Input maxLength={5} showCount value="12345" />);
|
||||
expect(wrapper.find('input').prop('value')).toBe('12345');
|
||||
expect(wrapper.find('.ant-input-show-count-suffix').getDOMNode().innerHTML).toBe('5 / 5');
|
||||
const { container } = render(<Input maxLength={5} showCount value="12345" />);
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBe('12345');
|
||||
expect(container.querySelector('.ant-input-show-count-suffix')?.innerHTML).toBe('5 / 5');
|
||||
});
|
||||
|
||||
it('control exceed maxLength', () => {
|
||||
const wrapper = mount(<Input maxLength={5} showCount value="12345678" />);
|
||||
expect(wrapper.find('input').prop('value')).toBe('12345678');
|
||||
expect(wrapper.find('.ant-input-show-count-suffix').getDOMNode().innerHTML).toBe('8 / 5');
|
||||
const { container } = render(<Input maxLength={5} showCount value="12345678" />);
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBe('12345678');
|
||||
expect(container.querySelector('.ant-input-show-count-suffix')?.innerHTML).toBe('8 / 5');
|
||||
});
|
||||
|
||||
describe('emoji', () => {
|
||||
it('should minimize value between emoji length and maxLength', () => {
|
||||
const wrapper = mount(<Input maxLength={1} showCount value="👀" />);
|
||||
expect(wrapper.find('input').prop('value')).toBe('👀');
|
||||
expect(wrapper.find('.ant-input-show-count-suffix').getDOMNode().innerHTML).toBe('1 / 1');
|
||||
const { container } = render(<Input maxLength={1} showCount value="👀" />);
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBe('👀');
|
||||
expect(container.querySelector('.ant-input-show-count-suffix')?.innerHTML).toBe('1 / 1');
|
||||
|
||||
const wrapper1 = mount(<Input maxLength={2} showCount value="👀" />);
|
||||
expect(wrapper1.find('.ant-input-show-count-suffix').getDOMNode().innerHTML).toBe('1 / 2');
|
||||
const { container: container1 } = render(<Input maxLength={2} showCount value="👀" />);
|
||||
expect(container1.querySelector('.ant-input-show-count-suffix')?.innerHTML).toBe('1 / 2');
|
||||
});
|
||||
|
||||
it('slice emoji', () => {
|
||||
const wrapper = mount(<Input maxLength={5} showCount value="1234😂" />);
|
||||
expect(wrapper.find('input').prop('value')).toBe('1234😂');
|
||||
expect(wrapper.find('.ant-input-show-count-suffix').getDOMNode().innerHTML).toBe('5 / 5');
|
||||
const { container } = render(<Input maxLength={5} showCount value="1234😂" />);
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBe('1234😂');
|
||||
expect(container.querySelector('.ant-input-show-count-suffix')?.innerHTML).toBe('5 / 5');
|
||||
});
|
||||
});
|
||||
|
||||
it('count formatter', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Input
|
||||
maxLength={5}
|
||||
showCount={{ formatter: ({ count, maxLength }) => `${count}, ${maxLength}` }}
|
||||
value="12345"
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find('input').prop('value')).toBe('12345');
|
||||
expect(wrapper.find('.ant-input-show-count-suffix').getDOMNode().innerHTML).toBe('5, 5');
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBe('12345');
|
||||
expect(container.querySelector('.ant-input-show-count-suffix')?.innerHTML).toBe('5, 5');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Input allowClear', () => {
|
||||
it('should change type when click', () => {
|
||||
const wrapper = mount(<Input allowClear />);
|
||||
wrapper.find('input').simulate('change', { target: { value: '111' } });
|
||||
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('111');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('');
|
||||
const { asFragment, container } = render(<Input allowClear />);
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: '111' } });
|
||||
expect(container.querySelector('input')?.value).toEqual('111');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(container.querySelector('input')?.value).toEqual('');
|
||||
});
|
||||
|
||||
it('should not show icon if value is undefined, null or empty string', () => {
|
||||
// @ts-ignore
|
||||
const wrappers = [null, undefined, ''].map(val => mount(<Input allowClear value={val} />));
|
||||
wrappers.forEach(wrapper => {
|
||||
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('');
|
||||
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const wrappers = [null, undefined, ''].map(val => render(<Input allowClear value={val} />));
|
||||
wrappers.forEach(({ asFragment, container }) => {
|
||||
expect(container.querySelector('input')?.value).toEqual('');
|
||||
expect(container.querySelector('.ant-input-clear-icon-hidden')).toBeTruthy();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not show icon if defaultValue is undefined, null or empty string', () => {
|
||||
const wrappers = [null, undefined, ''].map(val =>
|
||||
// @ts-ignore
|
||||
mount(<Input allowClear defaultValue={val} />),
|
||||
render(<Input allowClear defaultValue={val} />),
|
||||
);
|
||||
wrappers.forEach(wrapper => {
|
||||
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('');
|
||||
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
wrappers.forEach(({ asFragment, container }) => {
|
||||
expect(container.querySelector('input')?.value).toEqual('');
|
||||
expect(container.querySelector('.ant-input-clear-icon-hidden')).toBeTruthy();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('should trigger event correctly', () => {
|
||||
let argumentEventObject: React.ChangeEvent<HTMLInputElement> | undefined;
|
||||
|
||||
let argumentEventObjectType;
|
||||
let argumentEventObjectValue;
|
||||
const onChange: InputProps['onChange'] = e => {
|
||||
argumentEventObject = e;
|
||||
argumentEventObjectType = e.type;
|
||||
argumentEventObjectValue = e.target.value;
|
||||
};
|
||||
const wrapper = mount(<Input allowClear defaultValue="111" onChange={onChange} />);
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(argumentEventObject?.type).toBe('click');
|
||||
const { container } = render(<Input allowClear defaultValue="111" onChange={onChange} />);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(argumentEventObjectType).toBe('click');
|
||||
expect(argumentEventObjectValue).toBe('');
|
||||
expect(wrapper.find('input').at(0).getDOMNode<HTMLInputElement>().value).toBe('');
|
||||
expect(container.querySelector('input')?.value).toBe('');
|
||||
});
|
||||
|
||||
it('should trigger event correctly on controlled mode', () => {
|
||||
let argumentEventObject: React.ChangeEvent<HTMLInputElement> | undefined;
|
||||
let argumentEventObjectType;
|
||||
let argumentEventObjectValue;
|
||||
const onChange: InputProps['onChange'] = e => {
|
||||
argumentEventObject = e;
|
||||
argumentEventObjectType = e.type;
|
||||
argumentEventObjectValue = e.target.value;
|
||||
};
|
||||
const wrapper = mount(<Input allowClear value="111" onChange={onChange} />);
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(argumentEventObject?.type).toBe('click');
|
||||
const { container } = render(<Input allowClear value="111" onChange={onChange} />);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(argumentEventObjectType).toBe('click');
|
||||
expect(argumentEventObjectValue).toBe('');
|
||||
expect(wrapper.find('input').at(0).getDOMNode<HTMLInputElement>().value).toBe('111');
|
||||
expect(container.querySelector('input')?.value).toBe('111');
|
||||
});
|
||||
|
||||
it('should focus input after clear', () => {
|
||||
const wrapper = mount(<Input allowClear defaultValue="111" />, { attachTo: document.body });
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(document.activeElement).toBe(wrapper.find('input').at(0).getDOMNode());
|
||||
wrapper.unmount();
|
||||
const { container, unmount } = render(<Input allowClear defaultValue="111" />, {
|
||||
container: document.body,
|
||||
});
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(document.activeElement).toBe(container.querySelector('input'));
|
||||
unmount();
|
||||
});
|
||||
|
||||
['disabled', 'readOnly'].forEach(prop => {
|
||||
it(`should not support allowClear when it is ${prop}`, () => {
|
||||
const wrapper = mount(<Input allowClear defaultValue="111" {...{ [prop]: true }} />);
|
||||
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
|
||||
const { container } = render(<Input allowClear defaultValue="111" {...{ [prop]: true }} />);
|
||||
expect(container.querySelector('.ant-input-clear-icon-hidden')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@ -370,17 +368,20 @@ describe('Input allowClear', () => {
|
||||
// https://github.com/ant-design/ant-design/issues/31200
|
||||
it('should not lost focus when clear input', () => {
|
||||
const onBlur = jest.fn();
|
||||
const wrapper = mount(<Input allowClear defaultValue="value" onBlur={onBlur} />, {
|
||||
attachTo: document.body,
|
||||
});
|
||||
wrapper.find('input').getDOMNode<HTMLInputElement>().focus();
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('mouseDown');
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('mouseUp');
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('focus');
|
||||
wrapper.find('.ant-input-clear-icon').at(0).getDOMNode<HTMLInputElement>().click();
|
||||
const { container, unmount } = render(
|
||||
<Input allowClear defaultValue="value" onBlur={onBlur} />,
|
||||
{
|
||||
container: document.body,
|
||||
},
|
||||
);
|
||||
container.querySelector('input')?.focus();
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-clear-icon')!);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
fireEvent.mouseUp(container.querySelector('.ant-input-clear-icon')!);
|
||||
fireEvent.focus(container.querySelector('.ant-input-clear-icon')!);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(onBlur).not.toBeCalled();
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/31927
|
||||
@ -398,34 +399,35 @@ describe('Input allowClear', () => {
|
||||
);
|
||||
};
|
||||
|
||||
const wrapper = mount(<App />);
|
||||
const { container, unmount } = render(<App />);
|
||||
|
||||
wrapper.find('input').getDOMNode<HTMLInputElement>().focus();
|
||||
wrapper.find('input').simulate('change', { target: { value: '111' } });
|
||||
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('111');
|
||||
container.querySelector('input')?.focus();
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: '111' } });
|
||||
expect(container.querySelector('input')?.value).toEqual('111');
|
||||
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('');
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(container.querySelector('input')?.value).toEqual('');
|
||||
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('not crash when value is number', () => {
|
||||
const wrapper = mount(<Input suffix="Bamboo" value={1} />);
|
||||
expect(wrapper).toBeTruthy();
|
||||
const { container } = render(<Input suffix="Bamboo" value={1} />);
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display boolean value as string', () => {
|
||||
// @ts-ignore
|
||||
const wrapper = mount(<Input value />);
|
||||
expect(wrapper.find('input').first().getDOMNode<HTMLInputElement>().value).toBe('true');
|
||||
wrapper.setProps({ value: false });
|
||||
expect(wrapper.find('input').first().getDOMNode<HTMLInputElement>().value).toBe('false');
|
||||
const { container, rerender } = render(<Input value />);
|
||||
expect(container.querySelector('input')?.value).toBe('true');
|
||||
// @ts-ignore
|
||||
rerender(<Input value={false} />);
|
||||
expect(container.querySelector('input')?.value).toBe('false');
|
||||
});
|
||||
|
||||
it('should support custom clearIcon', () => {
|
||||
const wrapper = mount(<Input allowClear={{ clearIcon: 'clear' }} />);
|
||||
expect(wrapper.find('.ant-input-clear-icon').text()).toBe('clear');
|
||||
const { container } = render(<Input allowClear={{ clearIcon: 'clear' }} />);
|
||||
expect(container.querySelector('.ant-input-clear-icon')?.textContent).toBe('clear');
|
||||
});
|
||||
});
|
||||
|
||||
@ -438,9 +440,9 @@ describe('typescript types ', () => {
|
||||
'data-testid': 'test-id',
|
||||
'data-id': '12345',
|
||||
};
|
||||
const wrapper = mount(<Input {...props} />);
|
||||
const input = wrapper.find('input').first().getDOMNode();
|
||||
expect(input.getAttribute('data-testid')).toBe('test-id');
|
||||
expect(input.getAttribute('data-id')).toBe('12345');
|
||||
const { container } = render(<Input {...props} />);
|
||||
const input = container.querySelector('input');
|
||||
expect(input?.getAttribute('data-testid')).toBe('test-id');
|
||||
expect(input?.getAttribute('data-id')).toBe('12345');
|
||||
});
|
||||
});
|
||||
|
@ -1,9 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import RcTextArea from 'rc-textarea';
|
||||
import Input from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import { sleep, render } from '../../../tests/utils';
|
||||
import { sleep, render, fireEvent, triggerResize } from '../../../tests/utils';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
@ -69,34 +67,34 @@ describe('TextArea', () => {
|
||||
it('should support onPressEnter and onKeyDown', () => {
|
||||
const fakeHandleKeyDown = jest.fn();
|
||||
const fakeHandlePressEnter = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<TextArea onKeyDown={fakeHandleKeyDown} onPressEnter={fakeHandlePressEnter} />,
|
||||
);
|
||||
/** KeyCode 65 is A */
|
||||
wrapper.find('textarea').simulate('keydown', { keyCode: 65 });
|
||||
fireEvent.keyDown(container.querySelector('textarea'), { keyCode: 65 });
|
||||
expect(fakeHandleKeyDown).toHaveBeenCalledTimes(1);
|
||||
expect(fakeHandlePressEnter).toHaveBeenCalledTimes(0);
|
||||
|
||||
/** KeyCode 13 is Enter */
|
||||
wrapper.find('textarea').simulate('keydown', { keyCode: 13 });
|
||||
fireEvent.keyDown(container.querySelector('textarea'), { keyCode: 13 });
|
||||
expect(fakeHandleKeyDown).toHaveBeenCalledTimes(2);
|
||||
expect(fakeHandlePressEnter).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should support disabled', () => {
|
||||
const wrapper = mount(<TextArea disabled />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<TextArea disabled />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('maxLength', () => {
|
||||
it('should support maxLength', () => {
|
||||
const wrapper = mount(<TextArea maxLength={10} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<TextArea maxLength={10} />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('maxLength should not block control', () => {
|
||||
const wrapper = mount(<TextArea maxLength={1} value="light" />);
|
||||
expect(wrapper.find('textarea').props().value).toEqual('light');
|
||||
const { container } = render(<TextArea maxLength={1} value="light" />);
|
||||
expect(container.querySelector('textarea').value).toEqual('light');
|
||||
});
|
||||
|
||||
it('should limit correctly when in control', () => {
|
||||
@ -105,20 +103,22 @@ describe('TextArea', () => {
|
||||
return <TextArea maxLength={1} value={val} onChange={e => setVal(e.target.value)} />;
|
||||
};
|
||||
|
||||
const wrapper = mount(<Demo />);
|
||||
wrapper.find('textarea').simulate('change', { target: { value: 'light' } });
|
||||
const { container } = render(<Demo />);
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'light' } });
|
||||
|
||||
expect(wrapper.find('textarea').props().value).toEqual('l');
|
||||
expect(container.querySelector('textarea').value).toEqual('l');
|
||||
});
|
||||
|
||||
it('should exceed maxLength when use IME', () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mount(<TextArea maxLength={1} onChange={onChange} />);
|
||||
wrapper.find('textarea').simulate('compositionStart');
|
||||
wrapper.find('textarea').simulate('change', { target: { value: 'zhu' } });
|
||||
wrapper.find('textarea').simulate('compositionEnd', { currentTarget: { value: '竹' } });
|
||||
wrapper.find('textarea').simulate('change', { target: { value: '竹' } });
|
||||
const { container } = render(<TextArea maxLength={1} onChange={onChange} />);
|
||||
fireEvent.compositionStart(container.querySelector('textarea'));
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'zhu' } });
|
||||
fireEvent.compositionEnd(container.querySelector('textarea'), {
|
||||
currentTarget: { value: '竹' },
|
||||
});
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: '竹' } });
|
||||
|
||||
expect(onChange).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ target: expect.objectContaining({ value: '竹' }) }),
|
||||
@ -128,90 +128,93 @@ describe('TextArea', () => {
|
||||
// 字符输入
|
||||
it('should not cut off string when cursor position is not at the end', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(<TextArea maxLength={6} defaultValue="123456" onChange={onChange} />);
|
||||
wrapper
|
||||
.find('textarea')
|
||||
.simulate('change', { target: { selectionStart: 1, value: 'w123456' } });
|
||||
wrapper
|
||||
.find('textarea')
|
||||
.simulate('change', { target: { selectionStart: 3, value: '123w456' } });
|
||||
expect(wrapper.find('textarea').at(0).getDOMNode().value).toBe('123456');
|
||||
const { container } = render(
|
||||
<TextArea maxLength={6} defaultValue="123456" onChange={onChange} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
target: { selectionStart: 1, value: 'w123456' },
|
||||
});
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
target: { selectionStart: 3, value: 'w123456' },
|
||||
});
|
||||
expect(container.querySelector('textarea').value).toBe('123456');
|
||||
});
|
||||
|
||||
// 拼音输入
|
||||
// 1. 光标位于最后,且当前字符数未达到6个,若选中的字符 + 原字符的长度超过6个,则将最终的字符按照maxlength截断
|
||||
it('when the input method is pinyin and the cursor is at the end, should use maxLength to crop', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(<TextArea maxLength={6} defaultValue="1234" onChange={onChange} />);
|
||||
wrapper.find('textarea').instance().value = '1234'; // enzyme not support change `currentTarget`
|
||||
wrapper.find('textarea').instance().selectionStart = 4;
|
||||
wrapper.find('textarea').simulate('compositionStart');
|
||||
const { container } = render(
|
||||
<TextArea maxLength={6} defaultValue="1234" onChange={onChange} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
target: { selectionStart: 4, value: '1234' },
|
||||
});
|
||||
fireEvent.compositionStart(container.querySelector('textarea'));
|
||||
|
||||
wrapper
|
||||
.find('textarea')
|
||||
.simulate('change', { target: { selectionStart: 9, value: '1234z z z' } });
|
||||
wrapper
|
||||
.find('textarea')
|
||||
.simulate('change', { target: { selectionStart: 7, value: '1234组织者' } });
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
target: { selectionStart: 9, value: '1234z z z' },
|
||||
});
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
target: { selectionStart: 7, value: '1234组织者' },
|
||||
});
|
||||
|
||||
wrapper.find('textarea').instance().value = '1234组织者';
|
||||
wrapper.find('textarea').instance().selectionStart = 7;
|
||||
wrapper.find('textarea').simulate('compositionEnd');
|
||||
fireEvent.compositionEnd(container.querySelector('textarea'));
|
||||
|
||||
expect(wrapper.find('textarea').at(0).getDOMNode().value).toBe('1234组织');
|
||||
expect(container.querySelector('textarea').value).toBe('1234组织');
|
||||
});
|
||||
|
||||
// 2. 光标位于中间或开头,且当前字符数未达到6个,若选中的字符 + 原字符的长度超过6个,则显示原有字符
|
||||
it('when the input method is Pinyin and the cursor is in the middle, should display the original string', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(<TextArea maxLength={6} defaultValue="1234" onChange={onChange} />);
|
||||
wrapper.find('textarea').instance().value = '1234'; // enzyme not support change `currentTarget`
|
||||
wrapper.find('textarea').instance().selectionStart = 2;
|
||||
wrapper.find('textarea').simulate('compositionStart');
|
||||
const { container } = render(
|
||||
<TextArea maxLength={6} defaultValue="1234" onChange={onChange} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
target: { selectionStart: 2, value: '1234' },
|
||||
});
|
||||
fireEvent.compositionStart(container.querySelector('textarea'));
|
||||
|
||||
wrapper
|
||||
.find('textarea')
|
||||
.simulate('change', { target: { selectionStart: 2, value: '12z z z34' } });
|
||||
wrapper
|
||||
.find('textarea')
|
||||
.simulate('change', { target: { selectionStart: 5, value: '12组织者34' } });
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
target: { selectionStart: 2, value: '12z z z34' },
|
||||
});
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
target: { selectionStart: 5, value: '12组织者34' },
|
||||
});
|
||||
|
||||
wrapper.find('textarea').instance().value = '12组织者34';
|
||||
wrapper.find('textarea').instance().selectionStart = 5;
|
||||
wrapper.find('textarea').simulate('compositionEnd');
|
||||
fireEvent.compositionEnd(container.querySelector('textarea'));
|
||||
|
||||
expect(wrapper.find('textarea').at(0).getDOMNode().value).toBe('1234');
|
||||
expect(container.querySelector('textarea').value).toBe('1234');
|
||||
});
|
||||
});
|
||||
|
||||
it('when prop value not in this.props, resizeTextarea should be called', async () => {
|
||||
const ref = React.createRef();
|
||||
const wrapper = mount(<TextArea aria-label="textarea" ref={ref} />);
|
||||
const { container } = render(<TextArea aria-label="textarea" ref={ref} />);
|
||||
const resizeTextarea = jest.spyOn(ref.current.resizableTextArea, 'resizeTextarea');
|
||||
wrapper.find('textarea').simulate('change', {
|
||||
target: {
|
||||
value: 'test',
|
||||
},
|
||||
});
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'test' } });
|
||||
expect(resizeTextarea).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handleKeyDown', () => {
|
||||
const onPressEnter = jest.fn();
|
||||
const onKeyDown = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<TextArea onPressEnter={onPressEnter} onKeyDown={onKeyDown} aria-label="textarea" />,
|
||||
);
|
||||
wrapper.find(RcTextArea).instance().handleKeyDown({ keyCode: 13 });
|
||||
fireEvent.keyDown(container.querySelector('textarea'), { keyCode: 13 });
|
||||
|
||||
expect(onPressEnter).toHaveBeenCalled();
|
||||
expect(onKeyDown).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should trigger onResize', async () => {
|
||||
const onResize = jest.fn();
|
||||
const wrapper = mount(<TextArea onResize={onResize} autoSize />);
|
||||
const ref = React.createRef();
|
||||
render(<TextArea ref={ref} onResize={onResize} autoSize />);
|
||||
await sleep(100);
|
||||
wrapper.triggerResize();
|
||||
const target = ref.current.resizableTextArea.textArea;
|
||||
triggerResize(target);
|
||||
await Promise.resolve();
|
||||
|
||||
expect(onResize).toHaveBeenCalledWith(
|
||||
@ -236,86 +239,92 @@ describe('TextArea', () => {
|
||||
|
||||
describe('should support showCount', () => {
|
||||
it('maxLength', () => {
|
||||
const wrapper = mount(<TextArea maxLength={5} showCount value="12345" />);
|
||||
const textarea = wrapper.find('.ant-input-textarea');
|
||||
expect(wrapper.find('textarea').prop('value')).toBe('12345');
|
||||
expect(textarea.prop('data-count')).toBe('5 / 5');
|
||||
const { container } = render(<TextArea maxLength={5} showCount value="12345" />);
|
||||
expect(container.querySelector('textarea').value).toBe('12345');
|
||||
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
'5 / 5',
|
||||
);
|
||||
});
|
||||
|
||||
it('control exceed maxLength', () => {
|
||||
const wrapper = mount(<TextArea maxLength={5} showCount value="12345678" />);
|
||||
const textarea = wrapper.find('.ant-input-textarea');
|
||||
expect(wrapper.find('textarea').prop('value')).toBe('12345678');
|
||||
expect(textarea.prop('data-count')).toBe('8 / 5');
|
||||
const { container } = render(<TextArea maxLength={5} showCount value="12345678" />);
|
||||
expect(container.querySelector('textarea').value).toBe('12345678');
|
||||
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
'8 / 5',
|
||||
);
|
||||
});
|
||||
|
||||
describe('emoji', () => {
|
||||
it('should minimize value between emoji length and maxLength', () => {
|
||||
const wrapper = mount(<TextArea maxLength={1} showCount value="👀" />);
|
||||
const textarea = wrapper.find('.ant-input-textarea');
|
||||
expect(wrapper.find('textarea').prop('value')).toBe('👀');
|
||||
expect(textarea.prop('data-count')).toBe('1 / 1');
|
||||
const { container } = render(<TextArea maxLength={1} showCount value="👀" />);
|
||||
expect(container.querySelector('textarea').value).toBe('👀');
|
||||
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
'1 / 1',
|
||||
);
|
||||
|
||||
// fix: 当 maxLength 长度为 2 的时候,输入 emoji 之后 showCount 会显示 1/2,但是不能再输入了
|
||||
// zombieJ: 逻辑统一了,emoji 现在也可以正确计数了
|
||||
const wrapper1 = mount(<TextArea maxLength={2} showCount value="👀" />);
|
||||
const textarea1 = wrapper1.find('.ant-input-textarea');
|
||||
expect(textarea1.prop('data-count')).toBe('1 / 2');
|
||||
const { container: container1 } = render(<TextArea maxLength={2} showCount value="👀" />);
|
||||
expect(container1.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
'1 / 2',
|
||||
);
|
||||
});
|
||||
|
||||
it('defaultValue should slice', () => {
|
||||
const wrapper = mount(<TextArea maxLength={1} defaultValue="🧐cut" />);
|
||||
expect(wrapper.find('textarea').prop('value')).toBe('🧐');
|
||||
const { container } = render(<TextArea maxLength={1} defaultValue="🧐cut" />);
|
||||
expect(container.querySelector('textarea').value).toBe('🧐');
|
||||
});
|
||||
|
||||
// 修改TextArea value截取规则后新增单测
|
||||
it('slice emoji', () => {
|
||||
const wrapper = mount(<TextArea maxLength={5} showCount value="1234😂" />);
|
||||
const textarea = wrapper.find('.ant-input-textarea');
|
||||
expect(wrapper.find('textarea').prop('value')).toBe('1234😂');
|
||||
expect(textarea.prop('data-count')).toBe('5 / 5');
|
||||
const { container } = render(<TextArea maxLength={5} showCount value="1234😂" />);
|
||||
expect(container.querySelector('textarea').value).toBe('1234😂');
|
||||
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
'5 / 5',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('className & style patch to outer', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<TextArea className="bamboo" style={{ background: 'red' }} showCount />,
|
||||
);
|
||||
|
||||
// Outer
|
||||
expect(wrapper.find('div').first().hasClass('bamboo')).toBeTruthy();
|
||||
expect(wrapper.find('div').first().props().style.background).toEqual('red');
|
||||
expect(container.querySelector('div').classList.contains('bamboo')).toBeTruthy();
|
||||
expect(container.querySelector('div').style.background).toEqual('red');
|
||||
|
||||
// Inner
|
||||
expect(wrapper.find('.ant-input').hasClass('bamboo')).toBeFalsy();
|
||||
expect(wrapper.find('.ant-input').props().style.background).toBeFalsy();
|
||||
expect(container.querySelector('.ant-input').classList.contains('bamboo')).toBeFalsy();
|
||||
expect(container.querySelector('.ant-input').style.background).toBeFalsy();
|
||||
});
|
||||
|
||||
it('count formatter', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<TextArea
|
||||
maxLength={5}
|
||||
showCount={{ formatter: ({ count, maxLength }) => `${count}, ${maxLength}` }}
|
||||
value="12345"
|
||||
/>,
|
||||
);
|
||||
const textarea = wrapper.find('.ant-input-textarea');
|
||||
expect(wrapper.find('textarea').prop('value')).toBe('12345');
|
||||
expect(textarea.prop('data-count')).toBe('5, 5');
|
||||
expect(container.querySelector('textarea').value).toBe('12345');
|
||||
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
'5, 5',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should support size', async () => {
|
||||
const wrapper = mount(<TextArea size="large" />);
|
||||
expect(wrapper.find('textarea').hasClass('ant-input-lg')).toBe(true);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment, container } = render(<TextArea size="large" />);
|
||||
expect(container.querySelector('textarea').classList.contains('ant-input-lg')).toBe(true);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('set mouse cursor position', () => {
|
||||
const defaultValue = '11111';
|
||||
const valLength = defaultValue.length;
|
||||
const ref = React.createRef();
|
||||
mount(<TextArea autoFocus ref={ref} defaultValue={defaultValue} />);
|
||||
render(<TextArea autoFocus ref={ref} defaultValue={defaultValue} />);
|
||||
ref.current.resizableTextArea.textArea.setSelectionRange(valLength, valLength);
|
||||
expect(ref.current.resizableTextArea.textArea.selectionStart).toEqual(5);
|
||||
expect(ref.current.resizableTextArea.textArea.selectionEnd).toEqual(5);
|
||||
@ -324,110 +333,118 @@ describe('TextArea', () => {
|
||||
|
||||
describe('TextArea allowClear', () => {
|
||||
it('should change type when click', () => {
|
||||
const wrapper = mount(<TextArea allowClear />);
|
||||
wrapper.find('textarea').simulate('change', { target: { value: '111' } });
|
||||
expect(wrapper.find('textarea').getDOMNode().value).toEqual('111');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapper.find('textarea').getDOMNode().value).toEqual('');
|
||||
const { asFragment, container } = render(<TextArea allowClear />);
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: '111' } });
|
||||
expect(container.querySelector('textarea').value).toEqual('111');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(container.querySelector('textarea').value).toEqual('');
|
||||
});
|
||||
|
||||
it('should not show icon if value is undefined, null or empty string', () => {
|
||||
const wrappers = [null, undefined, ''].map(val => mount(<TextArea allowClear value={val} />));
|
||||
wrappers.forEach(wrapper => {
|
||||
expect(wrapper.find('textarea').getDOMNode().value).toEqual('');
|
||||
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const wrappers = [null, undefined, ''].map(val => render(<TextArea allowClear value={val} />));
|
||||
wrappers.forEach(({ asFragment, container }) => {
|
||||
expect(container.querySelector('textarea').value).toEqual('');
|
||||
expect(container.querySelector('.ant-input-clear-icon-hidden')).toBeTruthy();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not show icon if defaultValue is undefined, null or empty string', () => {
|
||||
const wrappers = [null, undefined, ''].map(val =>
|
||||
mount(<TextArea allowClear defaultValue={val} />),
|
||||
render(<TextArea allowClear defaultValue={val} />),
|
||||
);
|
||||
wrappers.forEach(wrapper => {
|
||||
expect(wrapper.find('textarea').getDOMNode().value).toEqual('');
|
||||
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
wrappers.forEach(({ asFragment, container }) => {
|
||||
expect(container.querySelector('textarea').value).toEqual('');
|
||||
expect(container.querySelector('.ant-input-clear-icon-hidden')).toBeTruthy();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('should trigger event correctly', () => {
|
||||
let argumentEventObject;
|
||||
let argumentEventObjectType;
|
||||
let argumentEventObjectValue;
|
||||
const onChange = e => {
|
||||
argumentEventObject = e;
|
||||
argumentEventObjectType = e.type;
|
||||
argumentEventObjectValue = e.target.value;
|
||||
};
|
||||
const wrapper = mount(<TextArea allowClear defaultValue="111" onChange={onChange} />);
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(argumentEventObject.type).toBe('click');
|
||||
const { container } = render(<TextArea allowClear defaultValue="111" onChange={onChange} />);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
expect(argumentEventObjectType).toBe('click');
|
||||
expect(argumentEventObjectValue).toBe('');
|
||||
expect(wrapper.find('textarea').at(0).getDOMNode().value).toBe('');
|
||||
expect(container.querySelector('textarea').value).toBe('');
|
||||
});
|
||||
|
||||
it('should trigger event correctly on controlled mode', () => {
|
||||
let argumentEventObject;
|
||||
let argumentEventObjectType;
|
||||
let argumentEventObjectValue;
|
||||
const onChange = e => {
|
||||
argumentEventObject = e;
|
||||
argumentEventObjectType = e.type;
|
||||
argumentEventObjectValue = e.target.value;
|
||||
};
|
||||
const wrapper = mount(<TextArea allowClear value="111" onChange={onChange} />);
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(argumentEventObject.type).toBe('click');
|
||||
const { container } = render(<TextArea allowClear value="111" onChange={onChange} />);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
expect(argumentEventObjectType).toBe('click');
|
||||
expect(argumentEventObjectValue).toBe('');
|
||||
expect(wrapper.find('textarea').at(0).getDOMNode().value).toBe('111');
|
||||
expect(container.querySelector('textarea').value).toBe('111');
|
||||
});
|
||||
|
||||
it('should focus textarea after clear', () => {
|
||||
const wrapper = mount(<TextArea allowClear defaultValue="111" />, { attachTo: document.body });
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(document.activeElement).toBe(wrapper.find('textarea').at(0).getDOMNode());
|
||||
wrapper.unmount();
|
||||
const { container, unmount } = render(<TextArea allowClear defaultValue="111" />, {
|
||||
container: document.body,
|
||||
});
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
expect(document.activeElement).toBe(container.querySelector('textarea'));
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should not support allowClear when it is disabled', () => {
|
||||
const wrapper = mount(<TextArea allowClear defaultValue="111" disabled />);
|
||||
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
|
||||
const { container } = render(<TextArea allowClear defaultValue="111" disabled />);
|
||||
expect(container.querySelector('.ant-input-clear-icon-hidden')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('not block input when `value` is undefined', () => {
|
||||
const wrapper = mount(<Input value={undefined} />);
|
||||
wrapper.find('input').simulate('change', { target: { value: 'Bamboo' } });
|
||||
expect(wrapper.find('input').props().value).toEqual('Bamboo');
|
||||
const { container, rerender } = render(<Input value={undefined} />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'Bamboo' } });
|
||||
expect(container.querySelector('input').value).toEqual('Bamboo');
|
||||
|
||||
// Controlled
|
||||
wrapper.setProps({ value: 'Light' });
|
||||
wrapper.find('input').simulate('change', { target: { value: 'Bamboo' } });
|
||||
expect(wrapper.find('input').props().value).toEqual('Light');
|
||||
rerender(<Input value="Light" />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'Bamboo' } });
|
||||
expect(container.querySelector('input').value).toEqual('Light');
|
||||
});
|
||||
|
||||
it('scroll to bottom when autoSize', async () => {
|
||||
const wrapper = mount(<Input.TextArea autoSize />, { attachTo: document.body });
|
||||
wrapper.find('textarea').simulate('focus');
|
||||
wrapper.find('textarea').getDOMNode().focus();
|
||||
const ref = React.createRef();
|
||||
const { container, unmount } = render(<Input.TextArea ref={ref} autoSize />, {
|
||||
container: document.body,
|
||||
legacyRoot: true,
|
||||
});
|
||||
fireEvent.focus(container.querySelector('textarea'));
|
||||
container.querySelector('textarea').focus();
|
||||
|
||||
const setSelectionRangeFn = jest.spyOn(
|
||||
wrapper.find('textarea').getDOMNode(),
|
||||
container.querySelector('textarea'),
|
||||
'setSelectionRange',
|
||||
);
|
||||
wrapper.find('textarea').simulate('input', { target: { value: '\n1' } });
|
||||
wrapper.triggerResize();
|
||||
fireEvent.input(container.querySelector('textarea'), { target: { value: '\n1' } });
|
||||
const target = ref.current.resizableTextArea.textArea;
|
||||
triggerResize(target);
|
||||
await sleep(100);
|
||||
expect(setSelectionRangeFn).toHaveBeenCalled();
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/26308
|
||||
it('should display defaultValue when value is undefined', () => {
|
||||
const wrapper = mount(<Input.TextArea defaultValue="Light" value={undefined} />);
|
||||
expect(wrapper.find('textarea').at(0).getDOMNode().value).toBe('Light');
|
||||
const { container } = render(<Input.TextArea defaultValue="Light" value={undefined} />);
|
||||
expect(container.querySelector('textarea').value).toBe('Light');
|
||||
});
|
||||
|
||||
it('onChange event should return HTMLTextAreaElement', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(<Input.TextArea onChange={onChange} allowClear />);
|
||||
const { container } = render(<Input.TextArea onChange={onChange} allowClear />);
|
||||
|
||||
function isNativeElement() {
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
@ -440,20 +457,16 @@ describe('TextArea allowClear', () => {
|
||||
}
|
||||
|
||||
// Change
|
||||
wrapper.find('textarea').simulate('change', {
|
||||
target: {
|
||||
value: 'bamboo',
|
||||
},
|
||||
});
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'bamboo' } });
|
||||
isNativeElement();
|
||||
|
||||
// Composition End
|
||||
wrapper.find('textarea').instance().value = 'light'; // enzyme not support change `currentTarget`
|
||||
wrapper.find('textarea').simulate('compositionEnd');
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'light' } });
|
||||
fireEvent.compositionEnd(container.querySelector('textarea'));
|
||||
isNativeElement();
|
||||
|
||||
// Reset
|
||||
wrapper.find('.ant-input-clear-icon').first().simulate('click');
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
isNativeElement();
|
||||
});
|
||||
|
||||
@ -472,45 +485,49 @@ describe('TextArea allowClear', () => {
|
||||
);
|
||||
};
|
||||
|
||||
const wrapper = mount(<App />);
|
||||
const { container, unmount } = render(<App />);
|
||||
container.querySelector('textarea').focus();
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: '111' } });
|
||||
expect(container.querySelector('textarea').value).toEqual('111');
|
||||
|
||||
wrapper.find('textarea').getDOMNode().focus();
|
||||
wrapper.find('textarea').simulate('change', { target: { value: '111' } });
|
||||
expect(wrapper.find('textarea').getDOMNode().value).toEqual('111');
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
expect(container.querySelector('textarea').value).toEqual('');
|
||||
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(wrapper.find('textarea').getDOMNode().value).toEqual('');
|
||||
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/31200
|
||||
it('should not lost focus when clear input', () => {
|
||||
const onBlur = jest.fn();
|
||||
const wrapper = mount(<TextArea allowClear defaultValue="value" onBlur={onBlur} />, {
|
||||
attachTo: document.body,
|
||||
});
|
||||
wrapper.find('textarea').getDOMNode().focus();
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('mouseDown');
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('mouseUp');
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('focus');
|
||||
wrapper.find('.ant-input-clear-icon').at(0).getDOMNode().click();
|
||||
const { container, unmount } = render(
|
||||
<TextArea allowClear defaultValue="value" onBlur={onBlur} />,
|
||||
{
|
||||
container: document.body,
|
||||
},
|
||||
);
|
||||
container.querySelector('textarea').focus();
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.mouseUp(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.focus(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
expect(onBlur).not.toBeCalled();
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should focus text area after clear', () => {
|
||||
const wrapper = mount(<TextArea allowClear defaultValue="111" />, { attachTo: document.body });
|
||||
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
|
||||
expect(document.activeElement).toBe(wrapper.find('textarea').at(0).getDOMNode());
|
||||
wrapper.unmount();
|
||||
const { container, unmount } = render(<TextArea allowClear defaultValue="111" />, {
|
||||
container: document.body,
|
||||
});
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
expect(document.activeElement).toBe(container.querySelector('textarea'));
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should display boolean value as string', () => {
|
||||
const wrapper = mount(<TextArea value />);
|
||||
expect(wrapper.find('textarea').first().getDOMNode().value).toBe('true');
|
||||
wrapper.setProps({ value: false });
|
||||
expect(wrapper.find('textarea').first().getDOMNode().value).toBe('false');
|
||||
const { container, rerender } = render(<TextArea value />);
|
||||
expect(container.querySelector('textarea').value).toBe('true');
|
||||
rerender(<TextArea value={false} />);
|
||||
expect(container.querySelector('textarea').value).toBe('false');
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Input from '..';
|
||||
import type { InputProps } from '../Input';
|
||||
import { render } from '../../../tests/utils';
|
||||
|
||||
describe('Input types', () => {
|
||||
it('should support data-attributes', () => {
|
||||
@ -9,9 +9,9 @@ describe('Input types', () => {
|
||||
'data-test': 'test',
|
||||
size: 'large',
|
||||
};
|
||||
const wrapper = mount(<Input {...dataProps} />);
|
||||
expect(wrapper.find('input').prop('data-test')).toBe('test');
|
||||
const wrapper2 = mount(<Input data-test="test" size="large" />);
|
||||
expect(wrapper2.find('input').prop('data-test')).toBe('test');
|
||||
const { container } = render(<Input {...dataProps} />);
|
||||
expect(container.querySelector('input')?.getAttribute('data-test')).toBe('test');
|
||||
const { container: container2 } = render(<Input data-test="test" size="large" />);
|
||||
expect(container2.querySelector('input')?.getAttribute('data-test')).toBe('test');
|
||||
});
|
||||
});
|
||||
|
@ -102,6 +102,7 @@
|
||||
.@{inputClass}-group-rtl & {
|
||||
border-right: 0;
|
||||
border-left: @border-width-base @border-style-base @input-border-color;
|
||||
border-radius: @border-radius-base 0 0 @border-radius-base;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,18 @@
|
||||
import * as React from 'react';
|
||||
import Dialog from 'rc-dialog';
|
||||
import classNames from 'classnames';
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import classNames from 'classnames';
|
||||
import Dialog from 'rc-dialog';
|
||||
import * as React from 'react';
|
||||
|
||||
import { getConfirmLocale } from './locale';
|
||||
import Button from '../button';
|
||||
import type { LegacyButtonType, ButtonProps } from '../button/button';
|
||||
import type { ButtonProps, LegacyButtonType } from '../button/button';
|
||||
import { convertLegacyProps } from '../button/button';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import type { DirectionType } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { canUseDocElement } from '../_util/styleChecker';
|
||||
import { NoFormStyle } from '../form/context';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import { getTransitionName } from '../_util/motion';
|
||||
import { canUseDocElement } from '../_util/styleChecker';
|
||||
import { getConfirmLocale } from './locale';
|
||||
import useStyle from './style';
|
||||
|
||||
let mousePosition: { x: number; y: number } | null;
|
||||
@ -204,23 +205,25 @@ const Modal: React.FC<ModalProps> = props => {
|
||||
[`${prefixCls}-wrap-rtl`]: direction === 'rtl',
|
||||
});
|
||||
return wrapSSR(
|
||||
<Dialog
|
||||
{...restProps}
|
||||
getContainer={
|
||||
getContainer === undefined ? (getContextPopupContainer as getContainerFunc) : getContainer
|
||||
}
|
||||
prefixCls={prefixCls}
|
||||
rootClassName={hashId}
|
||||
wrapClassName={wrapClassNameExtended}
|
||||
footer={footer === undefined ? defaultFooter : footer}
|
||||
visible={visible}
|
||||
mousePosition={mousePosition}
|
||||
onClose={handleCancel}
|
||||
closeIcon={closeIconToRender}
|
||||
focusTriggerAfterClose={focusTriggerAfterClose}
|
||||
transitionName={getTransitionName(rootPrefixCls, 'zoom', props.transitionName)}
|
||||
maskTransitionName={getTransitionName(rootPrefixCls, 'fade', props.maskTransitionName)}
|
||||
/>,
|
||||
<NoFormStyle status override>
|
||||
<Dialog
|
||||
{...restProps}
|
||||
getContainer={
|
||||
getContainer === undefined ? (getContextPopupContainer as getContainerFunc) : getContainer
|
||||
}
|
||||
prefixCls={prefixCls}
|
||||
rootClassName={hashId}
|
||||
wrapClassName={wrapClassNameExtended}
|
||||
footer={footer === undefined ? defaultFooter : footer}
|
||||
visible={visible}
|
||||
mousePosition={mousePosition}
|
||||
onClose={handleCancel}
|
||||
closeIcon={closeIconToRender}
|
||||
focusTriggerAfterClose={focusTriggerAfterClose}
|
||||
transitionName={getTransitionName(rootPrefixCls, 'zoom', props.transitionName)}
|
||||
maskTransitionName={getTransitionName(rootPrefixCls, 'fade', props.maskTransitionName)}
|
||||
/>
|
||||
</NoFormStyle>,
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -42,6 +42,13 @@
|
||||
// overflow: hidden;
|
||||
// content: '\a0';
|
||||
// }
|
||||
|
||||
// &&-in-form-item {
|
||||
// input[type='radio'] {
|
||||
// width: 14px;
|
||||
// height: 14px;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// .@{radio-prefix-cls} {
|
||||
@ -126,6 +133,12 @@
|
||||
// cursor: pointer;
|
||||
// opacity: 0;
|
||||
// }
|
||||
|
||||
// &&-disabled {
|
||||
// .@{radio-inner-prefix-cls} {
|
||||
// border-color: @border-color-base;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 选中状态
|
||||
@ -146,7 +159,6 @@
|
||||
|
||||
// .@{radio-inner-prefix-cls} {
|
||||
// background-color: @input-disabled-bg;
|
||||
// border-color: @border-color-base !important;
|
||||
// cursor: not-allowed;
|
||||
|
||||
// &::after {
|
||||
|
@ -214,7 +214,7 @@ const getRadioBasicStyle: GenerateStyle<RadioToken> = token => {
|
||||
|
||||
[radioInnerPrefixCls]: {
|
||||
backgroundColor: colorBgComponentDisabled,
|
||||
borderColor: `${colorBorder} !important`,
|
||||
borderColor: colorBorder,
|
||||
cursor: 'not-allowed',
|
||||
|
||||
'&::after': {
|
||||
|
@ -1,24 +1,24 @@
|
||||
// TODO: 4.0 - codemod should help to change `filterOption` to support node props.
|
||||
|
||||
import * as React from 'react';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import classNames from 'classnames';
|
||||
import type { SelectProps as RcSelectProps } from 'rc-select';
|
||||
import RcSelect, { Option, OptGroup, BaseSelectRef } from 'rc-select';
|
||||
import type { BaseOptionType, DefaultOptionType } from 'rc-select/lib/Select';
|
||||
import RcSelect, { BaseSelectRef, OptGroup, Option } from 'rc-select';
|
||||
import { OptionProps } from 'rc-select/lib/Option';
|
||||
import type { BaseOptionType, DefaultOptionType } from 'rc-select/lib/Select';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import getIcons from './utils/iconUtil';
|
||||
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 type { SelectCommonPlacement } from '../_util/motion';
|
||||
import { getTransitionDirection, getTransitionName } from '../_util/motion';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import type { SelectCommonPlacement } from '../_util/motion';
|
||||
import { getTransitionName, getTransitionDirection } from '../_util/motion';
|
||||
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
|
||||
import getIcons from './utils/iconUtil';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { render, fireEvent } from '../../../tests/utils';
|
||||
import Table from '..';
|
||||
|
||||
const columns = [
|
||||
@ -39,13 +39,13 @@ const data = [
|
||||
|
||||
describe('Table.expand', () => {
|
||||
it('click to expand', () => {
|
||||
const wrapper = mount(<Table columns={columns} dataSource={data} />);
|
||||
wrapper.find('.ant-table-row-expand-icon').last().simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { container, asFragment } = render(<Table columns={columns} dataSource={data} />);
|
||||
fireEvent.click(container.querySelector('.ant-table-row-expand-icon'));
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('expandRowByClick should not block click icon', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={[John, Jim]}
|
||||
@ -55,26 +55,15 @@ describe('Table.expand', () => {
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(container.querySelector('.ant-table-row-expand-icon'));
|
||||
expect(container.querySelector('.ant-table-row-expand-icon-expanded')).toBeTruthy();
|
||||
|
||||
wrapper.find('.ant-table-row-expand-icon').first().simulate('click');
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-table-row-expand-icon')
|
||||
.first()
|
||||
.hasClass('ant-table-row-expand-icon-expanded'),
|
||||
).toBeTruthy();
|
||||
|
||||
wrapper.find('.ant-table-row-expand-icon').first().simulate('click');
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-table-row-expand-icon')
|
||||
.first()
|
||||
.hasClass('ant-table-row-expand-icon-collapsed'),
|
||||
).toBeTruthy();
|
||||
fireEvent.click(container.querySelector('.ant-table-row-expand-icon'));
|
||||
expect(container.querySelector('.ant-table-row-expand-icon-collapsed')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('show expandIcon', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Table
|
||||
columns={[{ dataIndex: 'key' }]}
|
||||
dataSource={[{ key: 233 }]}
|
||||
@ -83,23 +72,19 @@ describe('Table.expand', () => {
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.expand-icon')).toHaveLength(1);
|
||||
expect(container.querySelectorAll('.expand-icon')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('row indent padding should be 0px when indentSize defined as 0', () => {
|
||||
const wrapper = mount(<Table indentSize={0} columns={columns} dataSource={data} />);
|
||||
const button = wrapper.find('.ant-table-row-expand-icon').at(0);
|
||||
button.simulate('click');
|
||||
expect(wrapper.find('.indent-level-1').at(0).prop('style')).toHaveProperty(
|
||||
'paddingLeft',
|
||||
'0px',
|
||||
);
|
||||
const { container } = render(<Table indentSize={0} columns={columns} dataSource={data} />);
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-table-row-expand-icon'));
|
||||
expect(container.querySelector('.indent-level-1').style.paddingLeft).toEqual('0px');
|
||||
});
|
||||
|
||||
describe('expandIconColumnIndex', () => {
|
||||
it('basic', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Table
|
||||
columns={[{ dataIndex: 'key' }]}
|
||||
dataSource={[{ key: 'bamboo' }]}
|
||||
@ -110,12 +95,13 @@ describe('Table.expand', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('td').at(0).text()).toEqual('bamboo');
|
||||
expect(wrapper.find('td').at(1).find('.ant-table-row-expand-icon').length).toBeTruthy();
|
||||
const tdNodeList = container.querySelectorAll('td');
|
||||
expect(tdNodeList[0].textContent).toEqual('bamboo');
|
||||
expect(tdNodeList[1].querySelector('.ant-table-row-expand-icon')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('work with selection', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Table
|
||||
columns={[{ dataIndex: 'key' }]}
|
||||
dataSource={[{ key: 'bamboo' }]}
|
||||
@ -126,10 +112,10 @@ describe('Table.expand', () => {
|
||||
rowSelection={{}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('td').at(0).find('.ant-checkbox-input').length).toBeTruthy();
|
||||
expect(wrapper.find('td').at(1).text()).toEqual('bamboo');
|
||||
expect(wrapper.find('td').at(2).find('.ant-table-row-expand-icon').length).toBeTruthy();
|
||||
const tdNodeList = container.querySelectorAll('td');
|
||||
expect(tdNodeList[0].querySelector('.ant-checkbox-input')).toBeTruthy();
|
||||
expect(tdNodeList[1].textContent).toEqual('bamboo');
|
||||
expect(tdNodeList[2].querySelector('.ant-table-row-expand-icon')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { render } from '../../../tests/utils';
|
||||
import Table from '..';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
|
||||
@ -37,7 +37,7 @@ describe('Table.order', () => {
|
||||
|
||||
it('warning if duplicated SELECTION_COLUMN', () => {
|
||||
resetWarned();
|
||||
mount(
|
||||
render(
|
||||
createTable({
|
||||
columns: [Table.SELECTION_COLUMN, Table.SELECTION_COLUMN],
|
||||
rowSelection: {},
|
||||
@ -53,7 +53,7 @@ describe('Table.order', () => {
|
||||
});
|
||||
|
||||
it('auto fixed', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
columns: [
|
||||
{
|
||||
@ -69,8 +69,9 @@ describe('Table.order', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
expect(wrapper.find('tr').last().find('td')).toHaveLength(3);
|
||||
expect(wrapper.find('tr').last().find('td.ant-table-cell-fix-left')).toHaveLength(2);
|
||||
wrapper.unmount();
|
||||
expect(container.querySelectorAll('tr')[1].querySelectorAll('td')).toHaveLength(3);
|
||||
expect(
|
||||
container.querySelectorAll('tr')[1].querySelectorAll('td.ant-table-cell-fix-left'),
|
||||
).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* eslint-disable import/first */
|
||||
jest.mock('../../_util/scrollTo');
|
||||
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { render, fireEvent } from '../../../tests/utils';
|
||||
import Table from '..';
|
||||
import scrollTo from '../../_util/scrollTo';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
@ -33,54 +34,64 @@ describe('Table.pagination', () => {
|
||||
return <Table columns={columns} dataSource={data} pagination={pagination} {...props} />;
|
||||
}
|
||||
|
||||
function renderedNames(wrapper) {
|
||||
return wrapper.find('BodyRow').map(row => row.props().record.name);
|
||||
function renderedNames(contain) {
|
||||
// --- reserve comment for code review ---
|
||||
// return wrapper.find('BodyRow').map(row => row.props().record.name);
|
||||
const namesList = [];
|
||||
contain
|
||||
.querySelector('.ant-table-tbody')
|
||||
.querySelectorAll('tr')
|
||||
.forEach(tr => {
|
||||
namesList.push(tr.querySelector('td').textContent);
|
||||
});
|
||||
return namesList;
|
||||
}
|
||||
|
||||
it('renders pagination correctly', () => {
|
||||
const wrapper = mount(createTable());
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(createTable());
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('not crash when pageSize is undefined', () => {
|
||||
expect(() => {
|
||||
mount(createTable({ pagination: { pageSize: undefined } }));
|
||||
render(createTable({ pagination: { pageSize: undefined } }));
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should not show pager if pagination.hideOnSinglePage is true and only 1 page', () => {
|
||||
const wrapper = mount(createTable({ pagination: { pageSize: 3, hideOnSinglePage: true } }));
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { pageSize: 3, hideOnSinglePage: false } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { pageSize: 4, hideOnSinglePage: true } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(0);
|
||||
wrapper.setProps({ pagination: { pageSize: 4, hideOnSinglePage: false } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { pageSize: 5, hideOnSinglePage: true } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(0);
|
||||
wrapper.setProps({ pagination: { pageSize: 5, hideOnSinglePage: false } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
const { container, rerender } = render(
|
||||
createTable({ pagination: { pageSize: 3, hideOnSinglePage: true } }),
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
rerender(createTable({ pagination: { pageSize: 3, hideOnSinglePage: false } }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
rerender(createTable({ pagination: { pageSize: 4, hideOnSinglePage: true } }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(0);
|
||||
rerender(createTable({ pagination: { pageSize: 4, hideOnSinglePage: false } }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
rerender(createTable({ pagination: { pageSize: 5, hideOnSinglePage: true } }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(0);
|
||||
rerender(createTable({ pagination: { pageSize: 5, hideOnSinglePage: false } }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should use pageSize when defaultPageSize and pageSize are both specified', () => {
|
||||
const wrapper = mount(createTable({ pagination: { pageSize: 3, defaultPageSize: 4 } }));
|
||||
expect(wrapper.find('.ant-pagination-item')).toHaveLength(2);
|
||||
const { container } = render(createTable({ pagination: { pageSize: 3, defaultPageSize: 4 } }));
|
||||
expect(container.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('paginate data', () => {
|
||||
const wrapper = mount(createTable());
|
||||
const { container } = render(createTable());
|
||||
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy']);
|
||||
wrapper.find('Pager').last().simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Jerry']);
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Lucy']);
|
||||
fireEvent.click(container.querySelector('.ant-pagination-next'));
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Jerry']);
|
||||
});
|
||||
|
||||
it('repaginates when pageSize change', () => {
|
||||
const wrapper = mount(createTable());
|
||||
|
||||
wrapper.setProps({ pagination: { pageSize: 1 } });
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack']);
|
||||
const { container, rerender } = render(createTable());
|
||||
rerender(createTable({ pagination: { pageSize: 1 } }));
|
||||
expect(renderedNames(container)).toEqual(['Jack']);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/33487
|
||||
@ -100,32 +111,32 @@ describe('Table.pagination', () => {
|
||||
/>
|
||||
);
|
||||
}
|
||||
const wrapper = mount(<App />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(<App />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should accept pagination size', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
pagination: { size: 'small' },
|
||||
}),
|
||||
);
|
||||
expect(wrapper.find('.ant-pagination.ant-pagination-mini')).toHaveLength(1);
|
||||
expect(container.querySelectorAll('.ant-pagination.ant-pagination-mini')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should scroll to first row when page change', () => {
|
||||
scrollTo.mockReturnValue(null);
|
||||
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({ scroll: { y: 20 }, pagination: { showSizeChanger: true, pageSize: 2 } }),
|
||||
);
|
||||
expect(scrollTo).toHaveBeenCalledTimes(0);
|
||||
|
||||
wrapper.find('Pager').last().simulate('click');
|
||||
fireEvent.click(container.querySelector('.ant-pagination-next'));
|
||||
expect(scrollTo).toHaveBeenCalledTimes(1);
|
||||
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
wrapper.find('.ant-select-item').last().simulate('click');
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
|
||||
fireEvent.click(container.querySelectorAll('.ant-select-item')[1]);
|
||||
expect(scrollTo).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
@ -134,24 +145,23 @@ describe('Table.pagination', () => {
|
||||
expect(top).toBe(0);
|
||||
expect(getContainer().className).toBe('ant-table-body');
|
||||
});
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({ scroll: { y: 20 }, pagination: { showSizeChanger: true, pageSize: 2 } }),
|
||||
);
|
||||
wrapper.find('Pager').last().simulate('click');
|
||||
fireEvent.click(container.querySelector('.ant-pagination-next'));
|
||||
});
|
||||
|
||||
it('fires change event', () => {
|
||||
const handleChange = jest.fn();
|
||||
const handlePaginationChange = jest.fn();
|
||||
const noop = () => {};
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
pagination: { ...pagination, onChange: handlePaginationChange, onShowSizeChange: noop },
|
||||
onChange: handleChange,
|
||||
}),
|
||||
);
|
||||
|
||||
wrapper.find('Pager').last().simulate('click');
|
||||
fireEvent.click(container.querySelector('.ant-pagination-next'));
|
||||
|
||||
expect(handleChange).toHaveBeenCalledWith(
|
||||
{
|
||||
@ -178,50 +188,56 @@ describe('Table.pagination', () => {
|
||||
// https://github.com/ant-design/ant-design/issues/4532
|
||||
// https://codepen.io/afc163/pen/dVeNoP?editors=001
|
||||
it('should have pager when change pagination from false to undefined', () => {
|
||||
const wrapper = mount(createTable({ pagination: false }));
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(0);
|
||||
wrapper.setProps({ pagination: undefined });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
expect(wrapper.find('.ant-pagination-item-active')).toHaveLength(1);
|
||||
const { container, rerender } = render(createTable({ pagination: false }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(0);
|
||||
|
||||
rerender(createTable({ pagination: undefined }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
expect(container.querySelectorAll('.ant-pagination-item-active')).toHaveLength(1);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/4532
|
||||
// https://codepen.io/afc163/pen/pWVRJV?editors=001
|
||||
it('should display pagination as prop pagination change between true and false', () => {
|
||||
const wrapper = mount(createTable());
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
expect(wrapper.find('.ant-pagination-item')).toHaveLength(2);
|
||||
wrapper.setProps({ pagination: false });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(0);
|
||||
wrapper.setProps({ pagination });
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
expect(wrapper.find('.ant-pagination-item')).toHaveLength(2);
|
||||
wrapper.find('.ant-pagination-item-2').simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Jerry']);
|
||||
wrapper.setProps({ pagination: false });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(0);
|
||||
wrapper.setProps({ pagination: undefined });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
expect(wrapper.find('.ant-pagination-item')).toHaveLength(2);
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Jerry']);
|
||||
const { container, rerender } = render(createTable());
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
expect(container.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
|
||||
|
||||
rerender(createTable({ pagination: false }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(0);
|
||||
|
||||
rerender(createTable({ pagination }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
expect(container.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
|
||||
fireEvent.click(container.querySelector('.ant-pagination-item-2'));
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Jerry']);
|
||||
|
||||
rerender(createTable({ pagination: false }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(0);
|
||||
|
||||
rerender(createTable({ pagination: undefined }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
expect(container.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Jerry']);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/5259
|
||||
it('change to correct page when data source changes', () => {
|
||||
const wrapper = mount(createTable({ pagination: { pageSize: 1 } }));
|
||||
wrapper.find('.ant-pagination-item-3').simulate('click');
|
||||
wrapper.setProps({ dataSource: [data[0]] });
|
||||
expect(wrapper.find('.ant-pagination-item-1').hasClass('ant-pagination-item-active')).toBe(
|
||||
true,
|
||||
);
|
||||
const { container, rerender } = render(createTable({ pagination: { pageSize: 1 } }));
|
||||
fireEvent.click(container.querySelector('.ant-pagination-item-3'));
|
||||
rerender(createTable({ dataSource: [data[0]] }));
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-pagination-item-1')
|
||||
.className.includes('ant-pagination-item-active'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/24913
|
||||
it('should called onChange when pageSize change', () => {
|
||||
const onChange = jest.fn();
|
||||
const onShowSizeChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
pagination: {
|
||||
current: 1,
|
||||
@ -232,28 +248,34 @@ describe('Table.pagination', () => {
|
||||
},
|
||||
}),
|
||||
);
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
expect(wrapper.find('.ant-select-item-option').length).toBe(4);
|
||||
wrapper.find('.ant-select-item-option').at(1).simulate('click');
|
||||
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
|
||||
expect(container.querySelectorAll('.ant-select-item-option').length).toBe(4);
|
||||
fireEvent.click(container.querySelectorAll('.ant-select-item-option')[1]);
|
||||
expect(onChange).toHaveBeenCalledWith(1, 20);
|
||||
});
|
||||
|
||||
it('should not change page when pagination current is specified', () => {
|
||||
const wrapper = mount(createTable({ pagination: { current: 2, pageSize: 1 } }));
|
||||
expect(wrapper.find('.ant-pagination-item-2').hasClass('ant-pagination-item-active')).toBe(
|
||||
true,
|
||||
);
|
||||
wrapper.find('.ant-pagination-item-3').simulate('click');
|
||||
expect(wrapper.find('.ant-pagination-item-2').hasClass('ant-pagination-item-active')).toBe(
|
||||
true,
|
||||
);
|
||||
const { container } = render(createTable({ pagination: { current: 2, pageSize: 1 } }));
|
||||
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-pagination-item-2')
|
||||
.className.includes('ant-pagination-item-active'),
|
||||
).toBe(true);
|
||||
fireEvent.click(container.querySelector('.ant-pagination-item-3'));
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-pagination-item-2')
|
||||
.className.includes('ant-pagination-item-active'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/29175
|
||||
it('should change page to max page count when pageSize change without pagination.total', () => {
|
||||
const onChange = jest.fn();
|
||||
const onShowSizeChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
pagination: {
|
||||
current: 10,
|
||||
@ -264,10 +286,13 @@ describe('Table.pagination', () => {
|
||||
dataSource: longData,
|
||||
}),
|
||||
);
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
expect(wrapper.find('.ant-select-item-option').length).toBe(4);
|
||||
wrapper.find('.ant-select-item-option').at(1).simulate('click');
|
||||
const newPageSize = parseInt(wrapper.find('.ant-select-item-option').at(1).text(), 10);
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
|
||||
expect(container.querySelectorAll('.ant-select-item-option').length).toBe(4);
|
||||
fireEvent.click(container.querySelectorAll('.ant-select-item-option')[1]);
|
||||
const newPageSize = parseInt(
|
||||
container.querySelectorAll('.ant-select-item-option')[1].textContent,
|
||||
10,
|
||||
);
|
||||
expect(onChange).toHaveBeenCalledWith(longData.length / newPageSize, 20);
|
||||
});
|
||||
|
||||
@ -275,7 +300,7 @@ describe('Table.pagination', () => {
|
||||
const onChange = jest.fn();
|
||||
const onShowSizeChange = jest.fn();
|
||||
const total = 20000;
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
pagination: {
|
||||
current: total / 10,
|
||||
@ -287,10 +312,14 @@ describe('Table.pagination', () => {
|
||||
dataSource: longData,
|
||||
}),
|
||||
);
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
expect(wrapper.find('.ant-select-item-option').length).toBe(4);
|
||||
wrapper.find('.ant-select-item-option').at(1).simulate('click');
|
||||
const newPageSize = parseInt(wrapper.find('.ant-select-item-option').at(1).text(), 10);
|
||||
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
|
||||
expect(container.querySelectorAll('.ant-select-item-option').length).toBe(4);
|
||||
fireEvent.click(container.querySelectorAll('.ant-select-item-option')[1]);
|
||||
const newPageSize = parseInt(
|
||||
container.querySelectorAll('.ant-select-item-option')[1].textContent,
|
||||
10,
|
||||
);
|
||||
expect(onChange).toHaveBeenCalledWith(total / newPageSize, 20);
|
||||
});
|
||||
|
||||
@ -298,7 +327,7 @@ describe('Table.pagination', () => {
|
||||
it('should not change page to max page if current is not greater max page when pageSize change', () => {
|
||||
const onChange = jest.fn();
|
||||
const onShowSizeChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
pagination: {
|
||||
current: 4,
|
||||
@ -309,15 +338,15 @@ describe('Table.pagination', () => {
|
||||
dataSource: longData,
|
||||
}),
|
||||
);
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
expect(wrapper.find('.ant-select-item-option').length).toBe(4);
|
||||
wrapper.find('.ant-select-item-option').at(1).simulate('click');
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
|
||||
expect(container.querySelectorAll('.ant-select-item-option').length).toBe(4);
|
||||
fireEvent.click(container.querySelectorAll('.ant-select-item-option')[1]);
|
||||
expect(onChange).toHaveBeenCalledWith(4, 20);
|
||||
});
|
||||
|
||||
it('should reset current to max page when data length is cut', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container, rerender } = render(
|
||||
createTable({
|
||||
pagination: {
|
||||
current: 10,
|
||||
@ -327,30 +356,59 @@ describe('Table.pagination', () => {
|
||||
dataSource: longData,
|
||||
}),
|
||||
);
|
||||
expect(wrapper.find('.ant-pagination-item-active').text()).toBe('10');
|
||||
wrapper.setProps({
|
||||
dataSource: longData.filter(item => item.key < 60),
|
||||
});
|
||||
expect(wrapper.find('.ant-pagination-item-active').text()).toBe('6');
|
||||
expect(container.querySelector('.ant-pagination-item-active').textContent).toBe('10');
|
||||
rerender(
|
||||
createTable({
|
||||
pagination: {
|
||||
current: 10,
|
||||
pageSize: 10,
|
||||
onChange,
|
||||
},
|
||||
dataSource: longData.filter(item => item.key < 60),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(container.querySelector('.ant-pagination-item-active').textContent).toBe('6');
|
||||
});
|
||||
|
||||
it('specify the position of pagination', () => {
|
||||
const wrapper = mount(createTable({ pagination: { position: ['topLeft'] } }));
|
||||
expect(wrapper.find('.ant-spin-container').children()).toHaveLength(2);
|
||||
expect(wrapper.find('.ant-spin-container').childAt(0).find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: ['bottomRight'] } });
|
||||
expect(wrapper.find('.ant-spin-container').children()).toHaveLength(2);
|
||||
expect(wrapper.find('.ant-spin-container').childAt(1).find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: ['topLeft', 'bottomRight'] } });
|
||||
expect(wrapper.find('.ant-spin-container').children()).toHaveLength(3);
|
||||
expect(wrapper.find('.ant-spin-container').childAt(0).find('.ant-pagination')).toHaveLength(1);
|
||||
expect(wrapper.find('.ant-spin-container').childAt(2).find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: ['none', 'none'] } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(0);
|
||||
wrapper.setProps({ pagination: { position: ['invalid'] } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: ['invalid', 'invalid'] } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
const { container, rerender } = render(createTable({ pagination: { position: ['topLeft'] } }));
|
||||
expect(container.querySelector('.ant-spin-container').children).toHaveLength(2);
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-spin-container')
|
||||
.children[0].className.includes('ant-pagination'),
|
||||
).toBe(true);
|
||||
|
||||
rerender(createTable({ pagination: { position: ['bottomRight'] } }));
|
||||
expect(container.querySelector('.ant-spin-container').children).toHaveLength(2);
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-spin-container')
|
||||
.children[1].className.includes('ant-pagination'),
|
||||
).toBe(true);
|
||||
|
||||
rerender(createTable({ pagination: { position: ['topLeft', 'bottomRight'] } }));
|
||||
expect(container.querySelector('.ant-spin-container').children).toHaveLength(3);
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-spin-container')
|
||||
.children[0].className.includes('ant-pagination'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-spin-container')
|
||||
.children[2].className.includes('ant-pagination'),
|
||||
).toBe(true);
|
||||
|
||||
rerender(createTable({ pagination: { position: ['none', 'none'] } }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(0);
|
||||
|
||||
rerender(createTable({ pagination: { position: ['invalid'] } }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
|
||||
rerender(createTable({ pagination: { position: ['invalid', 'invalid'] } }));
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -358,15 +416,15 @@ describe('Table.pagination', () => {
|
||||
* to `pagination`, since they misunderstand that `pagination` can accept a boolean value.
|
||||
*/
|
||||
it('Accepts pagination as true', () => {
|
||||
const wrapper = mount(createTable({ pagination: true }));
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(createTable({ pagination: true }));
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('ajax render should keep display by the dataSource', () => {
|
||||
const onChange = jest.fn();
|
||||
const onPaginationChange = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
onChange,
|
||||
pagination: {
|
||||
@ -376,9 +434,11 @@ describe('Table.pagination', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
expect(wrapper.find('.ant-table-tbody tr.ant-table-row')).toHaveLength(data.length);
|
||||
expect(container.querySelectorAll('.ant-table-tbody tr.ant-table-row')).toHaveLength(
|
||||
data.length,
|
||||
);
|
||||
|
||||
wrapper.find('.ant-pagination .ant-pagination-item-2').simulate('click');
|
||||
fireEvent.click(container.querySelector('.ant-pagination .ant-pagination-item-2'));
|
||||
expect(onChange.mock.calls[0][0].current).toBe(2);
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
{ current: 2, pageSize: 10, total: 200 },
|
||||
@ -396,14 +456,16 @@ describe('Table.pagination', () => {
|
||||
);
|
||||
expect(onPaginationChange).toHaveBeenCalledWith(2, 10);
|
||||
|
||||
expect(wrapper.find('.ant-table-tbody tr.ant-table-row')).toHaveLength(data.length);
|
||||
expect(container.querySelectorAll('.ant-table-tbody tr.ant-table-row')).toHaveLength(
|
||||
data.length,
|
||||
);
|
||||
});
|
||||
|
||||
it('onShowSizeChange should trigger once', () => {
|
||||
jest.useFakeTimers();
|
||||
const onShowSizeChange = jest.fn();
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
pagination: {
|
||||
total: 200,
|
||||
@ -413,11 +475,14 @@ describe('Table.pagination', () => {
|
||||
onChange,
|
||||
}),
|
||||
);
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
jest.runAllTimers();
|
||||
const dropdownWrapper = wrapper.find('Trigger');
|
||||
expect(wrapper.find('.ant-select-item-option').length).toBe(4);
|
||||
dropdownWrapper.find('.ant-select-item-option').at(3).simulate('click');
|
||||
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
|
||||
// resolve Warning: An update to Align ran an effect, but was not wrapped in act(...)
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(container.querySelectorAll('.ant-select-item-option').length).toBe(4);
|
||||
fireEvent.click(container.querySelectorAll('.ant-select-item-option')[3]);
|
||||
expect(onShowSizeChange).toHaveBeenCalledTimes(1);
|
||||
expect(onShowSizeChange).toHaveBeenLastCalledWith(1, 100);
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
@ -425,44 +490,47 @@ describe('Table.pagination', () => {
|
||||
});
|
||||
|
||||
it('should support current in pagination', () => {
|
||||
const wrapper = mount(createTable({ pagination: { current: 2, pageSize: 1 } }));
|
||||
expect(wrapper.find('.ant-pagination-item-active').text()).toBe('2');
|
||||
const { container } = render(createTable({ pagination: { current: 2, pageSize: 1 } }));
|
||||
expect(container.querySelector('.ant-pagination-item-active').textContent).toBe('2');
|
||||
});
|
||||
|
||||
it('should support defaultCurrent in pagination', () => {
|
||||
const wrapper = mount(createTable({ pagination: { defaultCurrent: 2, pageSize: 1 } }));
|
||||
expect(wrapper.find('.ant-pagination-item-active').text()).toBe('2');
|
||||
const { container } = render(createTable({ pagination: { defaultCurrent: 2, pageSize: 1 } }));
|
||||
expect(container.querySelector('.ant-pagination-item-active').textContent).toBe('2');
|
||||
});
|
||||
|
||||
it('should support defaultPageSize in pagination', () => {
|
||||
const wrapper = mount(createTable({ pagination: { defaultPageSize: 1 } }));
|
||||
expect(wrapper.find('.ant-pagination-item')).toHaveLength(4);
|
||||
const { container } = render(createTable({ pagination: { defaultPageSize: 1 } }));
|
||||
expect(container.querySelectorAll('.ant-pagination-item')).toHaveLength(4);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/19957
|
||||
it('ajax should work with pagination', () => {
|
||||
const wrapper = mount(createTable({ pagination: { total: 100 } }));
|
||||
wrapper.find('.ant-pagination-item-2').simulate('click');
|
||||
wrapper.setProps({ pagination: { current: 2, total: 100 } });
|
||||
const { container, rerender } = render(createTable({ pagination: { total: 100 } }));
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-pagination-item-2'));
|
||||
rerender(createTable({ pagination: { current: 2, total: 100 } }));
|
||||
|
||||
expect(
|
||||
wrapper.find('.ant-pagination-item-2').hasClass('ant-pagination-item-active'),
|
||||
container
|
||||
.querySelector('.ant-pagination-item-2')
|
||||
.className.includes('ant-pagination-item-active'),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('pagination should ignore invalidate total', () => {
|
||||
const wrapper = mount(createTable({ pagination: { total: null } }));
|
||||
expect(wrapper.find('.ant-pagination-item-1').length).toBeTruthy();
|
||||
const { container } = render(createTable({ pagination: { total: null } }));
|
||||
expect(container.querySelectorAll('.ant-pagination-item-1').length).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders pagination topLeft and bottomRight', () => {
|
||||
const wrapper = mount(createTable({ pagination: ['topLeft', 'bottomRight'] }));
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { asFragment } = render(createTable({ pagination: ['topLeft', 'bottomRight'] }));
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should call onChange when change pagination size', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
pagination: {
|
||||
total: 200,
|
||||
@ -471,10 +539,8 @@ describe('Table.pagination', () => {
|
||||
onChange,
|
||||
}),
|
||||
);
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
const dropdownWrapper = wrapper.find('Trigger');
|
||||
dropdownWrapper.find('.ant-select-item-option').at(2).simulate('click');
|
||||
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
|
||||
fireEvent.click(container.querySelectorAll('.ant-select-item-option')[2]);
|
||||
expect(onChange).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
@ -490,14 +556,14 @@ describe('Table.pagination', () => {
|
||||
});
|
||||
}
|
||||
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
dataSource: dynamicData,
|
||||
pagination: { total: 100, pageSize: 10, current: 2 },
|
||||
}),
|
||||
);
|
||||
|
||||
expect(wrapper.find('tbody tr')).toHaveLength(5);
|
||||
expect(container.querySelectorAll('tbody tr')).toHaveLength(5);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Table] `dataSource` length is less than `pagination.total` but large than `pagination.pageSize`. Please make sure your config correct data with async mode.',
|
||||
@ -512,17 +578,19 @@ describe('Table.pagination', () => {
|
||||
current: total,
|
||||
position: ['topLeft', 'bottomLeft'],
|
||||
};
|
||||
const wrapper = mount(
|
||||
const { container, rerender } = render(
|
||||
createTable({
|
||||
pagination: paginationProp,
|
||||
}),
|
||||
);
|
||||
rerender(
|
||||
createTable({
|
||||
dataSource: data.slice(total - 1),
|
||||
pagination: { ...paginationProp, total: total - 1 },
|
||||
}),
|
||||
);
|
||||
|
||||
wrapper.setProps({
|
||||
dataSource: data.slice(total - 1),
|
||||
pagination: { ...paginationProp, total: total - 1 },
|
||||
});
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(2);
|
||||
expect(container.querySelectorAll('.ant-pagination')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('showTotal should hide when removed', () => {
|
||||
@ -549,16 +617,16 @@ describe('Table.pagination', () => {
|
||||
);
|
||||
};
|
||||
|
||||
const wrapper = mount(<Demo />);
|
||||
expect(wrapper.find('.ant-pagination-total-text').text()).toEqual('>200<');
|
||||
const { container } = render(<Demo />);
|
||||
expect(container.querySelector('.ant-pagination-total-text').textContent).toEqual('>200<');
|
||||
|
||||
// Should hide
|
||||
wrapper.find('.ant-pagination-item-2').simulate('click');
|
||||
expect(wrapper.find('.ant-pagination-total-text')).toHaveLength(0);
|
||||
fireEvent.click(container.querySelector('.ant-pagination-item-2'));
|
||||
expect(container.querySelectorAll('.ant-pagination-total-text')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should preserve table pagination className', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Table
|
||||
data={[]}
|
||||
columns={[]}
|
||||
@ -570,7 +638,7 @@ describe('Table.pagination', () => {
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find('.ant-pagination').prop('className')).toEqual(
|
||||
expect(container.querySelector('.ant-pagination').className).toEqual(
|
||||
'ant-pagination ant-table-pagination ant-table-pagination-right pagination',
|
||||
);
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React from 'react';
|
||||
import { render, mount } from 'enzyme';
|
||||
import { render, fireEvent, act } from '../../../tests/utils';
|
||||
import Table from '..';
|
||||
|
||||
describe('Table.sorter', () => {
|
||||
@ -20,7 +20,7 @@ describe('Table.sorter', () => {
|
||||
{ key: 3, name: 'Jerry' },
|
||||
];
|
||||
|
||||
function createTable(tableProps, columnProps = {}) {
|
||||
function createTable(tableProps = {}, columnProps = {}) {
|
||||
return (
|
||||
<Table
|
||||
columns={[
|
||||
@ -36,17 +36,24 @@ describe('Table.sorter', () => {
|
||||
);
|
||||
}
|
||||
|
||||
function renderedNames(wrapper) {
|
||||
return wrapper.find('BodyRow').map(row => row.props().record.name);
|
||||
function renderedNames(contain) {
|
||||
const namesList = [];
|
||||
contain
|
||||
.querySelector('.ant-table-tbody')
|
||||
.querySelectorAll('tr')
|
||||
.forEach(tr => {
|
||||
namesList.push(tr.querySelector('td').textContent);
|
||||
});
|
||||
return namesList;
|
||||
}
|
||||
|
||||
it('renders sorter icon correctly', () => {
|
||||
const wrapper = render(createTable());
|
||||
expect(wrapper.find('thead')).toMatchSnapshot();
|
||||
const { container } = render(createTable());
|
||||
expect(container.querySelector('thead')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('default sort order ascend', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable(
|
||||
{},
|
||||
{
|
||||
@ -55,11 +62,11 @@ describe('Table.sorter', () => {
|
||||
),
|
||||
);
|
||||
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
});
|
||||
|
||||
it('default sort order descend', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable(
|
||||
{},
|
||||
{
|
||||
@ -68,14 +75,12 @@ describe('Table.sorter', () => {
|
||||
),
|
||||
);
|
||||
|
||||
const getNameColumn = () => wrapper.find('th').at(0);
|
||||
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('descending');
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(container.querySelector('th').getAttribute('aria-sort')).toEqual('descending');
|
||||
});
|
||||
|
||||
it('should change aria-sort when default sort order is set to descend', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable(
|
||||
{
|
||||
sortDirections: ['descend', 'ascend'],
|
||||
@ -86,63 +91,62 @@ describe('Table.sorter', () => {
|
||||
),
|
||||
);
|
||||
|
||||
const getNameColumn = () => wrapper.find('th').at(0);
|
||||
const getNameColumn = () => container.querySelector('th');
|
||||
|
||||
// Test that it cycles through the order of sortDirections
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('descending');
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('descending');
|
||||
|
||||
wrapper.find('.ant-table-column-sorters').simulate('click');
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('ascending');
|
||||
fireEvent.click(container.querySelector('.ant-table-column-sorters'));
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('ascending');
|
||||
|
||||
wrapper.find('.ant-table-column-sorters').simulate('click');
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
fireEvent.click(container.querySelector('.ant-table-column-sorters'));
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
});
|
||||
|
||||
it('sort records', () => {
|
||||
const wrapper = mount(createTable());
|
||||
|
||||
const getNameColumn = () => wrapper.find('th').at(0);
|
||||
const { container } = render(createTable());
|
||||
const getNameColumn = () => container.querySelector('th');
|
||||
|
||||
// first assert default state
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
|
||||
// ascend
|
||||
wrapper.find('.ant-table-column-sorters').simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('ascending');
|
||||
fireEvent.click(container.querySelector('.ant-table-column-sorters'));
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('ascending');
|
||||
|
||||
// descend
|
||||
wrapper.find('.ant-table-column-sorters').simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('descending');
|
||||
fireEvent.click(container.querySelector('.ant-table-column-sorters'));
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('descending');
|
||||
});
|
||||
|
||||
it('sort records with keydown', () => {
|
||||
const wrapper = mount(createTable());
|
||||
const { container } = render(createTable());
|
||||
|
||||
// ascend
|
||||
wrapper.find('.ant-table-column-sorters').simulate('keydown', { keyCode: 13 });
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
fireEvent.keyDown(container.querySelector('.ant-table-column-sorters'), { keyCode: 13 });
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
|
||||
// descend
|
||||
wrapper.find('.ant-table-column-sorters').simulate('keydown', { keyCode: 13 });
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
fireEvent.keyDown(container.querySelector('.ant-table-column-sorters'), { keyCode: 13 });
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
});
|
||||
|
||||
describe('can be controlled by sortOrder', () => {
|
||||
it('single', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
columns: [{ ...column, sortOrder: 'ascend' }],
|
||||
}),
|
||||
);
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
});
|
||||
|
||||
it('invalidate mix with single & multiple sorters', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
columns: [
|
||||
{
|
||||
@ -163,13 +167,13 @@ describe('Table.sorter', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
});
|
||||
});
|
||||
|
||||
it('provides sortOrder in the sorterFn', () => {
|
||||
let actualSortOrder;
|
||||
mount(
|
||||
render(
|
||||
createTable(
|
||||
{},
|
||||
{
|
||||
@ -185,39 +189,40 @@ describe('Table.sorter', () => {
|
||||
});
|
||||
|
||||
it('can update column sortOrder', () => {
|
||||
const wrapper = mount(
|
||||
const { container, rerender } = render(
|
||||
createTable({
|
||||
columns: [column],
|
||||
}),
|
||||
);
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
wrapper.setProps({
|
||||
columns: [{ ...column, sortOrder: 'ascend' }],
|
||||
});
|
||||
wrapper.update();
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
rerender(
|
||||
createTable({
|
||||
columns: [{ ...column, sortOrder: 'ascend' }],
|
||||
}),
|
||||
);
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
});
|
||||
|
||||
it('fires change event', () => {
|
||||
const handleChange = jest.fn();
|
||||
const wrapper = mount(createTable({ onChange: handleChange }));
|
||||
const { container } = render(createTable({ onChange: handleChange }));
|
||||
|
||||
// ascent
|
||||
wrapper.find('.ant-table-column-sorters').simulate('click');
|
||||
fireEvent.click(container.querySelector('.ant-table-column-sorters'));
|
||||
const sorter1 = handleChange.mock.calls[0][2];
|
||||
expect(sorter1.column.dataIndex).toBe('name');
|
||||
expect(sorter1.order).toBe('ascend');
|
||||
expect(sorter1.field).toBe('name');
|
||||
expect(sorter1.columnKey).toBe('name');
|
||||
|
||||
wrapper.find('.ant-table-column-sorters').simulate('click');
|
||||
fireEvent.click(container.querySelector('.ant-table-column-sorters'));
|
||||
const sorter2 = handleChange.mock.calls[1][2];
|
||||
expect(sorter2.column.dataIndex).toBe('name');
|
||||
expect(sorter2.order).toBe('descend');
|
||||
expect(sorter2.field).toBe('name');
|
||||
expect(sorter2.columnKey).toBe('name');
|
||||
|
||||
wrapper.find('.ant-table-column-sorters').simulate('click');
|
||||
fireEvent.click(container.querySelector('.ant-table-column-sorters'));
|
||||
const sorter3 = handleChange.mock.calls[2][2];
|
||||
expect(sorter3.column).toBe(undefined);
|
||||
expect(sorter3.order).toBe(undefined);
|
||||
@ -228,71 +233,98 @@ describe('Table.sorter', () => {
|
||||
it('hover header show sorter tooltip', () => {
|
||||
// tooltip has delay
|
||||
jest.useFakeTimers();
|
||||
const wrapper = mount(createTable({}));
|
||||
const { container, rerender } = render(createTable());
|
||||
|
||||
// default show sorter tooltip
|
||||
wrapper.find('.ant-table-column-sorters').simulate('mouseenter');
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-tooltip-open').length).toBeTruthy();
|
||||
wrapper.find('.ant-table-column-sorters').simulate('mouseout');
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-table-column-sorters'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(container.querySelector('.ant-tooltip-open')).toBeTruthy();
|
||||
fireEvent.mouseOut(container.querySelector('.ant-table-column-sorters'));
|
||||
|
||||
// set table props showSorterTooltip is false
|
||||
wrapper.setProps({ showSorterTooltip: false });
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-tooltip-open')).toHaveLength(0);
|
||||
rerender(createTable({ showSorterTooltip: false }));
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-table-column-sorters'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(container.querySelector('.ant-tooltip-open')).toBeFalsy();
|
||||
fireEvent.mouseOut(container.querySelector('.ant-table-column-sorters'));
|
||||
|
||||
// set table props showSorterTooltip is false, column showSorterTooltip is true
|
||||
wrapper.setProps({
|
||||
showSorterTooltip: false,
|
||||
columns: [{ ...column, showSorterTooltip: true }],
|
||||
rerender(
|
||||
createTable({ showSorterTooltip: true, columns: [{ ...column, showSorterTooltip: true }] }),
|
||||
);
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-table-column-sorters'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.find('.ant-table-column-sorters').simulate('mouseenter');
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-tooltip-open').length).toBeTruthy();
|
||||
wrapper.find('.ant-table-column-sorters').simulate('mouseout');
|
||||
expect(container.querySelector('.ant-tooltip-open')).toBeTruthy();
|
||||
fireEvent.mouseOut(container.querySelector('.ant-table-column-sorters'));
|
||||
|
||||
// set table props showSorterTooltip is true, column showSorterTooltip is false
|
||||
wrapper.setProps({
|
||||
showSorterTooltip: true,
|
||||
columns: [{ ...column, showSorterTooltip: false }],
|
||||
rerender(
|
||||
createTable({
|
||||
showSorterTooltip: true,
|
||||
columns: [{ ...column, showSorterTooltip: false }],
|
||||
}),
|
||||
);
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-table-column-sorters'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-tooltip-open')).toHaveLength(0);
|
||||
expect(container.querySelector('.ant-tooltip-open')).toBeFalsy();
|
||||
fireEvent.mouseOut(container.querySelector('.ant-table-column-sorters'));
|
||||
});
|
||||
|
||||
it('should show correct tooltip when showSorterTooltip is an object', () => {
|
||||
// basically copied from 'hover header show sorter tooltip'
|
||||
jest.useFakeTimers();
|
||||
const wrapper = mount(
|
||||
const { container, rerender } = render(
|
||||
createTable({ showSorterTooltip: { placement: 'bottom', title: 'static title' } }),
|
||||
);
|
||||
wrapper.find('.ant-table-column-sorters').simulate('mouseenter');
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-tooltip-open').length).toBeTruthy();
|
||||
wrapper.find('.ant-table-column-sorters').simulate('mouseout');
|
||||
|
||||
wrapper.setProps({ showSorterTooltip: false });
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-tooltip-open')).toHaveLength(0);
|
||||
wrapper.setProps({
|
||||
showSorterTooltip: false,
|
||||
columns: [{ ...column, showSorterTooltip: true }],
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-table-column-sorters'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.find('.ant-table-column-sorters').simulate('mouseenter');
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-tooltip-open').length).toBeTruthy();
|
||||
wrapper.find('.ant-table-column-sorters').simulate('mouseout');
|
||||
wrapper.setProps({
|
||||
showSorterTooltip: true,
|
||||
columns: [{ ...column, showSorterTooltip: false }],
|
||||
expect(container.querySelector('.ant-tooltip-open')).toBeTruthy();
|
||||
fireEvent.mouseOut(container.querySelector('.ant-table-column-sorters'));
|
||||
|
||||
// Root to false
|
||||
rerender(createTable({ showSorterTooltip: false }));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-tooltip-open')).toHaveLength(0);
|
||||
expect(container.querySelector('.ant-tooltip-open')).toBeFalsy();
|
||||
|
||||
// Column to true
|
||||
rerender(
|
||||
createTable({
|
||||
showSorterTooltip: false,
|
||||
columns: [{ ...column, showSorterTooltip: true }],
|
||||
}),
|
||||
);
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-table-column-sorters'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(container.querySelector('.ant-tooltip-open')).toBeTruthy();
|
||||
fireEvent.mouseOut(container.querySelector('.ant-table-column-sorters'));
|
||||
|
||||
// Column to false
|
||||
rerender(
|
||||
createTable({
|
||||
showSorterTooltip: true,
|
||||
columns: [{ ...column, showSorterTooltip: false }],
|
||||
}),
|
||||
);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(container.querySelector('.ant-tooltip-open')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('works with grouping columns in controlled mode', () => {
|
||||
@ -322,9 +354,9 @@ describe('Table.sorter', () => {
|
||||
{ key: 2, name: 'Tom', age: 21 },
|
||||
{ key: 3, name: 'Jerry', age: 22 },
|
||||
];
|
||||
const wrapper = mount(<Table columns={columns} dataSource={testData} />);
|
||||
const { container } = render(<Table columns={columns} dataSource={testData} />);
|
||||
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/11246#issuecomment-405009167
|
||||
@ -343,12 +375,12 @@ describe('Table.sorter', () => {
|
||||
{ key: 2, name: 'Tom', age: 21 },
|
||||
{ key: 3, name: 'Jerry', age: 22 },
|
||||
];
|
||||
const wrapper = mount(<Table columns={columns} dataSource={testData} />);
|
||||
expect(wrapper.find('.custom-title').text()).toEqual('');
|
||||
wrapper.find('.ant-table-column-sorters').simulate('click');
|
||||
expect(wrapper.find('.custom-title').text()).toEqual('ascend');
|
||||
wrapper.find('.ant-table-column-sorters').simulate('click');
|
||||
expect(wrapper.find('.custom-title').text()).toEqual('descend');
|
||||
const { container } = render(<Table columns={columns} dataSource={testData} />);
|
||||
expect(container.querySelector('.custom-title').textContent).toEqual('');
|
||||
fireEvent.click(container.querySelector('.ant-table-column-sorters'));
|
||||
expect(container.querySelector('.custom-title').textContent).toEqual('ascend');
|
||||
fireEvent.click(container.querySelector('.ant-table-column-sorters'));
|
||||
expect(container.querySelector('.custom-title').textContent).toEqual('descend');
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/pull/12264#discussion_r218053034
|
||||
@ -371,26 +403,26 @@ describe('Table.sorter', () => {
|
||||
{ key: 2, name: 'Tom', age: 21 },
|
||||
{ key: 3, name: 'Jerry', age: 22 },
|
||||
];
|
||||
const wrapper = mount(<Table columns={columns} dataSource={testData} />);
|
||||
const { container } = render(<Table columns={columns} dataSource={testData} />);
|
||||
|
||||
const getNameColumn = () => wrapper.find('.ant-table-column-has-sorters').at(0);
|
||||
const getAgeColumn = () => wrapper.find('.ant-table-column-has-sorters').at(1);
|
||||
const getNameIcon = name => getNameColumn().find(`.ant-table-column-sorter-${name}`).first();
|
||||
const getAgeIcon = name => getAgeColumn().find(`.ant-table-column-sorter-${name}`).first();
|
||||
const getNameColumn = () => container.querySelectorAll('.ant-table-column-has-sorters')[0];
|
||||
const getAgeColumn = () => container.querySelectorAll('.ant-table-column-has-sorters')[1];
|
||||
const getNameIcon = name => getNameColumn().querySelector(`.ant-table-column-sorter-${name}`);
|
||||
const getAgeIcon = name => getAgeColumn().querySelector(`.ant-table-column-sorter-${name}`);
|
||||
|
||||
// sort name
|
||||
getNameColumn().simulate('click');
|
||||
expect(getNameIcon('up').hasClass('active')).toBeTruthy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('ascending');
|
||||
expect(getAgeIcon('up').hasClass('active')).toBeFalsy();
|
||||
expect(getAgeColumn().prop('aria-sort')).toEqual(undefined);
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(getNameIcon('up').className.includes('active')).toBeTruthy();
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('ascending');
|
||||
expect(getAgeIcon('up').className.includes('active')).toBeFalsy();
|
||||
expect(getAgeColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
|
||||
// sort age
|
||||
getAgeColumn().simulate('click');
|
||||
expect(getNameIcon('up').hasClass('active')).toBeFalsy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
expect(getAgeIcon('up').hasClass('active')).toBeTruthy();
|
||||
expect(getAgeColumn().prop('aria-sort')).toEqual('ascending');
|
||||
fireEvent.click(getAgeColumn());
|
||||
expect(getNameIcon('up').className.includes('active')).toBeFalsy();
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
expect(getAgeIcon('up').className.includes('active')).toBeTruthy();
|
||||
expect(getAgeColumn().getAttribute('aria-sort')).toEqual('ascending');
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/12571
|
||||
@ -430,31 +462,31 @@ describe('Table.sorter', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const wrapper = mount(<TableTest />);
|
||||
const { container } = render(<TableTest />);
|
||||
|
||||
const getNameColumn = () => wrapper.find('th').at(0);
|
||||
const getIcon = name => getNameColumn().find(`.ant-table-column-sorter-${name}`).first();
|
||||
const getNameColumn = () => container.querySelector('th');
|
||||
const getIcon = name => getNameColumn().querySelector(`.ant-table-column-sorter-${name}`);
|
||||
|
||||
expect(getIcon('up').hasClass('active')).toBeFalsy();
|
||||
expect(getIcon('down').hasClass('active')).toBeFalsy();
|
||||
expect(getIcon('up').className.includes('active')).toBeFalsy();
|
||||
expect(getIcon('down').className.includes('active')).toBeFalsy();
|
||||
|
||||
// sort name
|
||||
getNameColumn().simulate('click');
|
||||
expect(getIcon('up').hasClass('active')).toBeTruthy();
|
||||
expect(getIcon('down').hasClass('active')).toBeFalsy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('ascending');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(getIcon('up').className.includes('active')).toBeTruthy();
|
||||
expect(getIcon('down').className.includes('active')).toBeFalsy();
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('ascending');
|
||||
|
||||
// sort name
|
||||
getNameColumn().simulate('click');
|
||||
expect(getIcon('up').hasClass('active')).toBeFalsy();
|
||||
expect(getIcon('down').hasClass('active')).toBeTruthy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('descending');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(getIcon('up').className.includes('active')).toBeFalsy();
|
||||
expect(getIcon('down').className.includes('active')).toBeTruthy();
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('descending');
|
||||
|
||||
// sort name
|
||||
getNameColumn().simulate('click');
|
||||
expect(getIcon('up').hasClass('active')).toBeFalsy();
|
||||
expect(getIcon('down').hasClass('active')).toBeFalsy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(getIcon('up').className.includes('active')).toBeFalsy();
|
||||
expect(getIcon('down').className.includes('active')).toBeFalsy();
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/12737
|
||||
@ -497,31 +529,31 @@ describe('Table.sorter', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const wrapper = mount(<TableTest />);
|
||||
const { container } = render(<TableTest />);
|
||||
|
||||
const getNameColumn = () => wrapper.find('th').at(0);
|
||||
const getIcon = name => getNameColumn().find(`.ant-table-column-sorter-${name}`).first();
|
||||
const getNameColumn = () => container.querySelector('th');
|
||||
const getIcon = name => getNameColumn().querySelector(`.ant-table-column-sorter-${name}`);
|
||||
|
||||
expect(getIcon('up').hasClass('active')).toBeFalsy();
|
||||
expect(getIcon('down').hasClass('active')).toBeFalsy();
|
||||
expect(getIcon('up').className.includes('active')).toBeFalsy();
|
||||
expect(getIcon('down').className.includes('active')).toBeFalsy();
|
||||
|
||||
// sort name
|
||||
getNameColumn().simulate('click');
|
||||
expect(getIcon('up').hasClass('active')).toBeTruthy();
|
||||
expect(getIcon('down').hasClass('active')).toBeFalsy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('ascending');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(getIcon('up').className.includes('active')).toBeTruthy();
|
||||
expect(getIcon('down').className.includes('active')).toBeFalsy();
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('ascending');
|
||||
|
||||
// sort name
|
||||
getNameColumn().simulate('click');
|
||||
expect(getIcon('up').hasClass('active')).toBeFalsy();
|
||||
expect(getIcon('down').hasClass('active')).toBeTruthy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('descending');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(getIcon('up').className.includes('active')).toBeFalsy();
|
||||
expect(getIcon('down').className.includes('active')).toBeTruthy();
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('descending');
|
||||
|
||||
// sort name
|
||||
getNameColumn().simulate('click');
|
||||
expect(getIcon('up').hasClass('active')).toBeFalsy();
|
||||
expect(getIcon('down').hasClass('active')).toBeFalsy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(getIcon('up').className.includes('active')).toBeFalsy();
|
||||
expect(getIcon('down').className.includes('active')).toBeFalsy();
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/12870
|
||||
@ -565,96 +597,96 @@ describe('Table.sorter', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const wrapper = mount(<TableTest />);
|
||||
const getNameColumn = () => wrapper.find('th').at(0);
|
||||
const { container } = render(<TableTest />);
|
||||
const getNameColumn = () => container.querySelector('th');
|
||||
expect(
|
||||
getNameColumn().find('.ant-table-column-sorter-up').at(0).hasClass('active'),
|
||||
getNameColumn().querySelector('.ant-table-column-sorter-up').className.includes('active'),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
getNameColumn().find('.ant-table-column-sorter-down').at(0).hasClass('active'),
|
||||
getNameColumn().querySelector('.ant-table-column-sorter-down').className.includes('active'),
|
||||
).toBeFalsy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
|
||||
// sort name
|
||||
getNameColumn().simulate('click');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(
|
||||
getNameColumn().find('.ant-table-column-sorter-up').at(0).hasClass('active'),
|
||||
getNameColumn().querySelector('.ant-table-column-sorter-up').className.includes('active'),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
getNameColumn().find('.ant-table-column-sorter-down').at(0).hasClass('active'),
|
||||
getNameColumn().querySelector('.ant-table-column-sorter-down').className.includes('active'),
|
||||
).toBeFalsy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('ascending');
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('ascending');
|
||||
|
||||
// sort name
|
||||
getNameColumn().simulate('click');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(
|
||||
getNameColumn().find('.ant-table-column-sorter-up').at(0).hasClass('active'),
|
||||
getNameColumn().querySelector('.ant-table-column-sorter-up').className.includes('active'),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
getNameColumn().find('.ant-table-column-sorter-down').at(0).hasClass('active'),
|
||||
getNameColumn().querySelector('.ant-table-column-sorter-down').className.includes('active'),
|
||||
).toBeTruthy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('descending');
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('descending');
|
||||
|
||||
// sort name
|
||||
getNameColumn().simulate('click');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(
|
||||
getNameColumn().find('.ant-table-column-sorter-up').at(0).hasClass('active'),
|
||||
getNameColumn().querySelector('.ant-table-column-sorter-up').className.includes('active'),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
getNameColumn().find('.ant-table-column-sorter-down').at(0).hasClass('active'),
|
||||
getNameColumn().querySelector('.ant-table-column-sorter-down').className.includes('active'),
|
||||
).toBeFalsy();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
});
|
||||
|
||||
it('should first sort by descend, then ascend, then cancel sort', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
sortDirections: ['descend', 'ascend'],
|
||||
}),
|
||||
);
|
||||
const getNameColumn = () => wrapper.find('th').at(0);
|
||||
const getNameColumn = () => container.querySelector('th');
|
||||
|
||||
// descend
|
||||
getNameColumn().simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('descending');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('descending');
|
||||
|
||||
// ascend
|
||||
getNameColumn().simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('ascending');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('ascending');
|
||||
|
||||
// cancel sort
|
||||
getNameColumn().simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
});
|
||||
|
||||
it('should first sort by descend, then cancel sort', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
sortDirections: ['descend'],
|
||||
}),
|
||||
);
|
||||
|
||||
const getNameColumn = () => wrapper.find('th').at(0);
|
||||
const getNameColumn = () => container.querySelector('th');
|
||||
|
||||
// default
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
|
||||
// descend
|
||||
getNameColumn().simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('descending');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('descending');
|
||||
|
||||
// cancel sort
|
||||
getNameColumn().simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
});
|
||||
|
||||
it('should first sort by descend, then cancel sort. (column prop)', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable(
|
||||
{},
|
||||
{
|
||||
@ -663,27 +695,27 @@ describe('Table.sorter', () => {
|
||||
),
|
||||
);
|
||||
|
||||
const getNameColumn = () => wrapper.find('th').at(0);
|
||||
const getNameColumn = () => container.querySelector('th');
|
||||
|
||||
// default
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
|
||||
// descend
|
||||
getNameColumn().simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('descending');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(renderedNames(container)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('descending');
|
||||
|
||||
// cancel sort
|
||||
getNameColumn().simulate('click');
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
});
|
||||
|
||||
it('pagination back', () => {
|
||||
const onPageChange = jest.fn();
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable({
|
||||
pagination: {
|
||||
pageSize: 2,
|
||||
@ -694,27 +726,27 @@ describe('Table.sorter', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const getNameColumn = () => wrapper.find('th').at(0);
|
||||
const getNameColumn = () => container.querySelector('th');
|
||||
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual(undefined);
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual(null);
|
||||
|
||||
getNameColumn().simulate('click');
|
||||
fireEvent.click(getNameColumn());
|
||||
expect(onChange.mock.calls[0][0].current).toBe(2);
|
||||
expect(onPageChange).not.toHaveBeenCalled();
|
||||
expect(getNameColumn().prop('aria-sort')).toEqual('ascending');
|
||||
expect(getNameColumn().getAttribute('aria-sort')).toEqual('ascending');
|
||||
});
|
||||
|
||||
it('should support onHeaderCell in sort column', () => {
|
||||
const onClick = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Table columns={[{ title: 'title', onHeaderCell: () => ({ onClick }), sorter: true }]} />,
|
||||
);
|
||||
wrapper.find('th').simulate('click');
|
||||
fireEvent.click(container.querySelector('th'));
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('could sort data with children', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTable(
|
||||
{
|
||||
defaultExpandAllRows: true,
|
||||
@ -755,7 +787,7 @@ describe('Table.sorter', () => {
|
||||
),
|
||||
);
|
||||
|
||||
expect(renderedNames(wrapper)).toEqual(['Brown', 'Green', 'Mike', 'Alex', 'Petter', 'Zoe']);
|
||||
expect(renderedNames(container)).toEqual(['Brown', 'Green', 'Mike', 'Alex', 'Petter', 'Zoe']);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/19443
|
||||
@ -774,22 +806,22 @@ describe('Table.sorter', () => {
|
||||
}
|
||||
}
|
||||
expect(() => {
|
||||
mount(<Demo />);
|
||||
render(<Demo />);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should support defaultOrder in Column', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Table dataSource={[{ key: '1', age: 1 }]}>
|
||||
<Table.Column title="Age" dataIndex="age" sorter defaultSortOrder="ascend" key="age" />
|
||||
</Table>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/20096
|
||||
it('invalidate sorter should not display sorter button', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Table
|
||||
columns={[
|
||||
{
|
||||
@ -814,12 +846,12 @@ describe('Table.sorter', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.ant-table-column-sorter-inner')).toHaveLength(0);
|
||||
expect(container.querySelectorAll('.ant-table-column-sorter-inner')).toHaveLength(0);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/21193
|
||||
it('table with sugar column', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Table>
|
||||
<Table.Column
|
||||
title="Chinese Score"
|
||||
@ -840,20 +872,24 @@ describe('Table.sorter', () => {
|
||||
</Table>,
|
||||
);
|
||||
|
||||
wrapper.find('th').first().simulate('click');
|
||||
fireEvent.click(container.querySelector('th'));
|
||||
|
||||
expect(wrapper.find('th.ant-table-column-sort')).toHaveLength(1);
|
||||
expect(container.querySelectorAll('th.ant-table-column-sort')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('surger should support sorterOrder', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Table>
|
||||
<Table.Column key="name" title="Name" dataIndex="name" sortOrder="ascend" sorter />
|
||||
</Table>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.ant-table-column-sorter-up').last().hasClass('active')).toBeTruthy();
|
||||
expect(wrapper.find('.ant-table-column-sorter-down').last().hasClass('active')).toBeFalsy();
|
||||
expect(
|
||||
container.querySelector('.ant-table-column-sorter-up').className.includes('active'),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
container.querySelector('.ant-table-column-sorter-down').className.includes('active'),
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('controlled multiple group', () => {
|
||||
@ -888,23 +924,19 @@ describe('Table.sorter', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const wrapper = mount(<Table columns={groupColumns} data={groupData} />);
|
||||
wrapper.update();
|
||||
const { container } = render(<Table columns={groupColumns} data={groupData} />);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-table-column-sorter-full')
|
||||
.first()
|
||||
.find('.ant-table-column-sorter-up')
|
||||
.first()
|
||||
.hasClass('active'),
|
||||
container
|
||||
.querySelectorAll('.ant-table-column-sorter-full')[0]
|
||||
.querySelector('.ant-table-column-sorter-up')
|
||||
.className.includes('active'),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-table-column-sorter-full')
|
||||
.last()
|
||||
.find('.ant-table-column-sorter-down')
|
||||
.first()
|
||||
.hasClass('active'),
|
||||
container
|
||||
.querySelectorAll('.ant-table-column-sorter-full')[1]
|
||||
.querySelector('.ant-table-column-sorter-down')
|
||||
.className.includes('active'),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
@ -933,10 +965,12 @@ describe('Table.sorter', () => {
|
||||
];
|
||||
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(<Table columns={groupColumns} data={groupData} onChange={onChange} />);
|
||||
const { container } = render(
|
||||
<Table columns={groupColumns} data={groupData} onChange={onChange} />,
|
||||
);
|
||||
|
||||
function clickToMatchExpect(index, sorter) {
|
||||
wrapper.find('.ant-table-column-sorters').at(index).simulate('click');
|
||||
fireEvent.click(container.querySelectorAll('.ant-table-column-sorters')[index]);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
|
@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Table from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { sleep, render } from '../../../tests/utils';
|
||||
import { sleep, render, fireEvent } from '../../../tests/utils';
|
||||
|
||||
const { Column, ColumnGroup } = Table;
|
||||
|
||||
@ -33,7 +32,7 @@ describe('Table', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Table dataSource={data} pagination={false}>
|
||||
<ColumnGroup title="Name">
|
||||
<Column title="First Name" dataIndex="firstName" key="firstName" />
|
||||
@ -45,7 +44,7 @@ describe('Table', () => {
|
||||
</Table>,
|
||||
);
|
||||
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('updates columns when receiving props', () => {
|
||||
@ -56,7 +55,8 @@ describe('Table', () => {
|
||||
dataIndex: 'name',
|
||||
},
|
||||
];
|
||||
const wrapper = mount(<Table columns={columns} />);
|
||||
const { container, rerender } = render(<Table columns={columns} />);
|
||||
|
||||
const newColumns = [
|
||||
{
|
||||
title: 'Title',
|
||||
@ -64,9 +64,8 @@ describe('Table', () => {
|
||||
dataIndex: 'title',
|
||||
},
|
||||
];
|
||||
wrapper.setProps({ columns: newColumns });
|
||||
|
||||
expect(wrapper.find('th').text()).toEqual('Title');
|
||||
rerender(<Table columns={newColumns} />);
|
||||
expect(container.querySelector('th').textContent).toEqual('Title');
|
||||
});
|
||||
|
||||
it('loading with Spin', async () => {
|
||||
@ -74,32 +73,34 @@ describe('Table', () => {
|
||||
spinning: false,
|
||||
delay: 500,
|
||||
};
|
||||
const wrapper = mount(<Table loading={loading} />);
|
||||
expect(wrapper.find('.ant-spin')).toHaveLength(0);
|
||||
expect(wrapper.find('.ant-table-placeholder').hostNodes().text()).not.toEqual('');
|
||||
const { container, rerender } = render(<Table loading={loading} />);
|
||||
expect(container.querySelectorAll('.ant-spin')).toHaveLength(0);
|
||||
expect(container.querySelector('.ant-table-placeholder').textContent).not.toEqual('');
|
||||
|
||||
loading.spinning = true;
|
||||
wrapper.setProps({ loading });
|
||||
expect(wrapper.find('.ant-spin')).toHaveLength(0);
|
||||
rerender(<Table loading={loading} />);
|
||||
expect(container.querySelectorAll('.ant-spin')).toHaveLength(0);
|
||||
await sleep(500);
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-spin')).toHaveLength(1);
|
||||
rerender(<Table loading />);
|
||||
expect(container.querySelectorAll('.ant-spin')).toHaveLength(1);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/22733
|
||||
it('support loading tip', async () => {
|
||||
const wrapper = mount(<Table loading={{ tip: 'loading...' }} />);
|
||||
const { container, rerender } = render(<Table loading={{ tip: 'loading...' }} />);
|
||||
await sleep(500);
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-spin')).toHaveLength(1);
|
||||
rerender(<Table loading={{ tip: 'loading...', loading: true }} />);
|
||||
expect(container.querySelectorAll('.ant-spin')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders custom components correctly when it changes', () => {
|
||||
const BodyWrapper1 = props => <tbody id="wrapper1" {...props} />;
|
||||
const BodyWrapper2 = props => <tbody id="wrapper2" {...props} />;
|
||||
const wrapper = mount(<Table components={{ body: { wrapper: BodyWrapper1 } }} />);
|
||||
wrapper.setProps({ components: { body: { wrapper: BodyWrapper2 } } });
|
||||
expect(wrapper.find('tbody').props().id).toBe('wrapper2');
|
||||
const { container, rerender } = render(
|
||||
<Table components={{ body: { wrapper: BodyWrapper1 } }} />,
|
||||
);
|
||||
rerender(<Table components={{ body: { wrapper: BodyWrapper2 } }} />);
|
||||
expect(container.querySelector('tbody').id).toBe('wrapper2');
|
||||
});
|
||||
|
||||
it('props#columnsPageRange and props#columnsPageSize do not warn anymore', () => {
|
||||
@ -118,7 +119,7 @@ describe('Table', () => {
|
||||
|
||||
const columnsPageRange = jest.fn();
|
||||
const columnsPageSize = jest.fn();
|
||||
mount(
|
||||
render(
|
||||
<Table
|
||||
dataSource={data}
|
||||
rowkey="key"
|
||||
@ -139,15 +140,15 @@ describe('Table', () => {
|
||||
|
||||
it('support onHeaderCell', () => {
|
||||
const onClick = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Table columns={[{ title: 'title', onHeaderCell: () => ({ onClick }) }]} />,
|
||||
);
|
||||
wrapper.find('th').simulate('click');
|
||||
fireEvent.click(container.querySelector('th'));
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not crash when column children is empty', () => {
|
||||
mount(
|
||||
render(
|
||||
<Table
|
||||
columns={[
|
||||
{
|
||||
@ -161,7 +162,7 @@ describe('Table', () => {
|
||||
});
|
||||
|
||||
it('should not crash when dataSource is array with none-object items', () => {
|
||||
mount(
|
||||
render(
|
||||
<Table
|
||||
columns={[
|
||||
{
|
||||
@ -174,7 +175,9 @@ describe('Table', () => {
|
||||
});
|
||||
|
||||
it('prevent touch event', () => {
|
||||
const wrapper = mount(
|
||||
// prevent touch event, 原来的用例感觉是少了 touchmove 调用判断
|
||||
const touchmove = jest.fn();
|
||||
const { container } = render(
|
||||
<Table
|
||||
columns={[
|
||||
{
|
||||
@ -185,7 +188,8 @@ describe('Table', () => {
|
||||
dataSource={[]}
|
||||
/>,
|
||||
);
|
||||
wrapper.find('.ant-table-wrapper').simulate('touchmove');
|
||||
fireEvent.touchMove(container.querySelector('.ant-table'));
|
||||
expect(touchmove).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders ellipsis by showTitle option', () => {
|
||||
@ -203,9 +207,9 @@ describe('Table', () => {
|
||||
{ title: 'id', dataKey: 'id', ellipsis: { showTitle: false } },
|
||||
{ title: 'age', dataKey: 'age', ellipsis: { showTitle: false } },
|
||||
];
|
||||
const wrapper = mount(<Table columns={columns} dataSource={data} />);
|
||||
wrapper.find('td').forEach(td => {
|
||||
expect(td.hasClass('ant-table-cell-ellipsis')).toBeTruthy();
|
||||
const { container } = render(<Table columns={columns} dataSource={data} />);
|
||||
container.querySelectorAll('td').forEach(td => {
|
||||
expect(td.className.includes('ant-table-cell-ellipsis')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@ -224,14 +228,13 @@ describe('Table', () => {
|
||||
{ title: 'id', dataKey: 'id', ellipsis: { showTitle: true } },
|
||||
{ title: 'age', dataKey: 'age', ellipsis: { showTitle: true } },
|
||||
];
|
||||
const wrapper = mount(<Table columns={columns} dataSource={data} />);
|
||||
|
||||
wrapper.find('.ant-table-thead th').forEach(td => {
|
||||
expect(td.getDOMNode().attributes.getNamedItem('title')).toBeTruthy();
|
||||
const { container } = render(<Table columns={columns} dataSource={data} />);
|
||||
container.querySelectorAll('.ant-table-thead th').forEach(td => {
|
||||
expect(td.attributes.title).toBeTruthy();
|
||||
});
|
||||
|
||||
wrapper.find('.ant-table-tbody td').forEach(td => {
|
||||
expect(td.getDOMNode().attributes.getNamedItem('title')).toBeFalsy();
|
||||
container.querySelectorAll('.ant-table-tbody td').forEach(td => {
|
||||
expect(td.attributes.title).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
@ -244,7 +247,7 @@ describe('Table', () => {
|
||||
dataIndex: 'name',
|
||||
},
|
||||
];
|
||||
mount(<Table columns={columns} rowKey={(record, index) => record + index} />);
|
||||
render(<Table columns={columns} rowKey={(record, index) => record + index} />);
|
||||
expect(warnSpy).toBeCalledWith(
|
||||
'Warning: [antd: Table] `index` parameter of `rowKey` function is deprecated. There is no guarantee that it will work as expected.',
|
||||
);
|
||||
|
@ -2,54 +2,46 @@
|
||||
|
||||
exports[`Table.filter clearFilters should support params 1`] = `
|
||||
<div
|
||||
className="ant-dropdown-custom-view"
|
||||
class="ant-dropdown-custom-view"
|
||||
id="customFilter"
|
||||
>
|
||||
<span
|
||||
id="setNoParams"
|
||||
onClick={[Function]}
|
||||
>
|
||||
setSelectedKeys
|
||||
</span>
|
||||
<span
|
||||
id="resetNoParams"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Reset
|
||||
</span>
|
||||
<span
|
||||
id="setConfirm"
|
||||
onClick={[Function]}
|
||||
>
|
||||
setSelectedKeys
|
||||
</span>
|
||||
<span
|
||||
id="resetConfirm"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Reset
|
||||
</span>
|
||||
<span
|
||||
id="setClose"
|
||||
onClick={[Function]}
|
||||
>
|
||||
setSelectedKeys
|
||||
</span>
|
||||
<span
|
||||
id="resetClose"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Reset
|
||||
</span>
|
||||
<span
|
||||
id="setParams"
|
||||
onClick={[Function]}
|
||||
>
|
||||
setSelectedKeys
|
||||
</span>
|
||||
<span
|
||||
id="resetParams"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Reset
|
||||
</span>
|
||||
@ -58,37 +50,43 @@ exports[`Table.filter clearFilters should support params 1`] = `
|
||||
|
||||
exports[`Table.filter override custom filter correctly 1`] = `
|
||||
<div
|
||||
className="ant-dropdown-custom-view"
|
||||
class="ant-dropdown-custom-view"
|
||||
id="customFilter"
|
||||
>
|
||||
<span
|
||||
id="setSelectedKeys"
|
||||
onClick={[Function]}
|
||||
>
|
||||
setSelectedKeys
|
||||
</span>
|
||||
<span
|
||||
id="confirm"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Confirm
|
||||
</span>
|
||||
<span
|
||||
id="reset"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Reset
|
||||
</span>
|
||||
<span
|
||||
id="simulateOnSelect"
|
||||
onClick={[Function]}
|
||||
>
|
||||
SimulateOnSelect
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table.filter renders custom content correctly 1`] = `<div />`;
|
||||
exports[`Table.filter renders custom content correctly 1`] = `
|
||||
<div
|
||||
class="ant-table-filter-dropdown"
|
||||
>
|
||||
<div
|
||||
class="custom-filter-dropdown"
|
||||
>
|
||||
custom filter
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table.filter renders custom filter icon as ReactNode 1`] = `
|
||||
<div
|
||||
@ -304,14 +302,6 @@ exports[`Table.filter renders custom filter icon correctly 1`] = `
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`Table.filter renders custom filter icon correctly 2`] = `
|
||||
<span
|
||||
class="customize-icon"
|
||||
>
|
||||
unfiltered
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`Table.filter renders custom filter icon with right Tooltip title 1`] = `
|
||||
<div
|
||||
class="ant-table-wrapper"
|
||||
@ -562,6 +552,286 @@ exports[`Table.filter renders filter correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table.filter renders menu correctly 1`] = `<div />`;
|
||||
exports[`Table.filter renders menu correctly 1`] = `
|
||||
<div
|
||||
class="ant-table-filter-dropdown"
|
||||
>
|
||||
<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"
|
||||
data-menu-id="rc-menu-uuid-test-boy"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span>
|
||||
Boy
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-dropdown-menu-item"
|
||||
data-menu-id="rc-menu-uuid-test-girl"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span>
|
||||
Girl
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-dropdown-menu-submenu ant-dropdown-menu-submenu-vertical"
|
||||
role="none"
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-menu-uuid-test-title-popup"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
class="ant-dropdown-menu-submenu-title"
|
||||
data-menu-id="rc-menu-uuid-test-title"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
Title
|
||||
</span>
|
||||
<span
|
||||
class="ant-dropdown-menu-submenu-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-dropdown-menu-submenu-arrow-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
style="display: none;"
|
||||
/>
|
||||
<div
|
||||
class="ant-table-filter-dropdown-btns"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-link ant-btn-sm"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Reset
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-sm"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
OK
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table.filter renders radio filter correctly 1`] = `<div />`;
|
||||
exports[`Table.filter renders radio filter correctly 1`] = `
|
||||
<div
|
||||
class="ant-table-filter-dropdown"
|
||||
>
|
||||
<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"
|
||||
data-menu-id="rc-menu-uuid-test-boy"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span>
|
||||
Boy
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-dropdown-menu-item"
|
||||
data-menu-id="rc-menu-uuid-test-girl"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span>
|
||||
Girl
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-dropdown-menu-submenu ant-dropdown-menu-submenu-vertical"
|
||||
role="none"
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-menu-uuid-test-title-popup"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
class="ant-dropdown-menu-submenu-title"
|
||||
data-menu-id="rc-menu-uuid-test-title"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
Title
|
||||
</span>
|
||||
<span
|
||||
class="ant-dropdown-menu-submenu-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-dropdown-menu-submenu-arrow-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
style="display: none;"
|
||||
/>
|
||||
<div
|
||||
class="ant-table-filter-dropdown-btns"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-link ant-btn-sm"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Reset
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-sm"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
OK
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -20,7 +20,7 @@ exports[`Table renders empty table 1`] = `
|
||||
class="ant-table-content"
|
||||
>
|
||||
<table
|
||||
style="table-layout:auto"
|
||||
style="table-layout: auto;"
|
||||
>
|
||||
<colgroup />
|
||||
<thead
|
||||
@ -156,7 +156,7 @@ exports[`Table renders empty table with custom emptyText 1`] = `
|
||||
class="ant-table-content"
|
||||
>
|
||||
<table
|
||||
style="table-layout:auto"
|
||||
style="table-layout: auto;"
|
||||
>
|
||||
<colgroup />
|
||||
<thead
|
||||
@ -246,17 +246,17 @@ exports[`Table renders empty table with fixed columns 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-table-content"
|
||||
style="overflow-x:auto;overflow-y:hidden"
|
||||
style="overflow-x: auto; overflow-y: hidden;"
|
||||
>
|
||||
<table
|
||||
style="width:1px;min-width:100%;table-layout:fixed"
|
||||
style="width: 1px; min-width: 100%; table-layout: fixed;"
|
||||
>
|
||||
<colgroup>
|
||||
<col
|
||||
style="width:100px"
|
||||
style="width: 100px;"
|
||||
/>
|
||||
<col
|
||||
style="width:100px"
|
||||
style="width: 100px;"
|
||||
/>
|
||||
<col />
|
||||
<col />
|
||||
@ -267,7 +267,7 @@ exports[`Table renders empty table with fixed columns 1`] = `
|
||||
<col />
|
||||
<col />
|
||||
<col
|
||||
style="width:100px"
|
||||
style="width: 100px;"
|
||||
/>
|
||||
</colgroup>
|
||||
<thead
|
||||
@ -276,13 +276,13 @@ exports[`Table renders empty table with fixed columns 1`] = `
|
||||
<tr>
|
||||
<th
|
||||
class="ant-table-cell ant-table-cell-fix-left"
|
||||
style="position:sticky;left:0"
|
||||
style="position: sticky; left: 0px;"
|
||||
>
|
||||
Full Name
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last"
|
||||
style="position:sticky;left:0"
|
||||
style="position: sticky; left: 0px;"
|
||||
>
|
||||
Age
|
||||
</th>
|
||||
@ -328,7 +328,7 @@ exports[`Table renders empty table with fixed columns 1`] = `
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell ant-table-cell-fix-right ant-table-cell-fix-right-first"
|
||||
style="position:sticky;right:0"
|
||||
style="position: sticky; right: 0px;"
|
||||
>
|
||||
Action
|
||||
</th>
|
||||
@ -340,103 +340,103 @@ exports[`Table renders empty table with fixed columns 1`] = `
|
||||
<tr
|
||||
aria-hidden="true"
|
||||
class="ant-table-measure-row"
|
||||
style="height:0;font-size:0"
|
||||
style="height: 0px; font-size: 0px;"
|
||||
>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="padding:0;border:0;height:0"
|
||||
style="padding: 0px; border: 0px; height: 0px;"
|
||||
>
|
||||
<div
|
||||
style="height:0;overflow:hidden"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
>
|
||||
|
||||
</div>
|
||||
@ -451,7 +451,7 @@ exports[`Table renders empty table with fixed columns 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-table-expanded-row-fixed"
|
||||
style="width:0;position:sticky;left:0;overflow:hidden"
|
||||
style="width: 0px; position: sticky; left: 0px; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="ant-empty ant-empty-normal"
|
||||
@ -555,7 +555,7 @@ exports[`Table renders empty table without emptyText when loading 1`] = `
|
||||
class="ant-table-content"
|
||||
>
|
||||
<table
|
||||
style="table-layout:auto"
|
||||
style="table-layout: auto;"
|
||||
>
|
||||
<colgroup />
|
||||
<thead
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { render } from '../../../tests/utils';
|
||||
import Table from '..';
|
||||
|
||||
const columns = [
|
||||
@ -46,19 +46,19 @@ const columnsFixed = [
|
||||
|
||||
describe('Table', () => {
|
||||
it('renders empty table', () => {
|
||||
const wrapper = render(<Table dataSource={[]} columns={columns} pagination={false} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const { asFragment } = render(<Table dataSource={[]} columns={columns} pagination={false} />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders empty table with fixed columns', () => {
|
||||
const wrapper = render(
|
||||
const { asFragment } = render(
|
||||
<Table dataSource={[]} columns={columnsFixed} pagination={false} scroll={{ x: 1 }} />,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders empty table with custom emptyText', () => {
|
||||
const wrapper = render(
|
||||
const { asFragment } = render(
|
||||
<Table
|
||||
dataSource={[]}
|
||||
columns={columns}
|
||||
@ -66,11 +66,11 @@ describe('Table', () => {
|
||||
locale={{ emptyText: 'custom empty text' }}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders empty table without emptyText when loading', () => {
|
||||
const wrapper = render(<Table dataSource={[]} columns={columns} loading />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const { asFragment } = render(<Table dataSource={[]} columns={columns} loading />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -2,6 +2,7 @@ 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 type { FilterState } from '.';
|
||||
import { flattenKeys } from '.';
|
||||
import Button from '../../../button';
|
||||
@ -13,7 +14,7 @@ import Empty from '../../../empty';
|
||||
import type { MenuProps } from '../../../menu';
|
||||
import Menu from '../../../menu';
|
||||
import Radio from '../../../radio';
|
||||
import type { DataNode, EventDataNode } from '../../../tree';
|
||||
import type { EventDataNode } from '../../../tree';
|
||||
import Tree from '../../../tree';
|
||||
import useSyncState from '../../../_util/hooks/useSyncState';
|
||||
import type {
|
||||
@ -27,6 +28,8 @@ import type {
|
||||
import FilterSearch from './FilterSearch';
|
||||
import FilterDropdownMenuWrapper from './FilterWrapper';
|
||||
|
||||
type FilterTreeDataNode = FieldDataNode<{ title: React.ReactNode; key: React.Key }>;
|
||||
|
||||
interface FilterRestProps {
|
||||
confirm?: Boolean;
|
||||
closeDropdown?: Boolean;
|
||||
@ -160,7 +163,10 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
setFilteredKeysSync(selectedKeys);
|
||||
};
|
||||
|
||||
const onCheck = (keys: Key[], { node, checked }: { node: EventDataNode; checked: boolean }) => {
|
||||
const onCheck = (
|
||||
keys: Key[],
|
||||
{ node, checked }: { node: EventDataNode<FilterTreeDataNode>; checked: boolean },
|
||||
) => {
|
||||
if (!filterMultiple) {
|
||||
onSelectKeys({ selectedKeys: checked && node.key ? [node.key] : [] });
|
||||
} else {
|
||||
@ -286,7 +292,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
const getTreeData = ({ filters }: { filters?: ColumnFilterItem[] }) =>
|
||||
(filters || []).map((filter, index) => {
|
||||
const key = String(filter.value);
|
||||
const item: DataNode = {
|
||||
const item: FilterTreeDataNode = {
|
||||
title: filter.text,
|
||||
key: filter.value !== undefined ? key : index,
|
||||
};
|
||||
@ -351,7 +357,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
{locale.filterCheckall}
|
||||
</Checkbox>
|
||||
) : null}
|
||||
<Tree
|
||||
<Tree<FilterTreeDataNode>
|
||||
checkable
|
||||
selectable={false}
|
||||
blockNode
|
||||
|
@ -53,6 +53,7 @@ Tree selection control.
|
||||
| treeDataSimpleMode | Enable simple mode of treeData. Changes the `treeData` schema to: \[{id:1, pId:0, value:'1', title:"test1",...},...] where pId is parent node's id). It is possible to replace the default `id` and `pId` keys by providing object to `treeDataSimpleMode` | boolean \| object<{ id: string, pId: string, rootPId: string }> | false | |
|
||||
| treeDefaultExpandAll | Whether to expand all treeNodes by default | boolean | false | |
|
||||
| treeDefaultExpandedKeys | Default expanded treeNodes | string\[] | - | |
|
||||
| treeExpandAction | Tree title open logic when click, optional: false \| `click` \| `doubleClick` | string \| boolean | false | 4.21.0 |
|
||||
| treeExpandedKeys | Set expanded keys | string\[] | - | |
|
||||
| treeIcon | Shows the icon before a TreeNode's title. There is no default style; you must set a custom style for it if set to `true` | boolean | false | |
|
||||
| treeLoadedKeys | (Controlled) Set loaded tree nodes, work with `loadData` only | string[] | [] | |
|
||||
|
@ -79,6 +79,7 @@ const InternalTreeSelect = <OptionType extends BaseOptionType | DefaultOptionTyp
|
||||
choiceTransitionName = '',
|
||||
status: customStatus,
|
||||
showArrow,
|
||||
treeExpandAction,
|
||||
...props
|
||||
}: TreeSelectProps<OptionType>,
|
||||
ref: React.Ref<BaseSelectRef>,
|
||||
@ -219,6 +220,7 @@ const InternalTreeSelect = <OptionType extends BaseOptionType | DefaultOptionTyp
|
||||
transitionName,
|
||||
)}
|
||||
showArrow={hasFeedback || showArrow}
|
||||
treeExpandAction={treeExpandAction}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -54,6 +54,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Ax4DA0njr/TreeSelect.svg
|
||||
| treeDataSimpleMode | 使用简单格式的 treeData,具体设置参考可设置的类型 (此时 treeData 应变为这样的数据结构: \[{id:1, pId:0, value:'1', title:"test1",...},...], `pId` 是父节点的 id) | boolean \| object<{ id: string, pId: string, rootPId: string }> | false | |
|
||||
| treeDefaultExpandAll | 默认展开所有树节点 | boolean | false | |
|
||||
| treeDefaultExpandedKeys | 默认展开的树节点 | string\[] | - | |
|
||||
| treeExpandAction | 点击节点 title 时的展开逻辑,可选:false \| `click` \| `doubleClick` | string \| boolean | false | 4.21.0 |
|
||||
| treeExpandedKeys | 设置展开的树节点 | string\[] | - | |
|
||||
| treeIcon | 是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true,需要自行定义图标相关样式 | boolean | false | |
|
||||
| treeLine | 是否展示线条样式,请参考 [Tree - showLine](/components/tree/#components-tree-demo-line) | boolean \| object | false | 4.17.0 |
|
||||
|
@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type RcTree from 'rc-tree';
|
||||
import debounce from 'lodash/debounce';
|
||||
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';
|
||||
@ -86,25 +85,10 @@ const DirectoryTree: React.ForwardRefRenderFunction<RcTree, DirectoryTreeProps>
|
||||
}
|
||||
}, [props.expandedKeys]);
|
||||
|
||||
const expandFolderNode = (event: React.MouseEvent<HTMLElement>, node: any) => {
|
||||
const { isLeaf } = node;
|
||||
|
||||
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call internal rc-tree expand function
|
||||
// https://github.com/ant-design/ant-design/issues/12567
|
||||
treeRef.current!.onNodeExpand(event as any, node);
|
||||
};
|
||||
|
||||
const onDebounceExpand = debounce(expandFolderNode, 200, {
|
||||
leading: true,
|
||||
});
|
||||
const onExpand = (
|
||||
keys: Key[],
|
||||
info: {
|
||||
node: EventDataNode;
|
||||
node: EventDataNode<any>;
|
||||
expanded: boolean;
|
||||
nativeEvent: MouseEvent;
|
||||
},
|
||||
@ -116,28 +100,6 @@ const DirectoryTree: React.ForwardRefRenderFunction<RcTree, DirectoryTreeProps>
|
||||
return props.onExpand?.(keys, info);
|
||||
};
|
||||
|
||||
const onClick = (event: React.MouseEvent<HTMLElement>, node: EventDataNode) => {
|
||||
const { expandAction } = props;
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'click') {
|
||||
onDebounceExpand(event, node);
|
||||
}
|
||||
|
||||
props.onClick?.(event, node);
|
||||
};
|
||||
|
||||
const onDoubleClick = (event: React.MouseEvent<HTMLElement>, node: EventDataNode) => {
|
||||
const { expandAction } = props;
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'doubleClick') {
|
||||
onDebounceExpand(event, node);
|
||||
}
|
||||
|
||||
props.onDoubleClick?.(event, node);
|
||||
};
|
||||
|
||||
const onSelect = (
|
||||
keys: Key[],
|
||||
event: {
|
||||
@ -224,8 +186,6 @@ const DirectoryTree: React.ForwardRefRenderFunction<RcTree, DirectoryTreeProps>
|
||||
expandedKeys={expandedKeys}
|
||||
selectedKeys={selectedKeys}
|
||||
onSelect={onSelect}
|
||||
onClick={onClick}
|
||||
onDoubleClick={onDoubleClick}
|
||||
onExpand={onExpand}
|
||||
/>
|
||||
);
|
||||
|
@ -788,914 +788,6 @@ exports[`Directory Tree defaultExpandParent 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Directory Tree expand click 1`] = `
|
||||
<div
|
||||
class="ant-tree ant-tree-block-node ant-tree-directory"
|
||||
role="tree"
|
||||
>
|
||||
<div>
|
||||
<input
|
||||
aria-label="for screen reader"
|
||||
style="width: 0px; height: 0px; display: flex; overflow: hidden; opacity: 0; border: 0px; padding: 0px; margin: 0px;"
|
||||
tabindex="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="ant-tree-treenode"
|
||||
style="position: absolute; pointer-events: none; visibility: hidden; height: 0px; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-indent"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-indent-unit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tree-list"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-list-holder"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tree-list-holder-inner"
|
||||
style="display: flex; flex-direction: column;"
|
||||
>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-selected"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
>
|
||||
<span
|
||||
aria-label="caret-down"
|
||||
class="anticon anticon-caret-down ant-tree-switcher-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="caret-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open ant-tree-node-selected"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder-open"
|
||||
class="anticon anticon-folder-open"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder-open"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M928 444H820V330.4c0-17.7-14.3-32-32-32H473L355.7 186.2a8.15 8.15 0 00-5.5-2.2H96c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h698c13 0 24.8-7.9 29.7-20l134-332c1.5-3.8 2.3-7.9 2.3-12 0-17.7-14.3-32-32-32zM136 256h188.5l119.6 114.4H748V444H238c-13 0-24.8 7.9-29.7 20L136 643.2V256zm635.3 512H159l103.3-256h612.4L771.3 768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-indent-unit ant-tree-indent-unit-start"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder"
|
||||
class="anticon anticon-folder"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-indent-unit ant-tree-indent-unit-start"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder"
|
||||
class="anticon anticon-folder"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
>
|
||||
<span
|
||||
aria-label="caret-down"
|
||||
class="anticon anticon-caret-down ant-tree-switcher-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="caret-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder"
|
||||
class="anticon anticon-folder"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Directory Tree expand click 2`] = `
|
||||
<div
|
||||
class="ant-tree ant-tree-block-node ant-tree-directory"
|
||||
role="tree"
|
||||
>
|
||||
<div>
|
||||
<input
|
||||
aria-label="for screen reader"
|
||||
style="width: 0px; height: 0px; display: flex; overflow: hidden; opacity: 0; border: 0px; padding: 0px; margin: 0px;"
|
||||
tabindex="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="ant-tree-treenode"
|
||||
style="position: absolute; pointer-events: none; visibility: hidden; height: 0px; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-indent"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-indent-unit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tree-list"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-list-holder"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tree-list-holder-inner"
|
||||
style="display: flex; flex-direction: column;"
|
||||
>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-selected"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
>
|
||||
<span
|
||||
aria-label="caret-down"
|
||||
class="anticon anticon-caret-down ant-tree-switcher-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="caret-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close ant-tree-node-selected"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder"
|
||||
class="anticon anticon-folder"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
>
|
||||
<span
|
||||
aria-label="caret-down"
|
||||
class="anticon anticon-caret-down ant-tree-switcher-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="caret-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder"
|
||||
class="anticon anticon-folder"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Directory Tree expand double click 1`] = `
|
||||
<div
|
||||
class="ant-tree ant-tree-block-node ant-tree-directory"
|
||||
role="tree"
|
||||
>
|
||||
<div>
|
||||
<input
|
||||
aria-label="for screen reader"
|
||||
style="width: 0px; height: 0px; display: flex; overflow: hidden; opacity: 0; border: 0px; padding: 0px; margin: 0px;"
|
||||
tabindex="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="ant-tree-treenode"
|
||||
style="position: absolute; pointer-events: none; visibility: hidden; height: 0px; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-indent"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-indent-unit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tree-list"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-list-holder"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tree-list-holder-inner"
|
||||
style="display: flex; flex-direction: column;"
|
||||
>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
>
|
||||
<span
|
||||
aria-label="caret-down"
|
||||
class="anticon anticon-caret-down ant-tree-switcher-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="caret-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder-open"
|
||||
class="anticon anticon-folder-open"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder-open"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M928 444H820V330.4c0-17.7-14.3-32-32-32H473L355.7 186.2a8.15 8.15 0 00-5.5-2.2H96c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h698c13 0 24.8-7.9 29.7-20l134-332c1.5-3.8 2.3-7.9 2.3-12 0-17.7-14.3-32-32-32zM136 256h188.5l119.6 114.4H748V444H238c-13 0-24.8 7.9-29.7 20L136 643.2V256zm635.3 512H159l103.3-256h612.4L771.3 768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-indent-unit ant-tree-indent-unit-start"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder"
|
||||
class="anticon anticon-folder"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-indent-unit ant-tree-indent-unit-start"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder"
|
||||
class="anticon anticon-folder"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
>
|
||||
<span
|
||||
aria-label="caret-down"
|
||||
class="anticon anticon-caret-down ant-tree-switcher-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="caret-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder"
|
||||
class="anticon anticon-folder"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Directory Tree expand double click 2`] = `
|
||||
<div
|
||||
class="ant-tree ant-tree-block-node ant-tree-directory"
|
||||
role="tree"
|
||||
>
|
||||
<div>
|
||||
<input
|
||||
aria-label="for screen reader"
|
||||
style="width: 0px; height: 0px; display: flex; overflow: hidden; opacity: 0; border: 0px; padding: 0px; margin: 0px;"
|
||||
tabindex="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="ant-tree-treenode"
|
||||
style="position: absolute; pointer-events: none; visibility: hidden; height: 0px; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-indent"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-indent-unit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tree-list"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="ant-tree-list-holder"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tree-list-holder-inner"
|
||||
style="display: flex; flex-direction: column;"
|
||||
>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
>
|
||||
<span
|
||||
aria-label="caret-down"
|
||||
class="anticon anticon-caret-down ant-tree-switcher-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="caret-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder"
|
||||
class="anticon anticon-folder"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-tree-indent"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
>
|
||||
<span
|
||||
aria-label="caret-down"
|
||||
class="anticon anticon-caret-down ant-tree-switcher-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="caret-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"
|
||||
title="---"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<span
|
||||
aria-label="folder"
|
||||
class="anticon anticon-folder"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="folder"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
---
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Directory Tree expand with state control click 1`] = `
|
||||
<div
|
||||
class="ant-tree ant-tree-block-node ant-tree-directory"
|
||||
|
@ -2,7 +2,7 @@ import debounce from 'lodash/debounce';
|
||||
import React from 'react';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import { act, fireEvent, render } from '../../../tests/utils';
|
||||
import Tree from '../index';
|
||||
|
||||
const { DirectoryTree, TreeNode } = Tree;
|
||||
@ -44,27 +44,45 @@ describe('Directory Tree', () => {
|
||||
|
||||
describe('expand', () => {
|
||||
it('click', () => {
|
||||
const { container, asFragment } = render(createTree());
|
||||
const onExpand = jest.fn();
|
||||
const { container } = render(createTree({ onExpand }));
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
jest.runAllTimers();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
jest.runAllTimers();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(onExpand).toHaveBeenCalledWith(['0-0'], expect.anything());
|
||||
onExpand.mockReset();
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.click(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
jest.runAllTimers();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(onExpand).toHaveBeenCalledWith([], expect.anything());
|
||||
});
|
||||
|
||||
it('double click', () => {
|
||||
const { container, asFragment } = render(createTree({ expandAction: 'doubleClick' }));
|
||||
const onExpand = jest.fn();
|
||||
const { container } = render(createTree({ expandAction: 'doubleClick', onExpand }));
|
||||
|
||||
fireEvent.doubleClick(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
jest.runAllTimers();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
jest.runAllTimers();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(onExpand).toHaveBeenCalledWith(['0-0'], expect.anything());
|
||||
onExpand.mockReset();
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.doubleClick(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
jest.runAllTimers();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(onExpand).toHaveBeenCalledWith([], expect.anything());
|
||||
});
|
||||
|
||||
describe('with state control', () => {
|
||||
|
@ -116,7 +116,7 @@ const App: React.FC = () => {
|
||||
{afterStr}
|
||||
</span>
|
||||
) : (
|
||||
<span>{item.title}</span>
|
||||
<span>{strTitle}</span>
|
||||
);
|
||||
if (item.children) {
|
||||
return { title, key: item.key, children: loop(item.children) };
|
||||
|
@ -79,10 +79,10 @@ Basic text writing, including headings, body text, lists, and more.
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| format | The Mime Type of the text | 'text/plain' \| 'text/html' | - | 4.21.0 |
|
||||
| icon | Custom copy icon: \[copyIcon, copiedIcon] | \[ReactNode, ReactNode] | - | 4.6.0 |
|
||||
| text | The text to copy | string | - | |
|
||||
| tooltips | Custom tooltip text, hide when it is false | \[ReactNode, ReactNode] | \[`Copy`, `Copied`] | 4.4.0 |
|
||||
| format | The Mime Type of the text | 'text/plain' \| 'text/html' | - | |
|
||||
| onCopy | Called when copied text | function | - | |
|
||||
|
||||
### editable
|
||||
|
@ -80,10 +80,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| format | 剪切板数据的 Mime Type | 'text/plain' \| 'text/html' | - | 4.21.0 |
|
||||
| icon | 自定义拷贝图标:\[默认图标, 拷贝后的图标] | \[ReactNode, ReactNode] | - | 4.6.0 |
|
||||
| text | 拷贝到剪切板里的文本 | string | - | |
|
||||
| tooltips | 自定义提示文案,为 false 时隐藏文案 | \[ReactNode, ReactNode] | \[`复制`, `复制成功`] | 4.4.0 |
|
||||
| format | 剪切板数据的 Mime Type | 'text/plain' \| 'text/html' | - | |
|
||||
| onCopy | 拷贝成功的回调函数 | function | - | |
|
||||
|
||||
### editable
|
||||
|
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "antd",
|
||||
"version": "4.20.7",
|
||||
"version": "4.21.0",
|
||||
"description": "An enterprise-class UI design language and React components implementation",
|
||||
"title": "Ant Design",
|
||||
"keywords": [
|
||||
@ -125,7 +125,7 @@
|
||||
"dayjs": "^1.11.1",
|
||||
"lodash": "^4.17.21",
|
||||
"memoize-one": "^6.0.0",
|
||||
"rc-cascader": "~3.5.0",
|
||||
"rc-cascader": "~3.6.0",
|
||||
"rc-checkbox": "~2.3.0",
|
||||
"rc-collapse": "~3.3.0",
|
||||
"rc-dialog": "~8.8.2",
|
||||
@ -153,8 +153,8 @@
|
||||
"rc-tabs": "~11.16.0",
|
||||
"rc-textarea": "~0.3.0",
|
||||
"rc-tooltip": "~5.1.1",
|
||||
"rc-tree": "~5.5.0",
|
||||
"rc-tree-select": "~5.3.0",
|
||||
"rc-tree": "~5.6.5",
|
||||
"rc-tree-select": "~5.4.0",
|
||||
"rc-trigger": "^5.2.10",
|
||||
"rc-upload": "~4.3.0",
|
||||
"rc-util": "^5.21.3",
|
||||
@ -172,7 +172,7 @@
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@types/enzyme": "^3.10.5",
|
||||
"@types/gtag.js": "^0.0.10",
|
||||
"@types/jest": "^27.0.0",
|
||||
"@types/jest": "^28.0.0",
|
||||
"@types/jest-axe": "^3.5.3",
|
||||
"@types/jest-environment-puppeteer": "^5.0.0",
|
||||
"@types/jest-image-snapshot": "^4.1.0",
|
||||
|
@ -35,7 +35,7 @@ const MAINTAINERS = [
|
||||
'Rustin-Liu',
|
||||
'fireairforce',
|
||||
'kerm1it',
|
||||
'MadCcc',
|
||||
'madccc',
|
||||
].map(author => author.toLowerCase());
|
||||
|
||||
const cwd = process.cwd();
|
||||
@ -47,7 +47,7 @@ function getDescription(entity) {
|
||||
}
|
||||
const descEle = entity.element.find('td:last');
|
||||
let htmlContent = descEle.html();
|
||||
htmlContent = htmlContent.replace(/<code>([^<]*)<\/code>/g, '`$1`');
|
||||
htmlContent = htmlContent.replace(/<code class="notranslate">([^<]*)<\/code>/g, '`$1`');
|
||||
return htmlContent.trim();
|
||||
}
|
||||
|
||||
@ -179,6 +179,9 @@ async function printLog() {
|
||||
if (str.toLowerCase().includes('fix') || str.includes('修复')) {
|
||||
icon = '🐞';
|
||||
}
|
||||
if (str.toLowerCase().includes('feat')) {
|
||||
icon = '🆕';
|
||||
}
|
||||
|
||||
let authorText = '';
|
||||
if (!MAINTAINERS.includes(author.toLowerCase())) {
|
||||
|
@ -4,6 +4,8 @@ import { StrictMode } from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import type { RenderOptions } from '@testing-library/react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { _rs as onLibResize } from 'rc-resize-observer/lib/utils/observerUtil';
|
||||
import { _rs as onEsResize } from 'rc-resize-observer/es/utils/observerUtil';
|
||||
|
||||
export function setMockDate(dateString = '2017-09-18T03:30:07.795') {
|
||||
MockDate.set(dateString);
|
||||
@ -28,4 +30,14 @@ const customRender = (ui: ReactElement, options?: Omit<RenderOptions, 'wrapper'>
|
||||
|
||||
export { customRender as render };
|
||||
|
||||
export const triggerResize = (target: Element) => {
|
||||
const originGetBoundingClientRect = target.getBoundingClientRect;
|
||||
|
||||
target.getBoundingClientRect = () => ({ width: 510, height: 903 } as DOMRect);
|
||||
onLibResize([{ target } as ResizeObserverEntry]);
|
||||
onEsResize([{ target } as ResizeObserverEntry]);
|
||||
|
||||
target.getBoundingClientRect = originGetBoundingClientRect;
|
||||
};
|
||||
|
||||
export * from '@testing-library/react';
|
||||
|
Loading…
Reference in New Issue
Block a user