mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-05 05:28:20 +08:00
commit
0cbd73fa8a
@ -148,6 +148,7 @@ module.exports = {
|
||||
'jest/no-done-callback': 0,
|
||||
'jest/valid-title': 0,
|
||||
'jest/no-conditional-expect': 0,
|
||||
'jest/no-standalone-expect': 0,
|
||||
|
||||
'unicorn/better-regex': 2,
|
||||
'unicorn/prefer-string-trim-start-end': 2,
|
||||
|
@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Alert from '..';
|
||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, act } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
import Popconfirm from '../../popconfirm';
|
||||
import Tooltip from '../../tooltip';
|
||||
@ -33,13 +32,13 @@ describe('Alert', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
jest.useFakeTimers();
|
||||
fireEvent.click(container.querySelector('.ant-alert-close-icon')!);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
describe('action of Alert', () => {
|
||||
|
@ -221,4 +221,11 @@ describe('Avatar Render', () => {
|
||||
expect(wrapper.querySelector('[crossorigin]')).toBeNull();
|
||||
expect(getByRole('img').getAttribute('crossOrigin')).toEqual(null);
|
||||
});
|
||||
|
||||
it('clickable', async () => {
|
||||
const onClick = jest.fn();
|
||||
const { container } = render(<Avatar onClick={onClick}>TestString</Avatar>);
|
||||
fireEvent.click(container.querySelector('.ant-avatar-string'));
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -33,6 +33,7 @@ export interface AvatarProps {
|
||||
children?: React.ReactNode;
|
||||
alt?: string;
|
||||
crossOrigin?: '' | 'anonymous' | 'use-credentials';
|
||||
onClick?: (e?: React.MouseEvent<HTMLElement>) => void;
|
||||
/* callback when img load error */
|
||||
/* return false to prevent Avatar show default fallback behavior, then you can do fallback by your self */
|
||||
onError?: () => boolean;
|
||||
|
@ -9,6 +9,7 @@ import zhCN from '../../locale/zh_CN';
|
||||
import Modal from '../../modal';
|
||||
import Pagination from '../../pagination';
|
||||
import TimePicker from '../../time-picker';
|
||||
import { act } from '../../../tests/utils';
|
||||
|
||||
describe('ConfigProvider.Locale', () => {
|
||||
function $$(className) {
|
||||
@ -44,7 +45,9 @@ describe('ConfigProvider.Locale', () => {
|
||||
title: 'title',
|
||||
content: 'Some descriptions',
|
||||
});
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
jest.useRealTimers();
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@ import React from 'react';
|
||||
import ConfigProvider from '..';
|
||||
import Affix from '../../affix';
|
||||
import Anchor from '../../anchor';
|
||||
import { act } from '../../../tests/utils';
|
||||
|
||||
describe('ConfigProvider.getTargetContainer', () => {
|
||||
it('Affix', () => {
|
||||
@ -16,7 +17,9 @@ describe('ConfigProvider.getTargetContainer', () => {
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(getTargetContainer).toHaveBeenCalled();
|
||||
jest.useRealTimers();
|
||||
@ -33,7 +36,9 @@ describe('ConfigProvider.getTargetContainer', () => {
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(getTargetContainer).toHaveBeenCalled();
|
||||
jest.useRealTimers();
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Form from '..';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, act } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
import Input from '../../input';
|
||||
|
||||
@ -43,8 +42,8 @@ describe('Form.List', () => {
|
||||
);
|
||||
|
||||
function operate(className) {
|
||||
act(() => {
|
||||
fireEvent.click(container.querySelector(className));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
}
|
||||
|
@ -81,6 +81,7 @@ import {
|
||||
Transfer,
|
||||
} from '../..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import { act } from '../../../tests/utils';
|
||||
import arEG from '../ar_EG';
|
||||
import azAZ from '../az_AZ';
|
||||
import bgBG from '../bg_BG';
|
||||
@ -296,7 +297,9 @@ describe('Locale Provider', () => {
|
||||
Modal.confirm({
|
||||
title: 'Hello World!',
|
||||
});
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
jest.useRealTimers();
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import Mentions from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { act } from '../../../tests/utils';
|
||||
|
||||
const { getMentions } = Mentions;
|
||||
|
||||
@ -68,7 +69,9 @@ describe('Mentions', () => {
|
||||
expect(onFocus).toHaveBeenCalled();
|
||||
|
||||
wrapper.find('textarea').simulate('blur');
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-mentions').hasClass('ant-mentions-focused')).toBeFalsy();
|
||||
expect(onBlur).toHaveBeenCalled();
|
||||
|
@ -7,11 +7,10 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { mount } from 'enzyme';
|
||||
import React, { useState } from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Menu from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import { fireEvent, render, act } from '../../../tests/utils';
|
||||
import Layout from '../../layout';
|
||||
import Tooltip from '../../tooltip';
|
||||
import initCollapseMotion from '../../_util/motion';
|
||||
@ -378,8 +377,8 @@ describe('Menu', () => {
|
||||
wrapper.setProps({ inlineCollapsed: true });
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('ul.ant-menu-root').hasClass('ant-menu-vertical')).toBeTruthy();
|
||||
expect(wrapper.find('PopupTrigger').prop('visible')).toBeFalsy();
|
||||
@ -388,8 +387,8 @@ describe('Menu', () => {
|
||||
wrapper.setProps({ inlineCollapsed: false });
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('ul.ant-menu-sub').last().hasClass('ant-menu-inline')).toBeTruthy();
|
||||
expect(wrapper.find('InlineSubMenuList').prop('open')).toBeTruthy();
|
||||
@ -708,8 +707,8 @@ describe('Menu', () => {
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('.ant-tooltip-inner').length).toBe(0);
|
||||
});
|
||||
@ -752,7 +751,9 @@ describe('Menu', () => {
|
||||
);
|
||||
|
||||
wrapper.find('.ant-menu-item').hostNodes().simulate('mouseenter');
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('.ant-tooltip-inner').length).toBeFalsy();
|
||||
|
@ -4,9 +4,9 @@ import { genCSSMotion } from 'rc-motion/lib/CSSMotion';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import { resetWarned } from 'rc-util/lib/warning';
|
||||
import * as React from 'react';
|
||||
import TestUtils, { act } from 'react-dom/test-utils';
|
||||
import TestUtils from 'react-dom/test-utils';
|
||||
import Modal from '..';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import { sleep, act } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import destroyFns from '../destroyFns';
|
||||
|
||||
@ -78,7 +78,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
content: 'some descriptions',
|
||||
...args,
|
||||
});
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
jest.useRealTimers();
|
||||
}
|
||||
|
||||
@ -87,7 +89,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
confirm({
|
||||
content: 'some descriptions',
|
||||
});
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(document.querySelector('.ant-modal-confirm-title')).toBe(null);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
@ -150,18 +154,26 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
onCancel,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await sleep();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(1);
|
||||
TestUtils.Simulate.keyDown($$('.ant-modal')[0], {
|
||||
keyCode: KeyCode.ESC,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await sleep(0);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(0);
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
@ -197,18 +209,26 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
open();
|
||||
|
||||
jest.useFakeTimers();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await sleep();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect($$('.ant-modal-confirm')).toHaveLength(1);
|
||||
|
||||
await sleep();
|
||||
$$('.ant-btn')[0].click();
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await sleep();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect($$('.ant-modal-confirm')).toHaveLength(0);
|
||||
jest.useRealTimers();
|
||||
@ -358,7 +378,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
expect($$('.ant-modal-confirm-title')[0].innerHTML).toBe('new title');
|
||||
expect($$('.ant-modal-confirm-content')[0].innerHTML).toBe('new content');
|
||||
instance.destroy();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
@ -403,7 +425,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
);
|
||||
expect($$('.ant-modal-confirm-btns .ant-btn-primary')[0].style.color).toBe('red');
|
||||
instance.destroy();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
@ -487,7 +511,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
Modal.destroyAll(); // clear destroyFns
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
const instances = [];
|
||||
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||
@ -716,18 +742,26 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
onCancel: close => mock(close),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await sleep();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
|
||||
TestUtils.Simulate.keyDown($$('.ant-modal')[0], {
|
||||
keyCode: KeyCode.ESC,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await sleep(0);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0);
|
||||
expect(mock).toBeCalledWith(expect.any(Function));
|
||||
|
@ -4,7 +4,7 @@ import React from 'react';
|
||||
import Popconfirm from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, act } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
|
||||
describe('Popconfirm', () => {
|
||||
@ -102,7 +102,9 @@ describe('Popconfirm', () => {
|
||||
expect(ref.current.getPopupDomNode().className).not.toContain('ant-popover-hidden');
|
||||
popconfirm.setProps({ visible: false });
|
||||
popconfirm.update(); // https://github.com/enzymejs/enzyme/issues/2305
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(popconfirm.find('Trigger').props().popupVisible).toBe(false);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Select from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import { fireEvent, render, act } from '../../../tests/utils';
|
||||
import Icon from '../../icon';
|
||||
|
||||
const { Option } = Select;
|
||||
@ -15,8 +14,8 @@ describe('Select', () => {
|
||||
rtlTest(Select);
|
||||
|
||||
function toggleOpen(container) {
|
||||
act(() => {
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
}
|
||||
@ -123,7 +122,9 @@ describe('Select', () => {
|
||||
<Option value="1">1</Option>
|
||||
</Select>,
|
||||
);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -1,8 +1,7 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Table from '..';
|
||||
import { fireEvent, render, waitFor } from '../../../tests/utils';
|
||||
import { fireEvent, render, waitFor, act } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import Input from '../../input';
|
||||
|
@ -2,9 +2,8 @@
|
||||
jest.mock('../../_util/scrollTo');
|
||||
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Table from '..';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import { fireEvent, render, act } from '../../../tests/utils';
|
||||
import scrollTo from '../../_util/scrollTo';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Table from '..';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import { fireEvent, render, act } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
|
||||
|
@ -3,6 +3,7 @@ import React from 'react';
|
||||
import Tag from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { act } from '../../../tests/utils';
|
||||
|
||||
describe('Tag', () => {
|
||||
mountTest(Tag);
|
||||
@ -25,7 +26,9 @@ describe('Tag', () => {
|
||||
expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
|
||||
wrapper.find('.anticon-close').simulate('click');
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(0);
|
||||
});
|
||||
@ -38,7 +41,9 @@ describe('Tag', () => {
|
||||
expect(wrapper.find('.anticon-close').length).toBe(1);
|
||||
expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
|
||||
wrapper.find('.anticon-close').simulate('click');
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
|
||||
});
|
||||
|
||||
@ -81,10 +86,14 @@ describe('Tag', () => {
|
||||
const wrapper = mount(<Tag visible />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
wrapper.setProps({ visible: false });
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
wrapper.setProps({ visible: true });
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -92,10 +101,14 @@ describe('Tag', () => {
|
||||
const wrapper = mount(<Tag visible={false} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
wrapper.setProps({ visible: true });
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
wrapper.setProps({ visible: false });
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { LikeOutlined, SmileOutlined } from '@ant-design/icons';
|
||||
import { act } from '@testing-library/react';
|
||||
import * as copyObj from 'copy-to-clipboard';
|
||||
import React from 'react';
|
||||
import { fireEvent, render, waitFor } from '../../../tests/utils';
|
||||
import { fireEvent, render, waitFor, act } from '../../../tests/utils';
|
||||
|
||||
import Base from '../Base';
|
||||
|
||||
@ -90,7 +89,9 @@ describe('Typography copy', () => {
|
||||
|
||||
jest.useFakeTimers();
|
||||
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
unmount();
|
||||
jest.useRealTimers();
|
||||
|
@ -5,7 +5,7 @@ import { resetWarned } from 'rc-util/lib/warning';
|
||||
import React from 'react';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render, sleep, waitFor } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, waitFor, act } from '../../../tests/utils';
|
||||
import Base from '../Base';
|
||||
import Link from '../Link';
|
||||
import Paragraph from '../Paragraph';
|
||||
@ -104,7 +104,9 @@ describe('Typography', () => {
|
||||
}
|
||||
|
||||
fireEvent.mouseEnter(wrapper.querySelector('.ant-typography-copy'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
if (tooltips === undefined || tooltips === true) {
|
||||
await waitFor(() => {
|
||||
@ -271,7 +273,9 @@ describe('Typography', () => {
|
||||
expect(onStart).not.toHaveBeenCalled();
|
||||
}
|
||||
fireEvent.mouseEnter(wrapper.querySelectorAll('.ant-typography-edit')[0]);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
if (tooltip === undefined || tooltip === true) {
|
||||
await waitFor(() => {
|
||||
|
@ -1,9 +1,8 @@
|
||||
/* eslint-disable react/no-string-refs, react/prefer-es6-class */
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Upload from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import { fireEvent, render, waitFor } from '../../../tests/utils';
|
||||
import { fireEvent, render, waitFor, act } from '../../../tests/utils';
|
||||
import { setup, teardown } from './mock';
|
||||
|
||||
describe('Upload.Dragger', () => {
|
||||
@ -26,7 +25,7 @@ describe('Upload.Dragger', () => {
|
||||
},
|
||||
});
|
||||
|
||||
await act(() => {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
|
@ -2,11 +2,10 @@
|
||||
import produce from 'immer';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Upload from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, act } from '../../../tests/utils';
|
||||
import Form from '../../form';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
import { getFileItem, isImageUrl, removeFileItem } from '../utils';
|
||||
@ -322,7 +321,9 @@ describe('Upload', () => {
|
||||
const { rerender } = render(<Upload ref={ref} />);
|
||||
expect(ref.current.fileList).toEqual([]);
|
||||
rerender(<Upload ref={ref} fileList={fileList} />);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(ref.current.fileList).toEqual(fileList);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
@ -725,7 +726,7 @@ describe('Upload', () => {
|
||||
await Promise.resolve();
|
||||
}
|
||||
});
|
||||
await act(() => {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await act(async () => {
|
||||
@ -945,7 +946,7 @@ describe('Upload', () => {
|
||||
});
|
||||
|
||||
// Motion leave status change: start > active
|
||||
await act(() => {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Upload from '..';
|
||||
import { fireEvent, render, sleep, waitFor } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, waitFor, act } from '../../../tests/utils';
|
||||
import Form from '../../form';
|
||||
import UploadList from '../UploadList';
|
||||
import { previewImage } from '../utils';
|
||||
@ -263,7 +262,7 @@ describe('Upload List', () => {
|
||||
// Error message
|
||||
fireEvent.mouseEnter(wrapper.querySelector('.ant-upload-list-item'));
|
||||
|
||||
await act(() => {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
@ -574,7 +573,9 @@ describe('Upload List', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
unmount();
|
||||
|
||||
@ -1132,13 +1133,15 @@ describe('Upload List', () => {
|
||||
await waitPromise();
|
||||
|
||||
// Wait for mock request finish request
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
// Basic called times
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
|
||||
// Check for images
|
||||
await act(() => {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
const afterImgNode = wrapper.container.querySelectorAll(
|
||||
@ -1313,7 +1316,7 @@ describe('Upload List', () => {
|
||||
|
||||
expect(uploadRef.current.fileList).toHaveLength(fileNames.length);
|
||||
|
||||
await act(() => {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(uploadRef.current.fileList).toHaveLength(fileNames.length);
|
||||
|
9
site/theme/en-US.d.ts
vendored
Normal file
9
site/theme/en-US.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
interface ENLocale {
|
||||
locale: 'en-US';
|
||||
messages: {
|
||||
[key: PropertyKey]: string;
|
||||
};
|
||||
}
|
||||
|
||||
const enLocale: ENLocale;
|
||||
export default enLocale;
|
@ -1,7 +1,6 @@
|
||||
/* eslint-disable camelcase */
|
||||
import React from 'react';
|
||||
import AntdIcon, { createFromIconfontCN } from '@ant-design/icons';
|
||||
|
||||
import { withThemeSuffix, removeTypeTheme, getThemeFromTypeName } from './utils';
|
||||
import warning from '../../../../components/_util/warning';
|
||||
|
||||
@ -9,7 +8,16 @@ const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/font_1329669_t1u72b9zk8s.js',
|
||||
});
|
||||
|
||||
const OldIcon = props => {
|
||||
interface IconProps {
|
||||
type: string;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface CreateIconfont {
|
||||
createFromIconfontCN: typeof createFromIconfontCN;
|
||||
}
|
||||
|
||||
const OldIcon: React.FC<IconProps> = props => {
|
||||
const { type, theme } = props;
|
||||
let computedType = type;
|
||||
if (theme) {
|
||||
@ -25,12 +33,8 @@ const OldIcon = props => {
|
||||
return <IconFont {...props} type={`icon-${computedType}`} />;
|
||||
};
|
||||
|
||||
const Icon = props => {
|
||||
if (typeof props.type === 'string') {
|
||||
return <OldIcon {...props} />;
|
||||
}
|
||||
return <AntdIcon {...props} />;
|
||||
};
|
||||
const Icon: React.FC<IconProps> & CreateIconfont = props =>
|
||||
typeof props.type === 'string' ? <OldIcon {...props} /> : <AntdIcon {...props} />;
|
||||
|
||||
Icon.createFromIconfontCN = createFromIconfontCN;
|
||||
|
@ -14,8 +14,8 @@ const fillTester = /-fill$/;
|
||||
const outlineTester = /-o$/;
|
||||
const twoToneTester = /-twotone$/;
|
||||
|
||||
export function getThemeFromTypeName(type) {
|
||||
let result = null;
|
||||
export function getThemeFromTypeName(type: string) {
|
||||
let result: string | null = null;
|
||||
if (fillTester.test(type)) {
|
||||
result = 'filled';
|
||||
} else if (outlineTester.test(type)) {
|
||||
@ -26,12 +26,12 @@ export function getThemeFromTypeName(type) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function removeTypeTheme(type) {
|
||||
export function removeTypeTheme(type: string): string {
|
||||
return type.replace(fillTester, '').replace(outlineTester, '').replace(twoToneTester, '');
|
||||
}
|
||||
|
||||
export function withThemeSuffix(type, theme) {
|
||||
let result = type;
|
||||
export function withThemeSuffix(type: string, theme: string): string {
|
||||
let result: string = type;
|
||||
if (theme === 'filled') {
|
||||
result += '-fill';
|
||||
} else if (theme === 'outlined') {
|
@ -31,7 +31,7 @@ export interface HeaderProps {
|
||||
intl: { locale: string };
|
||||
location: { pathname: string; query: any };
|
||||
router: any;
|
||||
themeConfig: { docVersions: Record<string, string> };
|
||||
themeConfig?: { docVersions: Record<string, string> };
|
||||
changeDirection: (direction: DirectionType) => void;
|
||||
}
|
||||
|
||||
@ -202,7 +202,7 @@ const Header: React.FC<HeaderProps & WrappedComponentProps<'intl'>> = props => {
|
||||
const { menuVisible, windowWidth, searching, showTechUIButton } = headerState;
|
||||
const docVersions: Record<string, string> = {
|
||||
[antdVersion]: antdVersion,
|
||||
...themeConfig.docVersions,
|
||||
...themeConfig?.docVersions,
|
||||
};
|
||||
const versionOptions = Object.keys(docVersions).map(version => (
|
||||
<Option value={docVersions[version]} key={version}>
|
||||
|
@ -4,6 +4,9 @@ import type { DirectionType } from 'antd/es/config-provider';
|
||||
export interface SiteContextProps {
|
||||
isMobile: boolean;
|
||||
direction: DirectionType;
|
||||
theme?: string;
|
||||
setTheme?: (theme: string, persist?: boolean) => void;
|
||||
setIframeTheme?: (iframeNode: HTMLIFrameElement, theme: string) => void;
|
||||
}
|
||||
|
||||
const SiteContext = React.createContext<SiteContextProps>({
|
||||
|
@ -1,24 +1,28 @@
|
||||
import { presetDarkPalettes, presetPalettes } from '@ant-design/colors';
|
||||
import { createCache, StyleProvider } from '@ant-design/cssinjs';
|
||||
import { setTwoToneColor } from '@ant-design/icons';
|
||||
import { ConfigProvider, theme as antdTheme } from 'antd';
|
||||
import zhCN from 'antd/lib/locale/zh_CN';
|
||||
import { browserHistory } from 'bisheng/router';
|
||||
import classNames from 'classnames';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
/* eslint-disable class-methods-use-this */
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import themeSwitcher from 'theme-switcher';
|
||||
import type { TwoToneColor } from '@ant-design/icons';
|
||||
import { setTwoToneColor } from '@ant-design/icons';
|
||||
import { ConfigProvider, theme as antdTheme } from 'antd';
|
||||
import { browserHistory } from 'bisheng/router';
|
||||
import { createCache, StyleProvider } from '@ant-design/cssinjs';
|
||||
import type { SeedToken } from 'antd/es/theme';
|
||||
import classNames from 'classnames';
|
||||
import { presetDarkPalettes, presetPalettes } from '@ant-design/colors';
|
||||
import zhCN from 'antd/lib/locale/zh_CN';
|
||||
import type { DirectionType } from 'antd/es/config-provider';
|
||||
import enLocale from '../../en-US';
|
||||
import cnLocale from '../../zh-CN';
|
||||
import * as utils from '../utils';
|
||||
import Header from './Header';
|
||||
import type { SiteContextProps } from './SiteContext';
|
||||
import SiteContext from './SiteContext';
|
||||
|
||||
import defaultSeedToken from '../../../../components/theme/themes/seed';
|
||||
import DynamicTheme from './DynamicTheme';
|
||||
import 'moment/locale/zh-cn';
|
||||
|
||||
if (typeof window !== 'undefined' && navigator.serviceWorker) {
|
||||
navigator.serviceWorker.getRegistrations().then(registrations => {
|
||||
@ -36,12 +40,12 @@ if (typeof window !== 'undefined') {
|
||||
require('../../static/style');
|
||||
|
||||
// Expose to iframe
|
||||
window.react = React;
|
||||
window['react-dom'] = ReactDOM;
|
||||
(window as any).react = React;
|
||||
(window as any)['react-dom'] = ReactDOM;
|
||||
// eslint-disable-next-line global-require
|
||||
window.antd = require('antd');
|
||||
(window as any).antd = require('antd');
|
||||
// eslint-disable-next-line global-require
|
||||
window['@ant-design/icons'] = require('@ant-design/icons');
|
||||
(window as any)['@ant-design/icons'] = require('@ant-design/icons');
|
||||
|
||||
// Error log statistic
|
||||
window.addEventListener('error', e => {
|
||||
@ -56,7 +60,7 @@ if (typeof window !== 'undefined') {
|
||||
const RESPONSIVE_MOBILE = 768;
|
||||
|
||||
// for dark.css timestamp to remove cache
|
||||
const timestamp = new Date().getTime();
|
||||
const timestamp = Date.now();
|
||||
const themeMap = {
|
||||
dark: `/dark.css?${timestamp}`,
|
||||
compact: `/compact.css?${timestamp}`,
|
||||
@ -69,17 +73,38 @@ const { switcher } = themeSwitcher(themeConfig);
|
||||
// Pass to global since bisheng do not have the process for wrapper
|
||||
const styleCache = createCache();
|
||||
if (typeof global !== 'undefined') {
|
||||
global.styleCache = styleCache;
|
||||
(global as any).styleCache = styleCache;
|
||||
}
|
||||
|
||||
export default class Layout extends React.Component {
|
||||
interface LayoutPropsType {
|
||||
location: any;
|
||||
router: any;
|
||||
helmetContext: any;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
interface LayoutStateType {
|
||||
appLocale: typeof cnLocale | typeof enLocale;
|
||||
theme: string;
|
||||
isMobile: boolean;
|
||||
direction: DirectionType;
|
||||
setTheme: SiteContextProps['setTheme'];
|
||||
setIframeTheme: SiteContextProps['setIframeTheme'];
|
||||
v5theme: string;
|
||||
designToken: SeedToken;
|
||||
hashedStyle: boolean;
|
||||
}
|
||||
|
||||
export default class Layout extends React.Component<LayoutPropsType, LayoutStateType> {
|
||||
static contextType = SiteContext;
|
||||
|
||||
timer: NodeJS.Timeout | null = null;
|
||||
|
||||
isBeforeComponent = false;
|
||||
|
||||
syncIframeThemeId = null;
|
||||
syncIframeThemeId?: number;
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: LayoutPropsType) {
|
||||
super(props);
|
||||
const { pathname } = props.location;
|
||||
const appLocale = utils.isZhCN(pathname) ? cnLocale : enLocale;
|
||||
@ -87,8 +112,9 @@ export default class Layout extends React.Component {
|
||||
this.state = {
|
||||
appLocale,
|
||||
theme: 'default',
|
||||
setTheme: this.setTheme,
|
||||
direction: 'ltr',
|
||||
isMobile: false,
|
||||
setTheme: this.setTheme,
|
||||
setIframeTheme: this.setIframeTheme,
|
||||
v5theme: 'default',
|
||||
designToken: defaultSeedToken,
|
||||
@ -98,50 +124,40 @@ export default class Layout extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
const { location, router } = this.props;
|
||||
router.listen(({ pathname, search }) => {
|
||||
router.listen(({ pathname, search }: any) => {
|
||||
const { theme } = this.props.location.query;
|
||||
if (typeof window.ga !== 'undefined') {
|
||||
window.ga('send', 'pageview', pathname + search);
|
||||
if (typeof (window as any).ga !== 'undefined') {
|
||||
(window as any).ga('send', 'pageview', pathname + search);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
if (typeof window._hmt !== 'undefined') {
|
||||
// eslint-disable-next-line
|
||||
window._hmt.push(['_trackPageview', pathname + search]);
|
||||
if (typeof (window as any)._hmt !== 'undefined') {
|
||||
(window as any)._hmt.push(['_trackPageview', pathname + search]);
|
||||
}
|
||||
const componentPage = /^\/?components/.test(pathname);
|
||||
|
||||
// only component page can use `dark` theme
|
||||
if (!componentPage) {
|
||||
this.isBeforeComponent = false;
|
||||
this.setTheme('default', false);
|
||||
this.setTheme?.('default', false);
|
||||
} else if (theme && !this.isBeforeComponent) {
|
||||
this.isBeforeComponent = true;
|
||||
this.setTheme(theme, false);
|
||||
this.setTheme?.(theme, false);
|
||||
}
|
||||
});
|
||||
|
||||
if (location.query.theme && /^\/?components/.test(location.pathname)) {
|
||||
this.isBeforeComponent = true;
|
||||
this.setTheme(location.query.theme, false);
|
||||
this.setTheme?.(location.query.theme, false);
|
||||
} else {
|
||||
this.isBeforeComponent = false;
|
||||
this.setTheme('default', false);
|
||||
this.setTheme?.('default', false);
|
||||
}
|
||||
|
||||
if (location.query.direction) {
|
||||
this.setState({
|
||||
direction: location.query.direction,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
direction: 'ltr',
|
||||
});
|
||||
}
|
||||
this.setState({ direction: location.query.direction || 'ltr' });
|
||||
|
||||
const nprogressHiddenStyle = document.getElementById('nprogress-style');
|
||||
if (nprogressHiddenStyle) {
|
||||
this.timer = setTimeout(() => {
|
||||
nprogressHiddenStyle.parentNode.removeChild(nprogressHiddenStyle);
|
||||
nprogressHiddenStyle.parentNode?.removeChild(nprogressHiddenStyle);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@ -149,7 +165,7 @@ export default class Layout extends React.Component {
|
||||
window.addEventListener('resize', this.updateMobileMode);
|
||||
|
||||
// Sync iframe theme with current theme
|
||||
this.syncIframeThemeId = setInterval(() => {
|
||||
this.syncIframeThemeId = window.setInterval(() => {
|
||||
const { designToken, hashedStyle } = this.state;
|
||||
const content = JSON.stringify({
|
||||
action: 'sync.theme',
|
||||
@ -157,16 +173,19 @@ export default class Layout extends React.Component {
|
||||
hashed: hashedStyle,
|
||||
});
|
||||
|
||||
document.querySelectorAll('iframe.iframe-demo').forEach(iframe => {
|
||||
iframe.contentWindow.postMessage(content);
|
||||
document.querySelectorAll<HTMLIFrameElement>('iframe.iframe-demo').forEach(iframe => {
|
||||
iframe.contentWindow?.postMessage(content);
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.timer);
|
||||
clearInterval(this.syncIframeThemeId);
|
||||
clearTimeout(this.timer as unknown as number);
|
||||
clearInterval(this.syncIframeThemeId as unknown as number);
|
||||
window.removeEventListener('resize', this.updateMobileMode);
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
}
|
||||
|
||||
updateMobileMode = () => {
|
||||
@ -179,19 +198,16 @@ export default class Layout extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
setIframeTheme = (iframeNode, theme) => {
|
||||
iframeNode.contentWindow.postMessage(
|
||||
setIframeTheme: LayoutStateType['setIframeTheme'] = (iframeNode, theme) => {
|
||||
iframeNode.contentWindow?.postMessage(
|
||||
JSON.stringify({
|
||||
action: 'change.theme',
|
||||
data: {
|
||||
themeConfig,
|
||||
theme,
|
||||
},
|
||||
data: { themeConfig, theme },
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
setTheme = (theme, persist = true) => {
|
||||
setTheme: LayoutStateType['setTheme'] = (theme, persist = true) => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
@ -203,24 +219,23 @@ export default class Layout extends React.Component {
|
||||
|
||||
const iframeNodes = document.querySelectorAll('.iframe-demo');
|
||||
// loop element node
|
||||
[].forEach.call(iframeNodes, iframeNode => {
|
||||
this.setIframeTheme(iframeNode, theme);
|
||||
[].forEach.call(iframeNodes, (iframeNode: HTMLIFrameElement) => {
|
||||
this.setIframeTheme?.(iframeNode, theme);
|
||||
});
|
||||
|
||||
this.setState({
|
||||
theme,
|
||||
});
|
||||
this.setState({ theme });
|
||||
const iconTwoToneThemeMap = {
|
||||
dark: [presetDarkPalettes.blue.primary, '#111d2c'],
|
||||
default: presetPalettes.blue.primary,
|
||||
};
|
||||
setTwoToneColor(iconTwoToneThemeMap[theme] || iconTwoToneThemeMap.default);
|
||||
} as const;
|
||||
setTwoToneColor(
|
||||
(iconTwoToneThemeMap[theme as keyof typeof iconTwoToneThemeMap] ||
|
||||
iconTwoToneThemeMap.default) as TwoToneColor,
|
||||
);
|
||||
};
|
||||
|
||||
changeDirection = direction => {
|
||||
this.setState({
|
||||
direction,
|
||||
});
|
||||
changeDirection = (direction: DirectionType): void => {
|
||||
this.setState({ direction });
|
||||
const { pathname, hash, query } = this.props.location;
|
||||
if (direction === 'ltr') {
|
||||
delete query.direction;
|
||||
@ -257,15 +272,14 @@ export default class Layout extends React.Component {
|
||||
: 'An enterprise-class UI design language and React UI library with a set of high-quality React components, one of best React UI library for enterprises';
|
||||
return (
|
||||
<StyleProvider cache={styleCache}>
|
||||
{/* eslint-disable-next-line react/jsx-no-constructed-context-values */}
|
||||
<SiteContext.Provider value={{ isMobile, direction, theme, setTheme, setIframeTheme }}>
|
||||
<HelmetProvider context={helmetContext}>
|
||||
<Helmet encodeSpecialCharacters={false}>
|
||||
<html
|
||||
lang={appLocale.locale === 'zh-CN' ? 'zh' : 'en'}
|
||||
data-direction={direction}
|
||||
className={classNames({
|
||||
[`rtl`]: direction === 'rtl',
|
||||
})}
|
||||
className={classNames({ [`rtl`]: direction === 'rtl' })}
|
||||
/>
|
||||
<title>{title}</title>
|
||||
<link
|
||||
@ -286,7 +300,7 @@ export default class Layout extends React.Component {
|
||||
defaultLocale="en-US"
|
||||
>
|
||||
<ConfigProvider
|
||||
locale={appLocale.locale === 'zh-CN' ? zhCN : null}
|
||||
locale={appLocale.locale === 'zh-CN' ? zhCN : undefined}
|
||||
direction={direction}
|
||||
theme={{
|
||||
token: designToken,
|
||||
@ -299,17 +313,19 @@ export default class Layout extends React.Component {
|
||||
{children}
|
||||
|
||||
<DynamicTheme
|
||||
componentName={this.props.params?.children?.replace('-cn', '')}
|
||||
defaultToken={{
|
||||
componentName={(this.props as any).params?.children?.replace('-cn', '')}
|
||||
defaultToken={
|
||||
{
|
||||
theme: v5theme,
|
||||
...designToken,
|
||||
hashed: hashedStyle,
|
||||
}}
|
||||
} as any
|
||||
}
|
||||
onChangeTheme={newToken => {
|
||||
console.log('Change Theme:', newToken);
|
||||
const { hashed, theme, ...restToken } = newToken;
|
||||
const { hashed, newTheme, ...restToken } = newToken as any;
|
||||
this.setState({
|
||||
v5theme: theme,
|
||||
v5theme: newTheme,
|
||||
designToken: restToken,
|
||||
hashedStyle: hashed,
|
||||
});
|
9
site/theme/zh-CN.d.ts
vendored
Normal file
9
site/theme/zh-CN.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
interface CNLocale {
|
||||
locale: 'zh-CN';
|
||||
messages: {
|
||||
[key: PropertyKey]: string;
|
||||
};
|
||||
}
|
||||
|
||||
const cnLocale: CNLocale;
|
||||
export default cnLocale;
|
@ -1,9 +1,8 @@
|
||||
import type { RenderOptions } from '@testing-library/react';
|
||||
import { render } from '@testing-library/react';
|
||||
import MockDate from 'mockdate';
|
||||
import type { ReactElement } from 'react';
|
||||
import React, { StrictMode } from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import type { RenderOptions } from '@testing-library/react';
|
||||
import { render, act } from '@testing-library/react';
|
||||
import { _rs as onLibResize } from 'rc-resize-observer/lib/utils/observerUtil';
|
||||
import { _rs as onEsResize } from 'rc-resize-observer/es/utils/observerUtil';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user