chore: auto merge branchs (#35857)

chore: merge master into feature
This commit is contained in:
github-actions[bot] 2022-06-01 11:41:18 +00:00 committed by GitHub
commit 1223895caa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 564 additions and 658 deletions

View File

@ -15,6 +15,16 @@ timeline: true
---
## 4.20.7
`2022-05-30`
- 🐞 Fix Drawer form instance lost bug when opened. [#35706](https://github.com/ant-design/ant-design/pull/35706) [@crazyair](https://github.com/crazyair)
- 🐞 Fix Segmented options invalid space between icon and text when using the icon prop. [#35701](https://github.com/ant-design/ant-design/pull/35701)
- 💄 Optimize Popover arrow style. [#35717](https://github.com/ant-design/ant-design/pull/35717)
- TypeScript
- 🤖 Fix Card type hints problem. [#35753](https://github.com/ant-design/ant-design/pull/35753)
## 4.20.6
`2022-05-22`

View File

@ -15,6 +15,16 @@ timeline: true
---
## 4.20.7
`2022-05-30`
- 🐞 修复 Drawer 打开时 form 实例为 null 的问题。[#35706](https://github.com/ant-design/ant-design/pull/35706) [@crazyair](https://github.com/crazyair)
- 🐞 修复 Segmented 组件中选项使用 icon 属性时图标与文字之间的间距失效问题。[#35701](https://github.com/ant-design/ant-design/pull/35701)
- 💄 优化 Popover 的箭头效果。[#35717](https://github.com/ant-design/ant-design/pull/35717)
- TypeScript
- 🤖 修复 Card 组件的类型提示。[#35753](https://github.com/ant-design/ant-design/pull/35753)
## 4.20.6
`2022-05-22`

View File

@ -1,5 +1,4 @@
import React from 'react';
import { mount } from 'enzyme';
import { render } from '../../../tests/utils';
import Drawer from '..';
import ConfigProvider from '../../config-provider';
@ -19,71 +18,71 @@ describe('Drawer', () => {
rtlTest(Drawer);
it('render correctly', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Drawer visible width={400} getContainer={false}>
Here is content of Drawer
</Drawer>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('getContainer return undefined', () => {
let wrapper = mount(<DrawerTest getContainer={() => undefined} />);
expect(wrapper.render()).toMatchSnapshot();
wrapper = mount(<DrawerTest getContainer={false} />);
expect(wrapper.render()).toMatchSnapshot();
const { container: wrapper, rerender } = render(<DrawerTest getContainer={() => undefined} />);
expect(wrapper.firstChild).toMatchSnapshot();
rerender(<DrawerTest getContainer={false} />);
expect(wrapper.firstChild).toMatchSnapshot();
});
it('render top drawer', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Drawer visible height={400} placement="top" getContainer={false}>
Here is content of Drawer
</Drawer>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('have a title', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Drawer visible title="Test Title" getContainer={false}>
Here is content of Drawer
</Drawer>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('closable is false', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Drawer visible closable={false} getContainer={false}>
Here is content of Drawer
</Drawer>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('destroyOnClose is true', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Drawer destroyOnClose visible={false} getContainer={false}>
Here is content of Drawer
</Drawer>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('className is test_drawer', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Drawer destroyOnClose visible={false} className="test_drawer" getContainer={false}>
Here is content of Drawer
</Drawer>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('style/drawerStyle/headerStyle/bodyStyle should work', () => {
const style = {
backgroundColor: '#08c',
};
const wrapper = mount(
const { container: wrapper } = render(
<Drawer
visible
style={style}
@ -95,44 +94,44 @@ describe('Drawer', () => {
Here is content of Drawer
</Drawer>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('have a footer', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Drawer visible footer="Test Footer" getContainer={false}>
Here is content of Drawer
</Drawer>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('forceRender works', () => {
const wrapper = mount(
const { baseElement, rerender } = render(
<Drawer>
<button type="button" className="forceRender">
should not be rendered
</button>
</Drawer>,
);
expect(wrapper.find('button.forceRender').length).toBe(0);
const wrapper2 = mount(
expect(baseElement.querySelectorAll('button.forceRender').length).toBe(0);
rerender(
<Drawer forceRender>
<button type="button" className="forceRender">
should be rendered
</button>
</Drawer>,
);
expect(wrapper2.find('button.forceRender').length).toBe(1);
expect(baseElement.querySelectorAll('button.forceRender').length).toBe(1);
});
it('support closeIcon', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Drawer visible closable closeIcon={<span>close</span>} width={400} getContainer={false}>
Here is content of Drawer
</Drawer>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('ConfigProvider should not warning', () => {
@ -151,7 +150,7 @@ describe('Drawer', () => {
it('should support ref', () => {
const ref = React.createRef();
mount(
render(
<Drawer visible ref={ref} width={400}>
Here is content of Drawer
</Drawer>,

View File

@ -68,6 +68,25 @@ describe('Drawer', () => {
expect(container.querySelector('.ant-drawer-wrapper-body')).toBeTruthy();
});
it('dom should be existed after close twice when getContainer is false', () => {
const { container, rerender } = render(getDrawer({ visible: true, getContainer: false }));
rerender(getDrawer({ visible: false, getContainer: false }));
const ev = new TransitionEvent('transitionend', { bubbles: true });
ev.propertyName = 'transform';
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev);
rerender(getDrawer({ visible: true, getContainer: false }));
const ev2 = new TransitionEvent('transitionend', { bubbles: true });
ev2.propertyName = 'transform';
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev2);
rerender(getDrawer({ visible: false, getContainer: false }));
const ev3 = new TransitionEvent('transitionend', { bubbles: true });
ev3.propertyName = 'transform';
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev3);
expect(container.querySelector('.ant-drawer-wrapper-body')).toBeTruthy();
});
it('test afterVisibleChange', async () => {
const afterVisibleChange = jest.fn();
const { rerender } = render(getDrawer({ afterVisibleChange, visible: true }));

View File

@ -1,7 +1,7 @@
import React from 'react';
import { mount } from 'enzyme';
import Drawer from '..';
import Button from '../../button';
import { render, fireEvent } from '../../../tests/utils';
class MultiDrawer extends React.Component {
state = { visible: false, childrenDrawer: false, hasChildren: true };
@ -110,68 +110,68 @@ class MultiDrawer extends React.Component {
describe('Drawer', () => {
it('render right MultiDrawer', () => {
const wrapper = mount(<MultiDrawer placement="right" />);
wrapper.find('button#open_drawer').simulate('click');
wrapper.find('button#open_two_drawer').simulate('click');
const translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
const { container: wrapper } = render(<MultiDrawer placement="right" />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
expect(translateX).toEqual('translateX(-180px)');
expect(wrapper.find('#two_drawer_text').exists()).toBe(true);
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
});
it('render left MultiDrawer', () => {
const wrapper = mount(<MultiDrawer placement="left" />);
wrapper.find('button#open_drawer').simulate('click');
wrapper.find('button#open_two_drawer').simulate('click');
const translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
const { container: wrapper } = render(<MultiDrawer placement="left" />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
expect(translateX).toEqual('translateX(180px)');
expect(wrapper.find('#two_drawer_text').exists()).toBe(true);
wrapper.find('.Two-level .ant-drawer-close').simulate('click');
expect(wrapper.find('.childrenDrawer').text()).toEqual('false');
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
fireEvent.click(wrapper.querySelector('.Two-level .ant-drawer-close'));
expect(wrapper.querySelector('.childrenDrawer').innerHTML).toEqual('false');
});
it('render top MultiDrawer', () => {
const wrapper = mount(<MultiDrawer placement="top" />);
wrapper.find('button#open_drawer').simulate('click');
wrapper.find('button#open_two_drawer').simulate('click');
const translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
const { container: wrapper } = render(<MultiDrawer placement="top" />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
expect(translateX).toEqual('translateY(180px)');
expect(wrapper.find('#two_drawer_text').exists()).toBe(true);
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
});
it('render MultiDrawer is child in unmount', () => {
const wrapper = mount(<MultiDrawer placement="top" mask={false} />);
wrapper.find('button#open_drawer').simulate('click');
wrapper.find('button#open_two_drawer').simulate('click');
wrapper.find('button#remove_drawer').simulate('click');
let translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
expect(translateX).toEqual(undefined);
wrapper.find('button#open_two_drawer').simulate('click');
translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
const { container: wrapper } = render(<MultiDrawer placement="top" mask={false} />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
fireEvent.click(wrapper.querySelector('button#remove_drawer'));
let translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
expect(translateX).toEqual('');
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
expect(translateX).toEqual('translateY(180px)');
expect(wrapper.find('#two_drawer_text').exists()).toBe(true);
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
});
it('custom MultiDrawer push distance', () => {
const wrapper = mount(<MultiDrawer push={{ distance: 256 }} />);
wrapper.find('button#open_drawer').simulate('click');
wrapper.find('button#open_two_drawer').simulate('click');
const translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
const { container: wrapper } = render(<MultiDrawer push={{ distance: 256 }} />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
expect(translateX).toEqual('translateX(-256px)');
});
it('custom MultiDrawer push with true', () => {
const wrapper = mount(<MultiDrawer push />);
wrapper.find('button#open_drawer').simulate('click');
wrapper.find('button#open_two_drawer').simulate('click');
const translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
const { container: wrapper } = render(<MultiDrawer push />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
expect(translateX).toEqual('translateX(-180px)');
});
it('custom MultiDrawer push with false', () => {
const wrapper = mount(<MultiDrawer push={false} />);
wrapper.find('button#open_drawer').simulate('click');
wrapper.find('button#open_two_drawer').simulate('click');
const translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
expect(translateX).toBeUndefined();
const { container: wrapper } = render(<MultiDrawer push={false} />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
const translateX = wrapper.querySelectorAll('.ant-drawer.test_drawer')[0].style.transform;
expect(translateX).toEqual('');
});
});

View File

@ -312,15 +312,12 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
className={drawerClassName}
getContainer={getContainer}
afterVisibleChange={open => {
if (!open) {
if (destroyCloseRef.current === false) {
// set true only once
if (open) {
destroyCloseRef.current = false;
} else if (destroyOnClose) {
destroyCloseRef.current = true;
}
if (destroyOnClose) {
setLoad(false);
}
}
afterVisibleChange?.(open);
}}
>

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { render, fireEvent } from '../../../tests/utils';
import Image from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -10,49 +10,60 @@ describe('Image', () => {
mountTest(Image);
rtlTest(Image);
it('Image preview props set false', () => {
const wrapper = mount(<Image src={src} preview={false} />);
const { container: wrapper } = render(<Image src={src} preview={false} />);
expect(wrapper.find('Image').at(0).prop('preview')).toBe(false);
expect(wrapper.find('Image').at(1).prop('preview')).toBe(false);
fireEvent.click(wrapper.querySelector('.ant-image'));
expect(wrapper.querySelector('.ant-image-preview-root')).toBe(null);
});
it('Group preview props set false', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Image.PreviewGroup preview={false}>
<Image src={src} />
</Image.PreviewGroup>,
);
expect(wrapper.find('Group').prop('preview')).toBe(false);
fireEvent.click(wrapper.querySelector('.ant-image'));
expect(wrapper.querySelector('.ant-image-preview-root')).toBe(null);
});
it('Default preview props', () => {
const wrapper = mount(<Image src={src} preview={{ visible: true }} />);
const { container: wrapper, baseElement } = render(
<Image src={src} preview={{ visible: true }} />,
);
expect(wrapper.find('Preview').prop('transitionName')).toBe('ant-zoom');
expect(wrapper.find('Preview').prop('maskTransitionName')).toBe('ant-fade');
fireEvent.click(wrapper.querySelector('.ant-image'));
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('ant-fade');
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('ant-zoom');
});
it('Default Group preview props', () => {
const wrapper = mount(
const { container: wrapper, baseElement } = render(
<Image.PreviewGroup preview={{ visible: true }}>
<Image src={src} />
</Image.PreviewGroup>,
);
expect(wrapper.find('Preview').prop('transitionName')).toBe('ant-zoom');
expect(wrapper.find('Preview').prop('maskTransitionName')).toBe('ant-fade');
fireEvent.click(wrapper.querySelector('.ant-image'));
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('ant-fade');
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('ant-zoom');
});
it('Customize preview props', () => {
const wrapper = mount(
const { container: wrapper, baseElement } = render(
<Image
src={src}
preview={{ visible: true, transitionName: 'abc', maskTransitionName: 'def' }}
/>,
);
expect(wrapper.find('Preview').prop('transitionName')).toBe('abc');
expect(wrapper.find('Preview').prop('maskTransitionName')).toBe('def');
fireEvent.click(wrapper.querySelector('.ant-image'));
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('abc');
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('def');
});
it('Customize Group preview props', () => {
const wrapper = mount(
const { container: wrapper, baseElement } = render(
<Image.PreviewGroup
preview={{ visible: true, transitionName: 'abc', maskTransitionName: 'def' }}
>
@ -60,7 +71,9 @@ describe('Image', () => {
</Image.PreviewGroup>,
);
expect(wrapper.find('Preview').prop('transitionName')).toBe('abc');
expect(wrapper.find('Preview').prop('maskTransitionName')).toBe('def');
fireEvent.click(wrapper.querySelector('.ant-image'));
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('abc');
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('def');
});
});

View File

@ -1,5 +1,4 @@
import React from 'react';
import { mount } from 'enzyme';
import List from '..';
import ConfigProvider from '../../config-provider';
import { render } from '../../../tests/utils';
@ -20,7 +19,7 @@ describe('List Item Layout', () => {
];
it('horizontal itemLayout List which contains string nodes should not be flex container', () => {
const wrapper = mount(
const { container: wrapper } = render(
<List
dataSource={data}
renderItem={item => (
@ -30,11 +29,13 @@ describe('List Item Layout', () => {
)}
/>,
);
expect(wrapper.find('.ant-list-item').at(0).hasClass('ant-list-item-no-flex')).toBe(true);
expect(
wrapper.querySelectorAll('.ant-list-item')[0].classList.contains('ant-list-item-no-flex'),
).toBe(true);
});
it('horizontal itemLayout List should be flex container defaultly', () => {
const wrapper = mount(
it('horizontal itemLayout List should be flex container by default', () => {
const { container: wrapper } = render(
<List
dataSource={data}
renderItem={item => (
@ -47,11 +48,13 @@ describe('List Item Layout', () => {
)}
/>,
);
expect(wrapper.find('.ant-list-item').at(0).hasClass('ant-list-item-no-flex')).toBe(false);
expect(
wrapper.querySelector('.ant-list-item').classList.contains('ant-list-item-no-flex'),
).toBe(false);
});
it('vertical itemLayout List should be flex container when there is extra node', () => {
const wrapper = mount(
const { container: wrapper } = render(
<List
itemLayout="vertical"
dataSource={data}
@ -65,11 +68,13 @@ describe('List Item Layout', () => {
)}
/>,
);
expect(wrapper.find('.ant-list-item').at(0).hasClass('ant-list-item-no-flex')).toBe(false);
expect(
wrapper.querySelectorAll('.ant-list-item')[0].classList.contains('ant-list-item-no-flex'),
).toBe(false);
});
it('vertical itemLayout List should not be flex container when there is not extra node', () => {
const wrapper = mount(
const { container: wrapper } = render(
<List
itemLayout="vertical"
dataSource={data}
@ -83,11 +88,13 @@ describe('List Item Layout', () => {
)}
/>,
);
expect(wrapper.find('.ant-list-item').at(0).hasClass('ant-list-item-no-flex')).toBe(true);
expect(
wrapper.querySelectorAll('.ant-list-item')[0].classList.contains('ant-list-item-no-flex'),
).toBe(true);
});
it('horizontal itemLayout List should accept extra node', () => {
const wrapper = mount(
const { container: wrapper } = render(
<List
dataSource={data}
renderItem={item => (
@ -104,11 +111,11 @@ describe('List Item Layout', () => {
)}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('should render in RTL direction', () => {
const wrapper = mount(
const { container: wrapper } = render(
<ConfigProvider direction="rtl">
<List
dataSource={data}
@ -127,7 +134,7 @@ describe('List Item Layout', () => {
/>
</ConfigProvider>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('rowKey could be string', () => {
@ -145,14 +152,14 @@ describe('List Item Layout', () => {
title: `ant design`,
},
];
const wrapper = mount(
const { container: wrapper } = render(
<List
dataSource={dataWithId}
rowKey="id"
renderItem={item => <List.Item>{item.title}</List.Item>}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('rowKey could be function', () => {
@ -170,14 +177,14 @@ describe('List Item Layout', () => {
title: `ant design`,
},
];
const wrapper = mount(
const { container: wrapper } = render(
<List
dataSource={dataWithId}
rowKey={item => item.id}
renderItem={item => <List.Item>{item.title}</List.Item>}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.firstChild).toMatchSnapshot();
});
it('should ref', () => {

View File

@ -1,10 +1,10 @@
import React from 'react';
import { render } from 'enzyme';
import { render } from '../../../tests/utils';
import List from '..';
describe('List', () => {
it('renders empty list', () => {
const wrapper = render(<List dataSource={[]} renderItem={() => <List.Item />} />);
expect(wrapper).toMatchSnapshot();
const { container } = render(<List dataSource={[]} renderItem={() => <List.Item />} />);
expect(container.firstChild).toMatchSnapshot();
});
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { render } from '../../../tests/utils';
import List from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -16,7 +16,9 @@ describe('List', () => {
const renderItem = item => <List.Item>{item}</List.Item>;
const dataSource = [];
const wrapper = mount(<List renderItem={renderItem} dataSource={dataSource} locale={locale} />);
expect(wrapper.find('div').first().props().locale).toBe(undefined);
const { container } = render(
<List renderItem={renderItem} dataSource={dataSource} locale={locale} />,
);
expect(container.querySelector('div.ant-list').getAttribute('locale')).toBe(null);
});
});

View File

@ -1,6 +1,6 @@
import React from 'react';
import { render } from 'enzyme';
import { LoadingOutlined } from '@ant-design/icons';
import { render } from '../../../tests/utils';
import List from '..';
@ -9,20 +9,20 @@ describe('List', () => {
const loading = {
spinning: true,
};
const wrapper = render(
const { container: wrapper } = render(
<List loading={loading} dataSource={[]} renderItem={() => <List.Item />} />,
);
expect(wrapper.find('.ant-list-empty-text')).toHaveLength(0);
expect(wrapper.querySelectorAll('.ant-list-empty-text')).toHaveLength(0);
});
it('renders object loading', () => {
const loading = {
spinning: true,
};
const wrapper = render(
const { container: wrapper } = render(
<List loading={loading} dataSource={[1]} renderItem={() => <List.Item />} />,
);
expect(wrapper.find('.ant-spin-spinning')).toHaveLength(1);
expect(wrapper.querySelectorAll('.ant-spin-spinning')).toHaveLength(1);
});
it('renders object loading with indicator', () => {
@ -32,9 +32,9 @@ describe('List', () => {
spinning: true,
indicator: antIcon,
};
const wrapper = render(
const { container: wrapper } = render(
<List loading={loading} dataSource={[1]} renderItem={() => <List.Item />} />,
);
expect(wrapper.find('.anticon-loading')).toHaveLength(1);
expect(wrapper.querySelectorAll('.anticon-loading')).toHaveLength(1);
});
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { render, mount } from 'enzyme';
import { fireEvent, render } from '../../../tests/utils';
import List from '..';
import { noop } from '../../_util/warning';
@ -26,47 +26,58 @@ describe('List.pagination', () => {
}
function renderedNames(wrapper) {
return wrapper.find('.ant-list-item').map(row => row.text());
return Array.prototype.map.call(
wrapper.querySelectorAll('.ant-list-item'),
row => row.textContent,
);
}
it('renders pagination correctly', () => {
const wrapper = render(createList());
expect(wrapper).toMatchSnapshot();
const { container } = render(createList());
expect(container.firstChild).toMatchSnapshot();
});
it('should not show pager if pagination.hideOnSinglePage is true and only 1 page', () => {
const wrapper = mount(createList({ 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: wrapper, rerender } = render(
createList({
pagination: {
pageSize: 3,
hideOnSinglePage: true,
},
}),
);
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
rerender(createList({ pagination: { pageSize: 3, hideOnSinglePage: false } }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
rerender(createList({ pagination: { pageSize: 4, hideOnSinglePage: true } }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(0);
rerender(createList({ pagination: { pageSize: 4, hideOnSinglePage: false } }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
rerender(createList({ pagination: { pageSize: 5, hideOnSinglePage: true } }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(0);
rerender(createList({ pagination: { pageSize: 5, hideOnSinglePage: false } }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
});
it('paginate data', () => {
const wrapper = mount(createList());
const { container: wrapper } = render(createList());
expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy']);
wrapper.find('Pager').last().simulate('click');
const paginationItems = wrapper.querySelectorAll('.ant-pagination-item');
fireEvent.click(paginationItems[paginationItems.length - 1]);
expect(renderedNames(wrapper)).toEqual(['Tom', 'Jerry']);
});
it('repaginates when pageSize change', () => {
const wrapper = mount(createList());
const { container: wrapper, rerender } = render(createList());
wrapper.setProps({ pagination: { pageSize: 1 } });
rerender(createList({ pagination: { pageSize: 1 } }));
expect(renderedNames(wrapper)).toEqual(['Jack']);
});
it('fires change event', () => {
const handlePaginationChange = jest.fn();
const wrapper = mount(
const { container: wrapper } = render(
createList({
pagination: {
...pagination,
@ -76,7 +87,8 @@ describe('List.pagination', () => {
}),
);
wrapper.find('Pager').last().simulate('click');
const paginationItems = wrapper.querySelectorAll('.ant-pagination-item');
fireEvent.click(paginationItems[paginationItems.length - 1]);
expect(handlePaginationChange).toHaveBeenCalledWith(2, 2);
});
@ -84,56 +96,70 @@ describe('List.pagination', () => {
// 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(createList());
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');
const { container: wrapper, rerender } = render(createList());
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
expect(wrapper.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
rerender(createList({ pagination: false }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(0);
rerender(createList({ pagination }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
expect(wrapper.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
fireEvent.click(wrapper.querySelector('.ant-pagination-item-2'));
expect(renderedNames(wrapper)).toEqual(['Tom', 'Jerry']);
wrapper.setProps({ pagination: false });
expect(wrapper.find('.ant-pagination')).toHaveLength(0);
wrapper.setProps({ pagination: true });
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
rerender(createList({ pagination: false }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(0);
rerender(createList({ pagination: true }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
// Legacy code will make pageSize ping with 10, here we fixed to keep sync by current one
expect(wrapper.find('.ant-pagination-item')).toHaveLength(2);
expect(wrapper.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
expect(renderedNames(wrapper)).toEqual(['Tom', 'Jerry']);
});
// https://github.com/ant-design/ant-design/issues/5259
it('change to correct page when data source changes', () => {
const wrapper = mount(createList({ 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: wrapper, rerender } = render(createList({ pagination: { pageSize: 1 } }));
fireEvent.click(wrapper.querySelector('.ant-pagination-item-3'));
rerender(createList({ dataSource: [data[0]] }));
expect(wrapper.querySelector('.ant-pagination-item-1')).toHaveClass(
'ant-pagination-item-active',
);
});
it('specify the position of pagination', () => {
const wrapper = mount(createList({ pagination: { position: 'top' } }));
expect(wrapper.find('.ant-list').childAt(0).find('.ant-pagination')).toHaveLength(1);
wrapper.setProps({ pagination: { position: 'bottom' } });
expect(wrapper.find('.ant-list').children().last().find('.ant-pagination')).toHaveLength(1);
wrapper.setProps({ pagination: { position: 'both' } });
expect(wrapper.find('.ant-pagination')).toHaveLength(2);
expect(wrapper.find('.ant-list').childAt(0).find('.ant-pagination')).toHaveLength(1);
expect(wrapper.find('.ant-list').children().last().find('.ant-pagination')).toHaveLength(1);
const { container: wrapper, rerender } = render(
createList({ pagination: { position: 'top' } }),
);
expect(wrapper.querySelector('.ant-list').querySelectorAll('.ant-pagination')).toHaveLength(1);
rerender(createList({ pagination: { position: 'bottom' } }));
expect(
wrapper.querySelector('.ant-list').lastElementChild.querySelectorAll('.ant-pagination'),
).toHaveLength(1);
rerender(createList({ pagination: { position: 'both' } }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(2);
expect(
wrapper.querySelector('.ant-list').firstElementChild.querySelectorAll('.ant-pagination'),
).toHaveLength(1);
expect(
wrapper.querySelector('.ant-list').lastElementChild.querySelectorAll('.ant-pagination'),
).toHaveLength(1);
});
it('should change page size work', () => {
const wrapper = mount(createList({ pagination: { showSizeChanger: true } }));
expect(wrapper.find('Pagination').first().render()).toMatchSnapshot();
const { container: wrapper } = render(createList({ pagination: { showSizeChanger: true } }));
expect(wrapper.querySelector('.ant-pagination')).toMatchSnapshot();
wrapper.find('.ant-select-selector').simulate('mousedown');
wrapper.find('.ant-select-item-option').at(2).simulate('click');
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector'));
fireEvent.click(wrapper.querySelectorAll('.ant-select-item-option')[2]);
wrapper.find('.ant-select-selector').simulate('mousedown');
expect(wrapper.find('Pagination').first().render()).toMatchSnapshot();
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector'));
expect(wrapper.querySelector('.ant-pagination')).toMatchSnapshot();
});
// https://github.com/ant-design/ant-design/issues/24913
@ -141,7 +167,7 @@ describe('List.pagination', () => {
it('should onChange called when pageSize change', () => {
const handlePaginationChange = jest.fn();
const handlePageSizeChange = () => {};
const wrapper = mount(
const { container: wrapper } = render(
createList({
pagination: {
...pagination,
@ -152,13 +178,13 @@ describe('List.pagination', () => {
}),
);
wrapper.find('.ant-select-selector').simulate('mousedown');
wrapper.find('.ant-select-item-option').at(1).simulate('click');
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector'));
fireEvent.click(wrapper.querySelectorAll('.ant-select-item-option')[1]);
expect(handlePaginationChange).toHaveBeenCalledWith(1, 10);
});
it('should default work', () => {
const wrapper = mount(
const { container: wrapper } = render(
createList({
pagination: {
defaultPageSize: 3,
@ -169,11 +195,11 @@ describe('List.pagination', () => {
}),
);
expect(wrapper.find('Pagination').first().render()).toMatchSnapshot();
expect(wrapper.querySelector('.ant-pagination')).toMatchSnapshot();
});
it('should not crash when pagination is null', () => {
mount(
render(
createList({
pagination: null,
}),

View File

@ -6857,11 +6857,11 @@ exports[`Locale Provider should display the text as ar 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -11944,11 +11944,11 @@ exports[`Locale Provider should display the text as az 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -17031,11 +17031,11 @@ exports[`Locale Provider should display the text as bg 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -22118,11 +22118,11 @@ exports[`Locale Provider should display the text as bn-bd 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -27205,11 +27205,11 @@ exports[`Locale Provider should display the text as by 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -32292,11 +32292,11 @@ exports[`Locale Provider should display the text as ca 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -37379,11 +37379,11 @@ exports[`Locale Provider should display the text as cs 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -42466,11 +42466,11 @@ exports[`Locale Provider should display the text as da 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -47553,11 +47553,11 @@ exports[`Locale Provider should display the text as de 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -52640,11 +52640,11 @@ exports[`Locale Provider should display the text as el 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -57727,11 +57727,11 @@ exports[`Locale Provider should display the text as en 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -62814,11 +62814,11 @@ exports[`Locale Provider should display the text as en-gb 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -67901,11 +67901,11 @@ exports[`Locale Provider should display the text as es 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -72988,11 +72988,11 @@ exports[`Locale Provider should display the text as et 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -78075,11 +78075,11 @@ exports[`Locale Provider should display the text as fa 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -83162,11 +83162,11 @@ exports[`Locale Provider should display the text as fi 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -88249,11 +88249,11 @@ exports[`Locale Provider should display the text as fr 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -93336,11 +93336,11 @@ exports[`Locale Provider should display the text as fr 2`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -98423,11 +98423,11 @@ exports[`Locale Provider should display the text as fr 3`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -103510,11 +103510,11 @@ exports[`Locale Provider should display the text as ga 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -108597,11 +108597,11 @@ exports[`Locale Provider should display the text as gl 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -113684,11 +113684,11 @@ exports[`Locale Provider should display the text as he 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -118771,11 +118771,11 @@ exports[`Locale Provider should display the text as hi 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -123858,11 +123858,11 @@ exports[`Locale Provider should display the text as hr 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -128945,11 +128945,11 @@ exports[`Locale Provider should display the text as hu 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -134032,11 +134032,11 @@ exports[`Locale Provider should display the text as hy-am 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -139119,11 +139119,11 @@ exports[`Locale Provider should display the text as id 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -144206,11 +144206,11 @@ exports[`Locale Provider should display the text as is 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -149293,11 +149293,11 @@ exports[`Locale Provider should display the text as it 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -154380,11 +154380,11 @@ exports[`Locale Provider should display the text as ja 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -159467,11 +159467,11 @@ exports[`Locale Provider should display the text as ka 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -164554,11 +164554,11 @@ exports[`Locale Provider should display the text as kk 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -169641,11 +169641,11 @@ exports[`Locale Provider should display the text as km 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -174728,11 +174728,11 @@ exports[`Locale Provider should display the text as kn 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -179815,11 +179815,11 @@ exports[`Locale Provider should display the text as ko 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -184902,11 +184902,11 @@ exports[`Locale Provider should display the text as ku 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -189989,11 +189989,11 @@ exports[`Locale Provider should display the text as ku-iq 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -195076,11 +195076,11 @@ exports[`Locale Provider should display the text as lt 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -200163,11 +200163,11 @@ exports[`Locale Provider should display the text as lv 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -205250,11 +205250,11 @@ exports[`Locale Provider should display the text as mk 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -210337,11 +210337,11 @@ exports[`Locale Provider should display the text as ml 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -215424,11 +215424,11 @@ exports[`Locale Provider should display the text as mn-mn 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -220511,11 +220511,11 @@ exports[`Locale Provider should display the text as ms-my 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -225598,11 +225598,11 @@ exports[`Locale Provider should display the text as nb 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -230685,11 +230685,11 @@ exports[`Locale Provider should display the text as ne-np 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -235772,11 +235772,11 @@ exports[`Locale Provider should display the text as nl 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -240859,11 +240859,11 @@ exports[`Locale Provider should display the text as nl-be 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -245946,11 +245946,11 @@ exports[`Locale Provider should display the text as pl 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -251033,11 +251033,11 @@ exports[`Locale Provider should display the text as pt 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -256120,11 +256120,11 @@ exports[`Locale Provider should display the text as pt-br 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -261207,11 +261207,11 @@ exports[`Locale Provider should display the text as ro 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -266294,11 +266294,11 @@ exports[`Locale Provider should display the text as ru 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -271381,11 +271381,11 @@ exports[`Locale Provider should display the text as sk 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -276468,11 +276468,11 @@ exports[`Locale Provider should display the text as sl 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -281555,11 +281555,11 @@ exports[`Locale Provider should display the text as sr 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -286642,11 +286642,11 @@ exports[`Locale Provider should display the text as sv 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -291729,11 +291729,11 @@ exports[`Locale Provider should display the text as ta 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -296816,11 +296816,11 @@ exports[`Locale Provider should display the text as th 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -301903,11 +301903,11 @@ exports[`Locale Provider should display the text as tk 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -306990,11 +306990,11 @@ exports[`Locale Provider should display the text as tr 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -312077,11 +312077,11 @@ exports[`Locale Provider should display the text as uk 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -317164,11 +317164,11 @@ exports[`Locale Provider should display the text as ur 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -322251,11 +322251,11 @@ exports[`Locale Provider should display the text as vi 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -327338,11 +327338,11 @@ exports[`Locale Provider should display the text as zh-cn 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -332425,11 +332425,11 @@ exports[`Locale Provider should display the text as zh-hk 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
@ -337512,11 +337512,11 @@ exports[`Locale Provider should display the text as zh-tw 1`] = `
class="ant-modal-mask ant-fade-appear ant-fade-appear-start ant-fade"
/>
<div
aria-labelledby="test-id"
class="ant-modal-wrap"
tabindex="-1"
>
<div
aria-labelledby="test-id"
aria-modal="true"
class="ant-modal ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"

View File

@ -33,7 +33,7 @@ const items = [
{ label: 'item 2', key: 'item-2' }, // which is required
{
label: 'sub menu',
key: 'submenu'
key: 'submenu',
children: [{ label: 'item 3', key: 'submenu-item-1' }],
},
];

View File

@ -1,76 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import Modal from '..';
import Button from '../../button';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
jest.mock('rc-util/lib/Portal');
class ModalTester extends React.Component {
constructor(props) {
super(props);
this.state = { visible: false };
}
componentDidMount() {
this.setState({ visible: true }); // eslint-disable-line react/no-did-mount-set-state
}
saveContainer = container => {
this.container = container;
};
getContainer = () => this.container;
render() {
const { visible } = this.state;
return (
<div>
<div ref={this.saveContainer} />
<Modal {...this.props} visible={visible} getContainer={this.getContainer}>
Here is content of Modal
</Modal>
</div>
);
}
}
describe('Modal', () => {
mountTest(Modal);
rtlTest(Modal);
it('render correctly', () => {
const wrapper = mount(<ModalTester />);
expect(wrapper.render()).toMatchSnapshot();
});
it('render without footer', () => {
const wrapper = mount(<ModalTester footer={null} />);
expect(wrapper.render()).toMatchSnapshot();
});
it('onCancel should be called', () => {
const onCancel = jest.fn();
const wrapper = mount(<Modal visible onCancel={onCancel} />);
wrapper.find('.ant-btn').first().simulate('click');
expect(onCancel).toHaveBeenCalled();
});
it('onOk should be called', () => {
const onOk = jest.fn();
const wrapper = mount(<Modal visible onOk={onOk} />);
wrapper.find('.ant-btn').last().simulate('click');
expect(onOk).toHaveBeenCalled();
});
it('support closeIcon', () => {
const wrapper = mount(<Modal closeIcon={<a>closeIcon</a>} visible />);
expect(wrapper.render()).toMatchSnapshot();
});
it('danger type', () => {
const wrapper = mount(<Modal okType="danger" visible />);
expect(wrapper.find(Button).last().props().danger).toBeTruthy();
});
});

View File

@ -0,0 +1,93 @@
import React from 'react';
import Modal from '..';
import type { ModalProps } from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { render, fireEvent } from '../../../tests/utils';
jest.mock('rc-util/lib/Portal');
class ModalTester extends React.Component<ModalProps, { visible: boolean }> {
state = { visible: false };
componentDidMount() {
this.setState({ visible: true }); // eslint-disable-line react/no-did-mount-set-state
}
container = React.createRef<HTMLDivElement>();
getContainer = () => this.container?.current!;
render() {
const { visible } = this.state;
return (
<div>
<div ref={this.container} />
<Modal {...this.props} visible={visible} getContainer={this.getContainer}>
Here is content of Modal
</Modal>
</div>
);
}
}
describe('Modal', () => {
mountTest(Modal);
rtlTest(Modal);
it('support closeIcon', () => {
render(<Modal closeIcon={<a>closeIcon</a>} visible />);
expect(document.body.querySelectorAll('.ant-modal-root')[0]).toMatchSnapshot();
});
it('render correctly', () => {
const { asFragment } = render(<ModalTester />);
expect(asFragment().firstChild).toMatchSnapshot();
});
it('render without footer', () => {
const { asFragment } = render(<ModalTester footer={null} />);
expect(asFragment().firstChild).toMatchSnapshot();
});
it('onCancel should be called', () => {
const onCancel = jest.fn();
render(<Modal visible onCancel={onCancel} />);
fireEvent.click(document.body.querySelectorAll('.ant-btn')[0]);
expect(onCancel).toHaveBeenCalled();
});
it('onOk should be called', () => {
const onOk = jest.fn();
render(<Modal visible onOk={onOk} />);
const btns = document.body.querySelectorAll('.ant-btn');
fireEvent.click(btns[btns.length - 1]);
expect(onOk).toHaveBeenCalled();
});
it('danger type', () => {
render(<Modal okType="danger" okText="123" visible />);
const btns = document.body.querySelectorAll('.ant-btn');
expect(btns[btns.length - 1].classList.contains('ant-btn-dangerous')).toBeTruthy();
});
it('mouse position', () => {
const Demo = () => {
const [visible, setVisible] = React.useState(false);
const containerRef = React.useRef<HTMLDivElement>(null);
return (
<div ref={containerRef}>
<div id="trigger" onClick={() => setVisible(true)}>
click me
</div>
<Modal visible={visible} getContainer={() => containerRef.current!} />
</div>
);
};
const { container } = render(<Demo />);
fireEvent.click(container.querySelectorAll('#trigger')[0]);
expect(
(container.querySelectorAll('.ant-modal')[0] as HTMLDivElement).style.transformOrigin,
).toBeTruthy();
});
});

View File

@ -2,11 +2,12 @@ import React from 'react';
import CSSMotion from 'rc-motion';
import { act } from 'react-dom/test-utils';
import { genCSSMotion } from 'rc-motion/lib/CSSMotion';
import { mount } from 'enzyme';
import Modal from '..';
import Button from '../../button';
import Input from '../../input';
import ConfigProvider from '../../config-provider';
import { render, fireEvent } from '../../../tests/utils';
import type { ModalFunc } from '../confirm';
jest.mock('rc-util/lib/Portal');
jest.mock('rc-motion');
@ -15,13 +16,14 @@ describe('Modal.hook', () => {
// Inject CSSMotion to replace with No transition support
const MockCSSMotion = genCSSMotion(false);
Object.keys(MockCSSMotion).forEach(key => {
// @ts-ignore
CSSMotion[key] = MockCSSMotion[key];
});
it('hooks support context', () => {
jest.useFakeTimers();
const Context = React.createContext('light');
let instance;
let instance: ReturnType<ModalFunc>;
const Demo = () => {
const [modal, contextHolder] = Modal.useModal();
@ -43,12 +45,12 @@ describe('Modal.hook', () => {
);
};
const wrapper = mount(<Demo />);
wrapper.find('button').simulate('click');
const { container } = render(<Demo />);
fireEvent.click(container.querySelectorAll('button')[0]);
expect(wrapper.find('.test-hook').text()).toEqual('bamboo');
expect(wrapper.find('.ant-btn').length).toBeTruthy();
expect(wrapper.find('.ant-modal-body').length).toBeTruthy();
expect(document.body.querySelectorAll('.test-hook')[0].textContent).toBe('bamboo');
expect(document.body.querySelectorAll('.ant-btn').length).toBeTruthy();
expect(document.body.querySelectorAll('.ant-modal-body').length).toBeTruthy();
// Update instance
act(() => {
@ -56,16 +58,14 @@ describe('Modal.hook', () => {
content: <div className="updated-content" />,
});
});
wrapper.update();
expect(wrapper.find('.updated-content')).toHaveLength(1);
expect(document.body.querySelectorAll('.updated-content')).toHaveLength(1);
// Destroy
act(() => {
instance.destroy();
jest.runAllTimers();
});
wrapper.update();
expect(wrapper.find('Modal')).toHaveLength(0);
expect(document.body.querySelectorAll('Modal')).toHaveLength(0);
jest.useRealTimers();
});
@ -88,14 +88,14 @@ describe('Modal.hook', () => {
);
};
const wrapper = mount(
const { container } = render(
<ConfigProvider direction="rtl">
<Demo />
</ConfigProvider>,
);
wrapper.find('button').simulate('click');
expect(wrapper.find('.ant-input-rtl').length).toBeTruthy();
fireEvent.click(container.querySelectorAll('button')[0]);
expect(document.body.querySelectorAll('.ant-input-rtl').length).toBeTruthy();
});
it('hooks modal should trigger onCancel', () => {
@ -125,14 +125,14 @@ describe('Modal.hook', () => {
);
};
const wrapper = mount(<Demo />);
const { container } = render(<Demo />);
wrapper.find('.open-hook-modal-btn').simulate('click');
wrapper.find('.ant-modal-confirm-btns .ant-btn').first().simulate('click');
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
fireEvent.click(document.body.querySelectorAll('.ant-modal-confirm-btns .ant-btn')[0]);
expect(cancelCount).toEqual(1); // click cancel btn, trigger onCancel
wrapper.find('.open-hook-modal-btn').simulate('click');
wrapper.find('.ant-modal-wrap').simulate('click');
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
fireEvent.click(document.body.querySelectorAll('.ant-modal-wrap')[0]);
expect(cancelCount).toEqual(2); // click modal wrapper, trigger onCancel
});
@ -160,10 +160,11 @@ describe('Modal.hook', () => {
);
};
const wrapper = mount(<Demo />);
wrapper.find('.open-hook-modal-btn').simulate('click');
expect(wrapper.find('.ant-modal-confirm-title').text()).toEqual('Bamboo');
const { container } = render(<Demo />);
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
expect(document.body.querySelectorAll('.ant-modal-confirm-title')[0].textContent).toEqual(
'Bamboo',
);
});
it('destroy before render', () => {
@ -188,9 +189,8 @@ describe('Modal.hook', () => {
);
};
const wrapper = mount(<Demo />);
wrapper.find('.open-hook-modal-btn').simulate('click');
expect(wrapper.exists('.ant-modal-confirm-title')).toBeFalsy();
const { container } = render(<Demo />);
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
expect(document.body.classList.contains('ant-modal-confirm-title')).toBeFalsy();
});
});

View File

@ -9,7 +9,7 @@ import { getTransitionName } from '../_util/motion';
export interface PopoverProps extends AbstractTooltipProps {
title?: React.ReactNode | RenderFunction;
content?: React.ReactNode | RenderFunction;
/** @private Used for Popconfirm. Safe to remove. */
/** @private Used For Popconfirm. Safe to remove. */
_overlay?: React.ReactNode;
}

View File

@ -34,7 +34,6 @@
.@{skeleton-title-prefix-cls} {
width: 100%;
height: @skeleton-title-height;
margin-top: @margin-md;
background: @skeleton-color;
border-radius: @skeleton-block-radius;
@ -262,23 +261,34 @@
}
.skeleton-color() {
position: relative;
overflow: hidden;
background: #fff;
&::after {
position: absolute;
top: 0;
right: -150%;
bottom: 0;
left: -150%;
background: linear-gradient(
90deg,
@skeleton-color 25%,
@skeleton-to-color 37%,
@skeleton-color 63%
);
background-size: 400% 100%;
animation: ~'@{skeleton-prefix-cls}-loading' 1.4s ease infinite;
content: "";
}
}
@keyframes ~"@{skeleton-prefix-cls}-loading" {
0% {
background-position: 100% 50%;
transform: translateX(-37.5%);
}
100% {
background-position: 0 50%;
transform: translateX(37.5%);
}
}

View File

@ -15,7 +15,9 @@ exports[`Tabs renderTabBar custom-tab-bar 1`] = `
>
<div
aria-hidden="false"
aria-labelledby="rc-tabs-test-tab-1"
class="ant-tabs-tabpane ant-tabs-tabpane-active"
id="rc-tabs-test-panel-1"
role="tabpanel"
tabindex="0"
>
@ -386,14 +388,16 @@ exports[`Tabs tabPosition remove card 1`] = `
>
<div
class="ant-tabs-nav-list"
style="transform:translate(0px, 0px)"
style="transform: translate(0px, 0px);"
>
<div
class="ant-tabs-tab ant-tabs-tab-active"
>
<div
aria-controls="rc-tabs-test-panel-1"
aria-selected="true"
class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-1"
role="tab"
tabindex="0"
>
@ -409,13 +413,13 @@ exports[`Tabs tabPosition remove card 1`] = `
class="ant-tabs-nav-operations ant-tabs-nav-operations-hidden"
>
<button
aria-controls="null-more-popup"
aria-controls="rc-tabs-test-more-popup"
aria-expanded="false"
aria-haspopup="listbox"
aria-hidden="true"
class="ant-tabs-nav-more"
id="null-more"
style="visibility:hidden;order:1"
id="rc-tabs-test-more"
style="visibility: hidden; order: 1;"
tabindex="-1"
type="button"
>
@ -454,7 +458,9 @@ exports[`Tabs tabPosition remove card 1`] = `
>
<div
aria-hidden="false"
aria-labelledby="rc-tabs-test-tab-1"
class="ant-tabs-tabpane ant-tabs-tabpane-active"
id="rc-tabs-test-panel-1"
role="tabpanel"
tabindex="0"
>

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount, render } from 'enzyme';
import { render, fireEvent } from '../../../tests/utils';
import Tabs from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -24,7 +24,7 @@ describe('Tabs', () => {
beforeEach(() => {
handleEdit = jest.fn();
wrapper = mount(
const { container } = render(
<Tabs type="editable-card" onEdit={handleEdit}>
<TabPane tab="foo" key="1">
foo
@ -34,52 +34,53 @@ describe('Tabs', () => {
{false}
</Tabs>,
);
wrapper = container;
});
it('add card', () => {
wrapper.find('.ant-tabs-nav-add').first().simulate('click');
fireEvent.click(wrapper.querySelector('.ant-tabs-nav-add'));
expect(handleEdit.mock.calls[0][1]).toBe('add');
});
it('remove card', () => {
wrapper.find('.anticon-close').simulate('click');
fireEvent.click(wrapper.querySelector('.anticon-close'));
expect(handleEdit).toHaveBeenCalledWith('1', 'remove');
});
it('validateElement', () => {
expect(wrapper.find('.ant-tabs-tab').length).toBe(1);
expect(wrapper.querySelectorAll('.ant-tabs-tab').length).toBe(1);
});
});
describe('tabPosition', () => {
it('remove card', () => {
const wrapper = render(
const { container } = render(
<Tabs tabPosition="left" tabBarExtraContent="xxx">
<TabPane tab="foo" key="1">
foo
</TabPane>
</Tabs>,
);
expect(wrapper).toMatchSnapshot();
expect(container.firstChild).toMatchSnapshot();
});
});
describe('renderTabBar', () => {
it('custom-tab-bar', () => {
const wrapper = render(
const { container } = render(
<Tabs renderTabBar={() => <div>custom-tab-bar</div>}>
<TabPane tab="foo" key="1">
foo
</TabPane>
</Tabs>,
);
expect(wrapper).toMatchSnapshot();
expect(container.firstChild).toMatchSnapshot();
});
});
it('warning for onNextClick', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
mount(<Tabs onNextClick={() => {}} />);
render(<Tabs onNextClick={() => {}} />);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Tabs] `onPrevClick` and `onNextClick` has been removed. Please use `onTabScroll` instead.',
);
@ -87,21 +88,21 @@ describe('Tabs', () => {
});
it('tabBarGutter should work', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Tabs tabBarGutter={0}>
<TabPane />
<TabPane />
<TabPane />
</Tabs>,
);
expect(wrapper.render()).toMatchSnapshot();
const wrapper2 = mount(
expect(wrapper.firstChild).toMatchSnapshot();
const { container: wrapper2 } = render(
<Tabs tabBarGutter={0} tabPosition="left">
<TabPane />
<TabPane />
<TabPane />
</Tabs>,
);
expect(wrapper2.render()).toMatchSnapshot();
expect(wrapper2.firstChild).toMatchSnapshot();
});
});

View File

@ -7,11 +7,11 @@ Here are the frequently asked questions about Ant Design and antd that you shoul
---
### Will you provide Sass/Stylus(etc.) style files in addition to the Less style files currently included?
## Will you provide Sass/Stylus(etc.) style files in addition to the Less style files currently included?
There is currently no plan to add support for Sass/Stylus(etc.) style files, but using tools on Google you can easily convert the provided Less files to your desired style format.
### `Select Dropdown DatePicker TimePicker Popover Popconfirm` disappears when I click another popup component inside it. How do I resolve this?
## `Select Dropdown DatePicker TimePicker Popover Popconfirm` disappears when I click another popup component inside it. How do I resolve this?
This is an old bug that has been fixed since `v3.11.x`. If you're using an older version, you can use `<Select getPopupContainer={trigger => trigger.parentElement}>` to render a component inside Popover. (Or other `getXxxxContainer` props)
@ -19,7 +19,7 @@ https://ant.design/components/select/#Select-props
Related issue: [#3487](https://github.com/ant-design/ant-design/issues/3487) [#3438](https://github.com/ant-design/ant-design/issues/3438)
### How do I prevent `Select Dropdown DatePicker TimePicker Popover Popconfirm` scrolling with the page?
## How do I prevent `Select Dropdown DatePicker TimePicker Popover Popconfirm` scrolling with the page?
Use `<Select getPopupContainer={trigger => trigger.parentElement}>` ([API reference](/components/select/#Select-props)) to render a component inside the scroll area. If you need to config this globally in your application, try `<ConfigProvider getPopupContainer={trigger => trigger.parentElement}>` ([API reference](/components/config-provider/#API))
@ -27,75 +27,69 @@ And make sure that parentElement is `position: relative` or `position: absolute`
Related issue: [#3487](https://github.com/ant-design/ant-design/issues/3487) [#3438](https://github.com/ant-design/ant-design/issues/3438)
### How do I modify the default theme of Ant Design?
## How do I modify the default theme of Ant Design?
See: https://ant.design/docs/react/customize-theme .
### How do I modify `Menu`/`Button`(etc.)'s style?
## How do I modify `Menu`/`Button`(etc.)'s style?
While you can override a component's style, we don't recommend doing so. antd is not only a set of React components, but also a design specification as well.
### How do I replace Moment.js with Day.js to reduce bundle size
## How do I replace Moment.js with Day.js to reduce bundle size
Please refer to [Replace Moment.js](/docs/react/replace-moment).
### It doesn't work when I change `defaultValue` dynamically.
## It doesn't work when I change `defaultValue` dynamically.
The `defaultXxxx` (e.g. `defaultValue`) of `Input`/`Select`(etc...) only works on the first render. It is a specification of React. Please read [React's documentation](https://facebook.github.io/react/docs/forms.html#controlled-components).
### Why does modifying props in mutable way not trigger a component update?
## Why does modifying props in mutable way not trigger a component update?
antd use shallow compare of props to optimize performance. You should always pass the new object when updating the state. Please ref [React's document](https://reactjs.org/docs/thinking-in-react.html)
### After I set the `value` of an `Input`/`Select`(etc.) component, the value cannot be changed by user's action.
## After I set the `value` of an `Input`/`Select`(etc.) component, the value cannot be changed by user's action.
Try `onChange` to change `value`, and please read [React's documentation](https://reactjs.org/docs/forms.html#controlled-components).
### Components are not vertically aligned when placed in single row.
## Components are not vertically aligned when placed in single row.
Try [Space](https://ant.design/components/space/) component to make them aligned.
### antd overrides my global styles
## antd overrides my global styles
Yes, antd is designed to help you develop a complete background application. To do so, we override some global styles for styling convenience, and currently these cannot be removed or changed. More info at https://github.com/ant-design/ant-design/issues/4331 .
Alternatively, follow the instructions in [How to avoid modifying global styles?](/docs/react/customize-theme#How-to-avoid-modifying-global-styles)
### I cannot install `antd` and `antd`'s dependencies in mainland China.
## I cannot install `antd` and `antd`'s dependencies in mainland China.
To potentially solve this, try [cnpm](http://npm.taobao.org/).
To potentially solve this, try [npm mirror china](https://npmmirror.com) and [cnpm](https://github.com/cnpm/cnpm).
### I set `dependencies.antd` as the git repository in `package.json`, but it doesn't work.
## I set `dependencies.antd` as the git repository in `package.json`, but it doesn't work.
Please install `antd` with either npm or yarn.
### `message` and `notification` is lower case, but other components are capitalized. Is this a typo?
## `message` and `notification` is lower case, but other components are capitalized. Is this a typo?
No, `message` is just a function, not a React Component, thus it is not a typo that it is in lower case.
### `antd` doesn't work well in mobile.
## `antd` doesn't work well in mobile.
Please check [Ant Design Mobile](http://mobile.ant.design) as a possible solution, as `antd` has not been optimized to work well on mobile. You can also try the [react-component](https://github.com/react-component/) repositories which start with 'm-' 'rn-', which are also designed for mobile.
### Does `antd` supply standalone files like 'React'?
## Does `antd` supply standalone files like 'React'?
Yes, you can [import `antd` with script tag](https://ant.design/docs/react/introduce#Import-in-Browser), but we recommend using `npm` to import `antd`, as it is simple and easy to maintain.
### I can't visit `icon` in my network environment.
You should deploy the iconfont files to your network by following this [example](https://github.com/ant-design/antd-init/tree/7c1a33cadb98f2fd8688fe527dd7f98215b9bced/examples/local-iconfont). [#1070](https://github.com/ant-design/ant-design/issues/1070)
After 3.9.x [we will also switch to using svg icons](/components/icon#svg-icons), so you won't need to deploy iconfont locally anymore as well.
### How do I extend antd's components?
## How do I extend antd's components?
If you need some features which should not be included in antd, try to extend antd's component with [HOC](https://gist.github.com/sebmarkbage/ef0bf1f338a7182b6775). [more](https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750#.eeu8q01s1)
### How do I fix dynamic styles while using a Content Security Policy (CSP)?
## How do I fix dynamic styles while using a Content Security Policy (CSP)?
You can configure `nonce` by [ConfigProvider](/components/config-provider/#Content-Security-Policy).
### When I set `mode` to `DatePicker`/`RangePicker`, why can I not select a year or month anymore?
## When I set `mode` to `DatePicker`/`RangePicker`, why can I not select a year or month anymore?
In a real world development, you may need a `YearPicker`, `MonthRangePicker` or `WeekRangePicker`. You are trying to add `mode` to `DatePicker`/`RangePicker` expected to implement those pickers. However, the `DatePicker`/`RangePicker` cannot be selected and the panels won't close now.
@ -106,13 +100,13 @@ Like [the explaination](https://github.com/ant-design/ant-design/issues/11586#is
Likewise`disabledDate` [cannot work on year/month panels](https://github.com/ant-design/ant-design/issues/9008#issuecomment-358554118) of `<DatePicker mode="year/month" />`, but only on cells of date panel.
##### Workaround
### Workaround
You can refer to [this article](https://juejin.im/post/5cf65c366fb9a07eca6968f9) or [this article](https://www.cnblogs.com/zyl-Tara/p/10197177.html), using `mode` and `onPanelChange` to encapsulate a `YearPicker` or `MonthRangePicker` for your needs.
Or you can simply upgrade to [antd@4.0](https://github.com/ant-design/ant-design/issues/16911), in which we [added more XxxPickers](https://github.com/ant-design/ant-design/issues/4524#issuecomment-480576884) to meet those requirements, and `disabledDate` could be effect on those pickers too.
### message/notification/Modal.confirm lost styles when set `prefixCls` on ConfigProvider?
## message/notification/Modal.confirm lost styles when set `prefixCls` on ConfigProvider?
Static methods like message/notification/Modal.confirm are not using the same render tree as `<Button />`, but rendered to independent DOM node created by `ReactDOM.render`, which cannot access React context from ConfigProvider. Consider two solutions here:
@ -126,11 +120,11 @@ ConfigProvider.config({
});
```
### Why shouldn't I use component internal props or state with ref?
## Why shouldn't I use component internal props or state with ref?
You should only access the API by official doc with ref. Directly access internal `props` or `state` is not recommended which will make your code strong coupling with current version. Any refactor will break your code like refactor with [Hooks](https://reactjs.org/docs/hooks-intro.html) version, delete or rename internal `props` or `state`, adjust internal node constructor, etc.
### How to spell Ant Design correctly?
## How to spell Ant Design correctly?
- ✅ **Ant Design**: Capitalized with space, for the design language.
- ✅ **antd**: all lowercase, for the React UI library.
@ -146,36 +140,6 @@ Here are some typical wrong examples:
- ❌ antdesign
- ❌ Antdesign
### Do you guys have any channel or website for submitting monetary donations, like through PayPal or Alipay?
## Do you guys have any channel or website for submitting monetary donations, like through PayPal or Alipay?
[https://opencollective.com/ant-design](https://opencollective.com/ant-design)
---
## Errors and Warnings
Here are some errors & warnings that you may encounter while using antd, although most of these are not actual bugs of antd itself.
### Adjacent JSX elements must be wrapped in an enclosing tag
Check out [this answer from StackOverflow](http://stackoverflow.com/questions/25034994/how-to-correctly-wrap-few-td-tags-for-jsxtransformer), along with also reading [React's documentation](http://facebook.github.io/react/docs/displaying-data.html#components-are-just-like-functions) to solve this.
### React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components)
Please make sure that you import `antd`'s components correctly. Read the corresponding documentation of the `antd`'s version which you use, and pay attention to typos.
### rm is not recognized as an internal or external command
Please read [this issue](https://github.com/ant-design/ant-design/issues/650#issuecomment-164966511), or try Linux/Unix.
### Failed propType: Invalid prop `AAA` of type `BBB` supplied to `CCC`, expected `DDD`. Check the render method of `EEE`.
Please read the corresponding documentation of the `antd`'s version which you are currently using, and make sure that you pass values with correct type to `antd`'s components,
### Unknown option: xxx/package.json.presets
Check out [this answer from Stack Overflow](http://stackoverflow.com/questions/33685365/unknown-option-babelrc-presets).
### Invariant Violation: findComponentRoot(...): Unable to find element.
You may have imported `React` twice. Set `React` & `ReactDOM` as external, if you are using webpack, see [#525](https://github.com/ant-design/ant-design/issues/525). If you are using others (browserify, etc...), please read their documentation and find options which can set `React` & `ReactDOM` as external.

View File

@ -7,11 +7,11 @@ title: FAQ
---
### 你们会提供 Sass/Stylus 等格式的样式文件吗?
## 你们会提供 Sass/Stylus 等格式的样式文件吗?
暂无计划。事实上你可以使用工具(请自行 Google将 Less 转换成 Sass/Stylus 等。
### 当我点击 `Select Dropdown DatePicker TimePicker Popover Popconfirm` 内的另一个 popup 组件时它会消失,如何解决?
## 当我点击 `Select Dropdown DatePicker TimePicker Popover Popconfirm` 内的另一个 popup 组件时它会消失,如何解决?
该问题在 `3.11.0` 后已经解决。如果你仍在使用旧版本,你可以通过 `<Select getPopupContainer={trigger => trigger.parentElement}>` 来在 Popover 中渲染组件,或者使用其他的 `getXxxxContainer` 参数。
@ -19,7 +19,7 @@ title: FAQ
相关 issue[#3487](https://github.com/ant-design/ant-design/issues/3487) [#3438](https://github.com/ant-design/ant-design/issues/3438)
### `Select Dropdown DatePicker TimePicker Popover Popconfirm` 会跟随滚动条上下移动?
## `Select Dropdown DatePicker TimePicker Popover Popconfirm` 会跟随滚动条上下移动?
使用 `<Select getPopupContainer={trigger => trigger.parentElement}>`[API 文档](/components/select/#Select-props))来将组件渲染到滚动区域内,或者使用其他的 `getXxxxContainer` 参数。如果需要全局解决这个问题,可以使用 `<ConfigProvider getPopupContainer={trigger => trigger.parentElement}>`[API 文档](/components/config-provider/#API)
@ -27,57 +27,57 @@ title: FAQ
相关 issue[#3487](https://github.com/ant-design/ant-design/issues/3487) [#3438](https://github.com/ant-design/ant-design/issues/3438)
### 如何修改 Ant Design 的默认主题?
## 如何修改 Ant Design 的默认主题?
可以参考[定制主题](/docs/react/customize-theme)。
### 如何修改 Ant Design 组件的默认样式?
## 如何修改 Ant Design 组件的默认样式?
你可以覆盖它们的样式但是我们不推荐这么做。antd 是一系列 React 组件,但同样是一套设计规范。
### 如何使用 Day.js 替换 Moment.js 来减小打包大小?
## 如何使用 Day.js 替换 Moment.js 来减小打包大小?
可以参考[替换 Moment.js](/docs/react/replace-moment)。
### 当我动态改变 `defaultValue` 的时候它并没有生效。
## 当我动态改变 `defaultValue` 的时候它并没有生效。
`Input`/`Select` 等的 `defaultXxxx`(例如 `defaultValue`)只有在第一次渲染的时候有效,这是 React 的规范,请阅读 [React 的文档](https://zh-hans.reactjs.org/docs/forms.html#controlled-components)。
### 为什么修改组件传入的对象或数组属性组件不会更新?
## 为什么修改组件传入的对象或数组属性组件不会更新?
antd 内部会对 props 进行浅比较实现性能优化。当状态变更,你总是应该传递一个新的对象。具体请参考 [React 的文档](https://zh-hans.reactjs.org/docs/thinking-in-react.html)
### 当我设置了 `Input`/`Select` 等的 `value` 时它就无法修改了。
## 当我设置了 `Input`/`Select` 等的 `value` 时它就无法修改了。
尝试使用 `onChange` 来改变 `value`,请参考 [React 的文档](https://zh-hans.reactjs.org/docs/forms.html#controlled-components)。
### 多个组件放一排时没有垂直对齐怎么办?
## 多个组件放一排时没有垂直对齐怎么办?
尝试使用 [Space](/components/space/) 组件来使他们对齐。
### antd 覆盖了我的全局样式!
## antd 覆盖了我的全局样式!
是的antd 在设计的时候就是用来开发一个完整的应用的,为了方便,我们覆盖了一些全局样式,现在还不能移除,想要了解更多请追踪 [这个 issue](https://github.com/ant-design/ant-design/issues/4331),或者参考这个教程 [How to avoid modifying global styles?](/docs/react/customize-theme#How-to-avoid-modifying-global-styles)
### 我没法安装 `antd``antd` 的依赖,顺便提一句,我在中国大陆。
## 我没法安装 `antd``antd` 的依赖,顺便提一句,我在中国大陆。
那啥,试试 [cnpm](http://npm.taobao.org/)。
那啥,试试 [npmmirror 国内镜像](https://npmmirror.com) 和 [cnpm](https://github.com/cnpm/cnpm)。
### 我在 package.json 里将 `dependencies.antd` 添加到了 git repository 中,但是没有用。
## 我在 package.json 里将 `dependencies.antd` 添加到了 git repository 中,但是没有用。
当然没用了,请使用 npm 安装 `antd`
### `message``notification` 是小写的,但是其他的组件都是首字母大写的,这是手滑吗?
## `message``notification` 是小写的,但是其他的组件都是首字母大写的,这是手滑吗?
不,因为 `message` 是一个函数,而不是一个 React 组件。
### `antd` 在移动端体验不佳。
## `antd` 在移动端体验不佳。
请浏览 [Ant Design Mobile](http://mobile.ant.design) 以了解详情,`antd` 并非针对移动端设计。你可以试试 [react-component](https://github.com/react-component/),其中带有 'm-' 'rn-' 前缀的库是为移动端设计的。
### `antd` 是否有国内镜像?
## `antd` 是否有国内镜像?
有的,你可以点击 https://ant-design.gitee.io/index-cn 访问
有的,你可以访问 https://ant-design.antgroup.com/index-cn 或 https://ant-design.gitee.io/index-cn
历史版本:
@ -85,31 +85,31 @@ antd 内部会对 props 进行浅比较实现性能优化。当状态变更,
- 2.x: https://ant-design-2x.gitee.io/
- 1.x: https://ant-design-1x.gitee.io/
### `antd` 会像 `React` 那样提供单文件引入吗?
## `antd` 会像 `React` 那样提供单文件引入吗?
是的,[你可以用 script 标签引入](https://ant.design/docs/react/introduce-cn#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%BC%95%E5%85%A5)。但是我们推荐使用 `npm` 来引入 `antd`,这样维护起来更简单方便。
### 在我的网络环境下没法获取到 `icon` 文件。
## 在我的网络环境下没法获取到 `icon` 文件。
你应该自行部署 iconfont 文件到你的网络上,参考这个[例子](https://github.com/ant-design/antd-init/tree/7c1a33cadb98f2fd8688fe527dd7f98215b9bced/examples/local-iconfont)。 [#1070](https://github.com/ant-design/ant-design/issues/1070)
`3.9.x` 版本后,[我们会使用 svg 图标](/components/icon#svg-icons),你就不用担心本地部署 iconfont 的问题了!
### 如何拓展 antd 的组件?
## 如何拓展 antd 的组件?
如果你需要一些 antd 没有包含的功能,你可以尝试通过 [HOC](https://gist.github.com/sebmarkbage/ef0bf1f338a7182b6775) 拓展 antd 的组件。 [更多](https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750#.eeu8q01s1)
### 我的组件默认语言是英文的?如何切回中文的。
## 我的组件默认语言是英文的?如何切回中文的。
请尝试使用 [ConfigProvider](/components/config-provider/#components-config-provider-demo-locale) 组件来包裹你的应用。
如果日期组件的国际化仍未生效,请配置 `moment.locale('zh-cn')` 并**检查你本地的 `moment` 版本和 `antd` 依赖的 `moment` 版本是否一致**。
### 开启了 Content Security Policy (CSP) 如何处理动态样式?
## 开启了 Content Security Policy (CSP) 如何处理动态样式?
你可以通过 [ConfigProvider](/components/config-provider/#Content-Security-Policy) 来配置 `nonce` 属性。
### 当我指定了 DatePicker/RangePicker 的 `mode` 属性后,点击后无法选择年份/月份?
## 当我指定了 DatePicker/RangePicker 的 `mode` 属性后,点击后无法选择年份/月份?
在业务开发中,你可能有年份选择,月份范围选择,周范围选择等需求,此时你给现有组件增加了 `mode` 属性,却发现无法进行点击选择行为,面板也不会关闭。如果给面板添加 `disabledDate` 也不会相应禁用对应的年/月/周。
@ -120,13 +120,13 @@ antd 内部会对 props 进行浅比较实现性能优化。当状态变更,
同样的,`disabledDate` 对于任何 `<DatePicker />` 也只会针对**日面板**生效,[并不会对 `<DatePicker mode="year/month" />` 上的年/月面板生效](https://github.com/ant-design/ant-design/issues/9008#issuecomment-358554118)。
##### 解决办法
### 解决办法
你可以参照 [这篇文章](https://juejin.im/post/5cf65c366fb9a07eca6968f9) 或者 [这篇文章](https://www.cnblogs.com/zyl-Tara/p/10197177.html) 里的做法,利用 `mode``onPanelChange` 等方法去封装一个 `YearPicker` 等组件。
另外我们已经在在 [antd@4.0](https://github.com/ant-design/ant-design/issues/16911) 中直接[添加了更多相关日期组件](https://github.com/ant-design/ant-design/issues/4524#issuecomment-480576884)来支持这些需求,现在不再需要使用 `mode="year|month"`,而是直接可以用 `YearPicker` `MonthPicker`,并且 `disabledDate` 也可以正确作用于这些 Picker。
### ConfigProvider 设置 `prefixCls`message/notification/Modal.confirm 生成的节点样式丢失了?
## ConfigProvider 设置 `prefixCls`message/notification/Modal.confirm 生成的节点样式丢失了?
message/notification/Modal.confirm 等静态方法不同于 `<Button />` 的渲染方式,是单独渲染在 `ReactDOM.render` 生成的 DOM 树节点上,无法共享 ConfigProvider 提供的 context 信息。你有两种解决方式:
@ -140,11 +140,11 @@ ConfigProvider.config({
});
```
### 为什么我不应该通过 ref 访问组件内部的 props 和 state
## 为什么我不应该通过 ref 访问组件内部的 props 和 state
你通过 ref 获得引用时只应该使用文档提供的方法。直接读取组件内部的 `props``state` 不是一个好的设计,这会使你的代码与组件版本强耦合。任何重构都可能会使你的代码无法工作,其中重构包括且不仅限于改造成 [Hooks](https://reactjs.org/docs/hooks-intro.html) 版本、移除 / 更名内部 `props``state`、调整内部 React 节点结构等等。
### 如何正确的拼写 Ant Design
## 如何正确的拼写 Ant Design
- ✅ **Ant Design**:用空格分隔的首字母大写单词,指代设计语言。
- ✅ **antd**:全小写,指代 React UI 组件库。
@ -161,36 +161,6 @@ ConfigProvider.config({
- ❌ antdesign
- ❌ Antdesign
### 你们有接受捐助的渠道吗,比如支付宝或者微信支付?
## 你们有接受捐助的渠道吗,比如支付宝或者微信支付?
[https://opencollective.com/ant-design](https://opencollective.com/ant-design)
---
## 错误和警告
这里是一些你在使用 antd 的过程中可能会遇到的错误和警告,但是其中一些并不是 antd 的 bug。
### Adjacent JSX elements must be wrapped in an enclosing tag
这里有一篇[来自 StackOverflow 的回答](http://stackoverflow.com/questions/25034994/how-to-correctly-wrap-few-td-tags-for-jsxtransformer),另外请阅读 [React 的文档](http://facebook.github.io/react/docs/displaying-data.html#components-are-just-like-functions)。
### React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components)
请确保你正确引入了 `antd` 的组件。参考 `antd` 相应组件的文档,注意你代码中的 typo。
### rm is not recognized as an internal or external command
请阅读这个 [issue](https://github.com/ant-design/ant-design/issues/650#issuecomment-164966511),或者试试 Linux/Unix。
### Failed propType: Invalid prop `AAA` of type `BBB` supplied to `CCC`, expected `DDD`. Check the render method of `EEE`.
请阅读你正在使用版本的 `antd` 的文档,确保你传递给 `antd` 组件的参数类型正确。
### Unknown option: xxx/package.json.presets
这里有一篇[来自 StackOverflow 的回答](http://stackoverflow.com/questions/33685365/unknown-option-babelrc-presets)可以参考。
### Invariant Violation: findComponentRoot(...): Unable to find element.
你或许引入了 React 两次。如果你使用 webpack请将 React & ReactDOM 设置为 external参见[#525](https://github.com/ant-design/ant-design/issues/525)。如果你使用其他工具browserify 等),请阅读它们的文档并将 React & ReactDOM 设置为 external。

View File

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "4.20.6",
"version": "4.20.7",
"description": "An enterprise-class UI design language and React components implementation",
"title": "Ant Design",
"keywords": [
@ -126,7 +126,7 @@
"rc-cascader": "~3.5.0",
"rc-checkbox": "~2.3.0",
"rc-collapse": "~3.3.0",
"rc-dialog": "~8.8.1",
"rc-dialog": "~8.8.2",
"rc-drawer": "~4.4.2",
"rc-dropdown": "~4.0.0",
"rc-field-form": "~1.26.1",
@ -161,7 +161,6 @@
"devDependencies": {
"@ant-design/bisheng-plugin": "^3.2.0",
"@ant-design/hitu": "^0.0.0-alpha.13",
"@ant-design/jest-image-snapshot": "^4.5.2",
"@ant-design/tools": "^15.0.2",
"@docsearch/css": "^3.0.0",
"@qixian.cs/github-contributors-list": "^1.0.3",
@ -216,7 +215,7 @@
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-compat": "^4.0.0",
"eslint-plugin-import": "^2.21.1",
"eslint-plugin-jest": "~26.3.0",
"eslint-plugin-jest": "^26.4.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-markdown": "^2.0.0",
"eslint-plugin-react": "^7.28.0",
@ -239,6 +238,7 @@
"jest-axe": "^6.0.0",
"jest-environment-jsdom": "^28.0.2",
"jest-environment-node": "^28.0.2",
"jest-image-snapshot": "^5.1.0",
"jest-puppeteer": "^6.0.0",
"jquery": "^3.4.1",
"jsdom": "^19.0.0",

View File

@ -1,7 +1,7 @@
import React from 'react';
// Reference: https://github.com/ant-design/ant-design/pull/24003#discussion_r427267386
// eslint-disable-next-line import/no-unresolved
import { configureToMatchImageSnapshot } from '@ant-design/jest-image-snapshot';
import { configureToMatchImageSnapshot } from 'jest-image-snapshot';
import ReactDOMServer from 'react-dom/server';
import glob from 'glob';
import MockDate from 'mockdate';

View File

@ -1,145 +0,0 @@
// Type definitions for jest-image-snapshot 4.3
// Project: https://github.com/americanexpress/jest-image-snapshot#readme
// Definitions by: Janeene Beeforth <https://github.com/dawnmist>
// erbridge <https://github.com/erbridge>
// Piotr Błażejewicz <https://github.com/peterblazejewicz>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 3.8
/// <reference types="jest" />
declare module '@ant-design/jest-image-snapshot' {
import { PixelmatchOptions } from 'pixelmatch';
import { Options as SSIMOptions } from 'ssim.js';
export interface MatchImageSnapshotOptions {
/**
* If set to true, the build will not fail when the screenshots to compare have different sizes.
*
* @default false
*/
allowSizeMismatch?: boolean | undefined;
/** Custom config passed to 'pixelmatch' or 'ssim' */
customDiffConfig?: PixelmatchOptions | Partial<SSIMOptions> | undefined;
/**
* The method by which images are compared. `pixelmatch` does a pixel by pixel comparison,
* whereas `ssim` does a structural similarity comparison.
*
* @default 'pixelmatch'
*/
comparisonMethod?: 'pixelmatch' | 'ssim' | undefined;
/** Custom snapshots directory. Absolute path of a directory to keep the snapshot in. */
customSnapshotsDir?: string | undefined;
/** A custom absolute path of a directory to keep this diff in */
customDiffDir?: string | undefined;
/**
* A custom name to give this snapshot. If not provided, one is computed automatically. When a
* function is provided it is called with an object containing testPath, currentTestName,
* counter and defaultIdentifier as its first argument. The function must return an identifier
* to use for the snapshot.
*/
customSnapshotIdentifier?:
| ((parameters: {
testPath: string;
currentTestName: string;
counter: number;
defaultIdentifier: string;
}) => string)
| string
| undefined;
/**
* Changes diff image layout direction.
*
* @default 'horizontal'
*/
diffDirection?: 'horizontal' | 'vertical' | undefined;
/**
* Will output base64 string of a diff image to console in case of failed tests (in addition to
* creating a diff image). This string can be copy-pasted to a browser address string to preview
* the diff for a failed test.
*
* @default false
*/
dumpDiffToConsole?: boolean | undefined;
/**
* Will output the image to the terminal using iTerm's Inline Images Protocol. If the term is
* not compatible, it does the same thing as `dumpDiffToConsole`.
*
* @default false
*/
dumpInlineDiffToConsole?: boolean | undefined;
/**
* Removes coloring from the console output, useful if storing the results to a file.
*
* @default false
*/
noColors?: boolean | undefined;
/**
* Sets the threshold that would trigger a test failure based on the failureThresholdType
* selected. This is different to the customDiffConfig.threshold above - the
* customDiffConfig.threshold is the per pixel failure threshold, whereas this is the failure
* threshold for the entire comparison.
*
* @default 0
*/
failureThreshold?: number | undefined;
/**
* Sets the type of threshold that would trigger a failure.
*
* @default 'pixel'
*/
failureThresholdType?: 'pixel' | 'percent' | undefined;
/**
* Updates a snapshot even if it passed the threshold against the existing one.
*
* @default false
*/
updatePassedSnapshot?: boolean | undefined;
/**
* Applies Gaussian Blur on compared images, accepts radius in pixels as value. Useful when you
* have noise after scaling images per different resolutions on your target website, usually
* setting its value to 1-2 should be enough to solve that problem.
*
* @default 0
*/
blur?: number | undefined;
/**
* Runs the diff in process without spawning a child process.
*
* @default false
*/
runInProcess?: boolean | undefined;
}
/**
* Function to be passed to jest's expect.extend. Example: import { toMatchImageSnapshot } from
* 'jest-image-snapshot'; expect.extend({ toMatchImageSnapshot });
*/
export function toMatchImageSnapshot(options?: MatchImageSnapshotOptions): {
message(): string;
pass: boolean;
};
/**
* Configurable function that can be passed to jest's expect.extend. Example: import {
* configureToMatchImageSnapshot } from 'jest-image-snapshot'; const toMatchImageSnapshot =
* configureToMatchImageSnapshot({ noColors: true }); expect.extend({ toMatchImageSnapshot });
*/
export function configureToMatchImageSnapshot(
options: MatchImageSnapshotOptions,
): () => { message(): string; pass: boolean };
/** Mutates original state with new state */
export function updateSnapshotState<TObject, TPartial>(
originalSnapshotState: TObject,
partialSnapshotState: TPartial,
): TObject & TPartial;
declare global {
namespace jest {
interface Matchers<R, T> {
toMatchImageSnapshot(options?: MatchImageSnapshotOptions): R;
}
}
}
}