Merge branch 'master' into next-merge-master

This commit is contained in:
MadCcc 2022-08-08 15:12:31 +08:00
commit dbff63666a
30 changed files with 287 additions and 170 deletions

View File

@ -148,6 +148,7 @@ module.exports = {
'jest/no-done-callback': 0, 'jest/no-done-callback': 0,
'jest/valid-title': 0, 'jest/valid-title': 0,
'jest/no-conditional-expect': 0, 'jest/no-conditional-expect': 0,
'jest/no-standalone-expect': 0,
'unicorn/better-regex': 2, 'unicorn/better-regex': 2,
'unicorn/prefer-string-trim-start-end': 2, 'unicorn/prefer-string-trim-start-end': 2,

View File

@ -1,9 +1,8 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import Alert from '..'; import Alert from '..';
import accessibilityTest from '../../../tests/shared/accessibilityTest'; import accessibilityTest from '../../../tests/shared/accessibilityTest';
import rtlTest from '../../../tests/shared/rtlTest'; 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 Button from '../../button';
import Popconfirm from '../../popconfirm'; import Popconfirm from '../../popconfirm';
import Tooltip from '../../tooltip'; import Tooltip from '../../tooltip';
@ -33,13 +32,13 @@ describe('Alert', () => {
/>, />,
); );
jest.useFakeTimers();
fireEvent.click(container.querySelector('.ant-alert-close-icon')!);
act(() => { act(() => {
jest.useFakeTimers();
fireEvent.click(container.querySelector('.ant-alert-close-icon')!);
jest.runAllTimers(); jest.runAllTimers();
jest.useRealTimers();
}); });
expect(onClose).toHaveBeenCalled(); expect(onClose).toHaveBeenCalled();
jest.useRealTimers();
}); });
describe('action of Alert', () => { describe('action of Alert', () => {

View File

@ -221,4 +221,11 @@ describe('Avatar Render', () => {
expect(wrapper.querySelector('[crossorigin]')).toBeNull(); expect(wrapper.querySelector('[crossorigin]')).toBeNull();
expect(getByRole('img').getAttribute('crossOrigin')).toEqual(null); 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();
});
}); });

View File

@ -33,6 +33,7 @@ export interface AvatarProps {
children?: React.ReactNode; children?: React.ReactNode;
alt?: string; alt?: string;
crossOrigin?: '' | 'anonymous' | 'use-credentials'; crossOrigin?: '' | 'anonymous' | 'use-credentials';
onClick?: (e?: React.MouseEvent<HTMLElement>) => void;
/* callback when img load error */ /* callback when img load error */
/* return false to prevent Avatar show default fallback behavior, then you can do fallback by your self */ /* return false to prevent Avatar show default fallback behavior, then you can do fallback by your self */
onError?: () => boolean; onError?: () => boolean;

View File

@ -9,6 +9,7 @@ import zhCN from '../../locale/zh_CN';
import Modal from '../../modal'; import Modal from '../../modal';
import Pagination from '../../pagination'; import Pagination from '../../pagination';
import TimePicker from '../../time-picker'; import TimePicker from '../../time-picker';
import { act } from '../../../tests/utils';
describe('ConfigProvider.Locale', () => { describe('ConfigProvider.Locale', () => {
function $$(className) { function $$(className) {
@ -44,7 +45,9 @@ describe('ConfigProvider.Locale', () => {
title: 'title', title: 'title',
content: 'Some descriptions', content: 'Some descriptions',
}); });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
jest.useRealTimers(); jest.useRealTimers();
}; };

View File

@ -3,6 +3,7 @@ import React from 'react';
import ConfigProvider from '..'; import ConfigProvider from '..';
import Affix from '../../affix'; import Affix from '../../affix';
import Anchor from '../../anchor'; import Anchor from '../../anchor';
import { act } from '../../../tests/utils';
describe('ConfigProvider.getTargetContainer', () => { describe('ConfigProvider.getTargetContainer', () => {
it('Affix', () => { it('Affix', () => {
@ -16,7 +17,9 @@ describe('ConfigProvider.getTargetContainer', () => {
</ConfigProvider>, </ConfigProvider>,
); );
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(getTargetContainer).toHaveBeenCalled(); expect(getTargetContainer).toHaveBeenCalled();
jest.useRealTimers(); jest.useRealTimers();
@ -33,7 +36,9 @@ describe('ConfigProvider.getTargetContainer', () => {
</ConfigProvider>, </ConfigProvider>,
); );
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(getTargetContainer).toHaveBeenCalled(); expect(getTargetContainer).toHaveBeenCalled();
jest.useRealTimers(); jest.useRealTimers();

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import Form from '..'; import Form from '..';
import { fireEvent, render, sleep } from '../../../tests/utils'; import { fireEvent, render, sleep, act } from '../../../tests/utils';
import Button from '../../button'; import Button from '../../button';
import Input from '../../input'; import Input from '../../input';
@ -43,8 +42,8 @@ describe('Form.List', () => {
); );
function operate(className) { function operate(className) {
fireEvent.click(container.querySelector(className));
act(() => { act(() => {
fireEvent.click(container.querySelector(className));
jest.runAllTimers(); jest.runAllTimers();
}); });
} }

View File

@ -81,6 +81,7 @@ import {
Transfer, Transfer,
} from '../..'; } from '../..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import { act } from '../../../tests/utils';
import arEG from '../ar_EG'; import arEG from '../ar_EG';
import azAZ from '../az_AZ'; import azAZ from '../az_AZ';
import bgBG from '../bg_BG'; import bgBG from '../bg_BG';
@ -296,7 +297,9 @@ describe('Locale Provider', () => {
Modal.confirm({ Modal.confirm({
title: 'Hello World!', title: 'Hello World!',
}); });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
jest.useRealTimers(); jest.useRealTimers();
} }

View File

@ -4,6 +4,7 @@ import Mentions from '..';
import focusTest from '../../../tests/shared/focusTest'; import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
import { act } from '../../../tests/utils';
const { getMentions } = Mentions; const { getMentions } = Mentions;
@ -68,7 +69,9 @@ describe('Mentions', () => {
expect(onFocus).toHaveBeenCalled(); expect(onFocus).toHaveBeenCalled();
wrapper.find('textarea').simulate('blur'); wrapper.find('textarea').simulate('blur');
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
wrapper.update(); wrapper.update();
expect(wrapper.find('.ant-mentions').hasClass('ant-mentions-focused')).toBeFalsy(); expect(wrapper.find('.ant-mentions').hasClass('ant-mentions-focused')).toBeFalsy();
expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalled();

View File

@ -7,11 +7,10 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { act } from 'react-dom/test-utils';
import Menu from '..'; import Menu from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render } from '../../../tests/utils'; import { fireEvent, render, act } from '../../../tests/utils';
import Layout from '../../layout'; import Layout from '../../layout';
import Tooltip from '../../tooltip'; import Tooltip from '../../tooltip';
import initCollapseMotion from '../../_util/motion'; import initCollapseMotion from '../../_util/motion';
@ -378,8 +377,8 @@ describe('Menu', () => {
wrapper.setProps({ inlineCollapsed: true }); wrapper.setProps({ inlineCollapsed: true });
act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
wrapper.update();
}); });
wrapper.update();
expect(wrapper.find('ul.ant-menu-root').hasClass('ant-menu-vertical')).toBeTruthy(); expect(wrapper.find('ul.ant-menu-root').hasClass('ant-menu-vertical')).toBeTruthy();
expect(wrapper.find('PopupTrigger').prop('visible')).toBeFalsy(); expect(wrapper.find('PopupTrigger').prop('visible')).toBeFalsy();
@ -388,8 +387,8 @@ describe('Menu', () => {
wrapper.setProps({ inlineCollapsed: false }); wrapper.setProps({ inlineCollapsed: false });
act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
wrapper.update();
}); });
wrapper.update();
expect(wrapper.find('ul.ant-menu-sub').last().hasClass('ant-menu-inline')).toBeTruthy(); expect(wrapper.find('ul.ant-menu-sub').last().hasClass('ant-menu-inline')).toBeTruthy();
expect(wrapper.find('InlineSubMenuList').prop('open')).toBeTruthy(); expect(wrapper.find('InlineSubMenuList').prop('open')).toBeTruthy();
@ -708,8 +707,8 @@ describe('Menu', () => {
act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
wrapper.update();
}); });
wrapper.update();
expect(wrapper.find('.ant-tooltip-inner').length).toBe(0); expect(wrapper.find('.ant-tooltip-inner').length).toBe(0);
}); });
@ -752,7 +751,9 @@ describe('Menu', () => {
); );
wrapper.find('.ant-menu-item').hostNodes().simulate('mouseenter'); wrapper.find('.ant-menu-item').hostNodes().simulate('mouseenter');
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
wrapper.update(); wrapper.update();
expect(wrapper.find('.ant-tooltip-inner').length).toBeFalsy(); expect(wrapper.find('.ant-tooltip-inner').length).toBeFalsy();

View File

@ -4,9 +4,9 @@ import { genCSSMotion } from 'rc-motion/lib/CSSMotion';
import KeyCode from 'rc-util/lib/KeyCode'; import KeyCode from 'rc-util/lib/KeyCode';
import { resetWarned } from 'rc-util/lib/warning'; import { resetWarned } from 'rc-util/lib/warning';
import * as React from 'react'; import * as React from 'react';
import TestUtils, { act } from 'react-dom/test-utils'; import TestUtils from 'react-dom/test-utils';
import Modal from '..'; import Modal from '..';
import { sleep } from '../../../tests/utils'; import { sleep, act } from '../../../tests/utils';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';
import destroyFns from '../destroyFns'; import destroyFns from '../destroyFns';
@ -78,7 +78,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
content: 'some descriptions', content: 'some descriptions',
...args, ...args,
}); });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
jest.useRealTimers(); jest.useRealTimers();
} }
@ -87,7 +89,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
confirm({ confirm({
content: 'some descriptions', content: 'some descriptions',
}); });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(document.querySelector('.ant-modal-confirm-title')).toBe(null); expect(document.querySelector('.ant-modal-confirm-title')).toBe(null);
jest.useRealTimers(); jest.useRealTimers();
}); });
@ -150,18 +154,26 @@ describe('Modal.confirm triggers callbacks correctly', () => {
onCancel, onCancel,
}); });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
await sleep(); await sleep();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(1); expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(1);
TestUtils.Simulate.keyDown($$('.ant-modal')[0], { TestUtils.Simulate.keyDown($$('.ant-modal')[0], {
keyCode: KeyCode.ESC, keyCode: KeyCode.ESC,
}); });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
await sleep(0); await sleep(0);
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(0); expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(0);
expect(onCancel).toHaveBeenCalledTimes(1); expect(onCancel).toHaveBeenCalledTimes(1);
@ -197,18 +209,26 @@ describe('Modal.confirm triggers callbacks correctly', () => {
open(); open();
jest.useFakeTimers(); jest.useFakeTimers();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
await sleep(); await sleep();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect($$('.ant-modal-confirm')).toHaveLength(1); expect($$('.ant-modal-confirm')).toHaveLength(1);
await sleep(); await sleep();
$$('.ant-btn')[0].click(); $$('.ant-btn')[0].click();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
await sleep(); await sleep();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect($$('.ant-modal-confirm')).toHaveLength(0); expect($$('.ant-modal-confirm')).toHaveLength(0);
jest.useRealTimers(); 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-title')[0].innerHTML).toBe('new title');
expect($$('.ant-modal-confirm-content')[0].innerHTML).toBe('new content'); expect($$('.ant-modal-confirm-content')[0].innerHTML).toBe('new content');
instance.destroy(); instance.destroy();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
jest.useRealTimers(); 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'); expect($$('.ant-modal-confirm-btns .ant-btn-primary')[0].style.color).toBe('red');
instance.destroy(); instance.destroy();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
jest.useRealTimers(); jest.useRealTimers();
}); });
}); });
@ -487,7 +511,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
jest.useFakeTimers(); jest.useFakeTimers();
Modal.destroyAll(); // clear destroyFns Modal.destroyAll(); // clear destroyFns
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
const instances = []; const instances = [];
['info', 'success', 'warning', 'error'].forEach(type => { ['info', 'success', 'warning', 'error'].forEach(type => {
@ -716,18 +742,26 @@ describe('Modal.confirm triggers callbacks correctly', () => {
onCancel: close => mock(close), onCancel: close => mock(close),
}); });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
await sleep(); await sleep();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1); expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
TestUtils.Simulate.keyDown($$('.ant-modal')[0], { TestUtils.Simulate.keyDown($$('.ant-modal')[0], {
keyCode: KeyCode.ESC, keyCode: KeyCode.ESC,
}); });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
await sleep(0); await sleep(0);
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0); expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0);
expect(mock).toBeCalledWith(expect.any(Function)); expect(mock).toBeCalledWith(expect.any(Function));

View File

@ -4,7 +4,7 @@ import React from 'react';
import Popconfirm from '..'; import Popconfirm from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; 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 Button from '../../button';
describe('Popconfirm', () => { describe('Popconfirm', () => {
@ -102,7 +102,9 @@ describe('Popconfirm', () => {
expect(ref.current.getPopupDomNode().className).not.toContain('ant-popover-hidden'); expect(ref.current.getPopupDomNode().className).not.toContain('ant-popover-hidden');
popconfirm.setProps({ visible: false }); popconfirm.setProps({ visible: false });
popconfirm.update(); // https://github.com/enzymejs/enzyme/issues/2305 popconfirm.update(); // https://github.com/enzymejs/enzyme/issues/2305
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(popconfirm.find('Trigger').props().popupVisible).toBe(false); expect(popconfirm.find('Trigger').props().popupVisible).toBe(false);
jest.useRealTimers(); jest.useRealTimers();
}); });

View File

@ -1,10 +1,9 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import Select from '..'; import Select from '..';
import focusTest from '../../../tests/shared/focusTest'; import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render } from '../../../tests/utils'; import { fireEvent, render, act } from '../../../tests/utils';
import Icon from '../../icon'; import Icon from '../../icon';
const { Option } = Select; const { Option } = Select;
@ -15,8 +14,8 @@ describe('Select', () => {
rtlTest(Select); rtlTest(Select);
function toggleOpen(container) { function toggleOpen(container) {
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
act(() => { act(() => {
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
jest.runAllTimers(); jest.runAllTimers();
}); });
} }
@ -123,7 +122,9 @@ describe('Select', () => {
<Option value="1">1</Option> <Option value="1">1</Option>
</Select>, </Select>,
); );
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(asFragment().firstChild).toMatchSnapshot(); expect(asFragment().firstChild).toMatchSnapshot();
}); });
}); });

View File

@ -1,8 +1,7 @@
/* eslint-disable react/no-multi-comp */ /* eslint-disable react/no-multi-comp */
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import Table from '..'; import Table from '..';
import { fireEvent, render, waitFor } from '../../../tests/utils'; import { fireEvent, render, waitFor, act } from '../../../tests/utils';
import Button from '../../button'; import Button from '../../button';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';
import Input from '../../input'; import Input from '../../input';

View File

@ -2,9 +2,8 @@
jest.mock('../../_util/scrollTo'); jest.mock('../../_util/scrollTo');
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import Table from '..'; import Table from '..';
import { fireEvent, render } from '../../../tests/utils'; import { fireEvent, render, act } from '../../../tests/utils';
import scrollTo from '../../_util/scrollTo'; import scrollTo from '../../_util/scrollTo';
import { resetWarned } from '../../_util/warning'; import { resetWarned } from '../../_util/warning';

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import Table from '..'; import Table from '..';
import { fireEvent, render } from '../../../tests/utils'; import { fireEvent, render, act } from '../../../tests/utils';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';
import { resetWarned } from '../../_util/warning'; import { resetWarned } from '../../_util/warning';

View File

@ -3,6 +3,7 @@ import React from 'react';
import Tag from '..'; import Tag from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
import { act } from '../../../tests/utils';
describe('Tag', () => { describe('Tag', () => {
mountTest(Tag); mountTest(Tag);
@ -25,7 +26,9 @@ describe('Tag', () => {
expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1); expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
wrapper.find('.anticon-close').simulate('click'); wrapper.find('.anticon-close').simulate('click');
expect(onClose).toHaveBeenCalled(); expect(onClose).toHaveBeenCalled();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
wrapper.update(); wrapper.update();
expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(0); 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('.anticon-close').length).toBe(1);
expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1); expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
wrapper.find('.anticon-close').simulate('click'); wrapper.find('.anticon-close').simulate('click');
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1); expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
}); });
@ -81,10 +86,14 @@ describe('Tag', () => {
const wrapper = mount(<Tag visible />); const wrapper = mount(<Tag visible />);
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
wrapper.setProps({ visible: false }); wrapper.setProps({ visible: false });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
wrapper.setProps({ visible: true }); wrapper.setProps({ visible: true });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
}); });
@ -92,10 +101,14 @@ describe('Tag', () => {
const wrapper = mount(<Tag visible={false} />); const wrapper = mount(<Tag visible={false} />);
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
wrapper.setProps({ visible: true }); wrapper.setProps({ visible: true });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
wrapper.setProps({ visible: false }); wrapper.setProps({ visible: false });
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
}); });
}); });

View File

@ -1,8 +1,7 @@
import { LikeOutlined, SmileOutlined } from '@ant-design/icons'; import { LikeOutlined, SmileOutlined } from '@ant-design/icons';
import { act } from '@testing-library/react';
import * as copyObj from 'copy-to-clipboard'; import * as copyObj from 'copy-to-clipboard';
import React from 'react'; import React from 'react';
import { fireEvent, render, waitFor } from '../../../tests/utils'; import { fireEvent, render, waitFor, act } from '../../../tests/utils';
import Base from '../Base'; import Base from '../Base';
@ -90,7 +89,9 @@ describe('Typography copy', () => {
jest.useFakeTimers(); jest.useFakeTimers();
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]); fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
unmount(); unmount();
jest.useRealTimers(); jest.useRealTimers();

View File

@ -5,7 +5,7 @@ import { resetWarned } from 'rc-util/lib/warning';
import React from 'react'; import React from 'react';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; 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 Base from '../Base';
import Link from '../Link'; import Link from '../Link';
import Paragraph from '../Paragraph'; import Paragraph from '../Paragraph';
@ -104,7 +104,9 @@ describe('Typography', () => {
} }
fireEvent.mouseEnter(wrapper.querySelector('.ant-typography-copy')); fireEvent.mouseEnter(wrapper.querySelector('.ant-typography-copy'));
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
if (tooltips === undefined || tooltips === true) { if (tooltips === undefined || tooltips === true) {
await waitFor(() => { await waitFor(() => {
@ -271,7 +273,9 @@ describe('Typography', () => {
expect(onStart).not.toHaveBeenCalled(); expect(onStart).not.toHaveBeenCalled();
} }
fireEvent.mouseEnter(wrapper.querySelectorAll('.ant-typography-edit')[0]); fireEvent.mouseEnter(wrapper.querySelectorAll('.ant-typography-edit')[0]);
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
if (tooltip === undefined || tooltip === true) { if (tooltip === undefined || tooltip === true) {
await waitFor(() => { await waitFor(() => {

View File

@ -1,9 +1,8 @@
/* eslint-disable react/no-string-refs, react/prefer-es6-class */ /* eslint-disable react/no-string-refs, react/prefer-es6-class */
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import Upload from '..'; import Upload from '..';
import mountTest from '../../../tests/shared/mountTest'; 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'; import { setup, teardown } from './mock';
describe('Upload.Dragger', () => { describe('Upload.Dragger', () => {
@ -26,7 +25,7 @@ describe('Upload.Dragger', () => {
}, },
}); });
await act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
}); });

View File

@ -2,11 +2,10 @@
import produce from 'immer'; import produce from 'immer';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import Upload from '..'; import Upload from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; 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 Form from '../../form';
import { resetWarned } from '../../_util/warning'; import { resetWarned } from '../../_util/warning';
import { getFileItem, isImageUrl, removeFileItem } from '../utils'; import { getFileItem, isImageUrl, removeFileItem } from '../utils';
@ -322,7 +321,9 @@ describe('Upload', () => {
const { rerender } = render(<Upload ref={ref} />); const { rerender } = render(<Upload ref={ref} />);
expect(ref.current.fileList).toEqual([]); expect(ref.current.fileList).toEqual([]);
rerender(<Upload ref={ref} fileList={fileList} />); rerender(<Upload ref={ref} fileList={fileList} />);
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(ref.current.fileList).toEqual(fileList); expect(ref.current.fileList).toEqual(fileList);
jest.useRealTimers(); jest.useRealTimers();
}); });
@ -725,7 +726,7 @@ describe('Upload', () => {
await Promise.resolve(); await Promise.resolve();
} }
}); });
await act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
}); });
await act(async () => { await act(async () => {
@ -945,7 +946,7 @@ describe('Upload', () => {
}); });
// Motion leave status change: start > active // Motion leave status change: start > active
await act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
}); });

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import Upload from '..'; 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 Form from '../../form';
import UploadList from '../UploadList'; import UploadList from '../UploadList';
import { previewImage } from '../utils'; import { previewImage } from '../utils';
@ -263,7 +262,7 @@ describe('Upload List', () => {
// Error message // Error message
fireEvent.mouseEnter(wrapper.querySelector('.ant-upload-list-item')); fireEvent.mouseEnter(wrapper.querySelector('.ant-upload-list-item'));
await act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
}); });
@ -574,7 +573,9 @@ describe('Upload List', () => {
/>, />,
); );
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
unmount(); unmount();
@ -1132,13 +1133,15 @@ describe('Upload List', () => {
await waitPromise(); await waitPromise();
// Wait for mock request finish request // Wait for mock request finish request
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
// Basic called times // Basic called times
expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalled();
// Check for images // Check for images
await act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
}); });
const afterImgNode = wrapper.container.querySelectorAll( const afterImgNode = wrapper.container.querySelectorAll(
@ -1313,7 +1316,7 @@ describe('Upload List', () => {
expect(uploadRef.current.fileList).toHaveLength(fileNames.length); expect(uploadRef.current.fileList).toHaveLength(fileNames.length);
await act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
}); });
expect(uploadRef.current.fileList).toHaveLength(fileNames.length); expect(uploadRef.current.fileList).toHaveLength(fileNames.length);

9
site/theme/en-US.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
interface ENLocale {
locale: 'en-US';
messages: {
[key: PropertyKey]: string;
};
}
const enLocale: ENLocale;
export default enLocale;

View File

@ -1,7 +1,6 @@
/* eslint-disable camelcase */ /* eslint-disable camelcase */
import React from 'react'; import React from 'react';
import AntdIcon, { createFromIconfontCN } from '@ant-design/icons'; import AntdIcon, { createFromIconfontCN } from '@ant-design/icons';
import { withThemeSuffix, removeTypeTheme, getThemeFromTypeName } from './utils'; import { withThemeSuffix, removeTypeTheme, getThemeFromTypeName } from './utils';
import warning from '../../../../components/_util/warning'; import warning from '../../../../components/_util/warning';
@ -9,7 +8,16 @@ const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_1329669_t1u72b9zk8s.js', 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; const { type, theme } = props;
let computedType = type; let computedType = type;
if (theme) { if (theme) {
@ -25,12 +33,8 @@ const OldIcon = props => {
return <IconFont {...props} type={`icon-${computedType}`} />; return <IconFont {...props} type={`icon-${computedType}`} />;
}; };
const Icon = props => { const Icon: React.FC<IconProps> & CreateIconfont = props =>
if (typeof props.type === 'string') { typeof props.type === 'string' ? <OldIcon {...props} /> : <AntdIcon {...props} />;
return <OldIcon {...props} />;
}
return <AntdIcon {...props} />;
};
Icon.createFromIconfontCN = createFromIconfontCN; Icon.createFromIconfontCN = createFromIconfontCN;

View File

@ -14,8 +14,8 @@ const fillTester = /-fill$/;
const outlineTester = /-o$/; const outlineTester = /-o$/;
const twoToneTester = /-twotone$/; const twoToneTester = /-twotone$/;
export function getThemeFromTypeName(type) { export function getThemeFromTypeName(type: string) {
let result = null; let result: string | null = null;
if (fillTester.test(type)) { if (fillTester.test(type)) {
result = 'filled'; result = 'filled';
} else if (outlineTester.test(type)) { } else if (outlineTester.test(type)) {
@ -26,12 +26,12 @@ export function getThemeFromTypeName(type) {
return result; return result;
} }
export function removeTypeTheme(type) { export function removeTypeTheme(type: string): string {
return type.replace(fillTester, '').replace(outlineTester, '').replace(twoToneTester, ''); return type.replace(fillTester, '').replace(outlineTester, '').replace(twoToneTester, '');
} }
export function withThemeSuffix(type, theme) { export function withThemeSuffix(type: string, theme: string): string {
let result = type; let result: string = type;
if (theme === 'filled') { if (theme === 'filled') {
result += '-fill'; result += '-fill';
} else if (theme === 'outlined') { } else if (theme === 'outlined') {

View File

@ -31,7 +31,7 @@ export interface HeaderProps {
intl: { locale: string }; intl: { locale: string };
location: { pathname: string; query: any }; location: { pathname: string; query: any };
router: any; router: any;
themeConfig: { docVersions: Record<string, string> }; themeConfig?: { docVersions: Record<string, string> };
changeDirection: (direction: DirectionType) => void; changeDirection: (direction: DirectionType) => void;
} }
@ -202,7 +202,7 @@ const Header: React.FC<HeaderProps & WrappedComponentProps<'intl'>> = props => {
const { menuVisible, windowWidth, searching, showTechUIButton } = headerState; const { menuVisible, windowWidth, searching, showTechUIButton } = headerState;
const docVersions: Record<string, string> = { const docVersions: Record<string, string> = {
[antdVersion]: antdVersion, [antdVersion]: antdVersion,
...themeConfig.docVersions, ...themeConfig?.docVersions,
}; };
const versionOptions = Object.keys(docVersions).map(version => ( const versionOptions = Object.keys(docVersions).map(version => (
<Option value={docVersions[version]} key={version}> <Option value={docVersions[version]} key={version}>

View File

@ -4,6 +4,9 @@ import type { DirectionType } from 'antd/es/config-provider';
export interface SiteContextProps { export interface SiteContextProps {
isMobile: boolean; isMobile: boolean;
direction: DirectionType; direction: DirectionType;
theme?: string;
setTheme?: (theme: string, persist?: boolean) => void;
setIframeTheme?: (iframeNode: HTMLIFrameElement, theme: string) => void;
} }
const SiteContext = React.createContext<SiteContextProps>({ const SiteContext = React.createContext<SiteContextProps>({

View File

@ -1,24 +1,28 @@
import { presetDarkPalettes, presetPalettes } from '@ant-design/colors'; /* eslint-disable class-methods-use-this */
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';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Helmet, HelmetProvider } from 'react-helmet-async'; import { Helmet, HelmetProvider } from 'react-helmet-async';
import { IntlProvider } from 'react-intl'; import { IntlProvider } from 'react-intl';
import themeSwitcher from 'theme-switcher'; 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 enLocale from '../../en-US';
import cnLocale from '../../zh-CN'; import cnLocale from '../../zh-CN';
import * as utils from '../utils'; import * as utils from '../utils';
import Header from './Header'; import Header from './Header';
import type { SiteContextProps } from './SiteContext';
import SiteContext from './SiteContext'; import SiteContext from './SiteContext';
import defaultSeedToken from '../../../../components/theme/themes/seed'; import defaultSeedToken from '../../../../components/theme/themes/seed';
import DynamicTheme from './DynamicTheme'; import DynamicTheme from './DynamicTheme';
import 'moment/locale/zh-cn';
if (typeof window !== 'undefined' && navigator.serviceWorker) { if (typeof window !== 'undefined' && navigator.serviceWorker) {
navigator.serviceWorker.getRegistrations().then(registrations => { navigator.serviceWorker.getRegistrations().then(registrations => {
@ -36,12 +40,12 @@ if (typeof window !== 'undefined') {
require('../../static/style'); require('../../static/style');
// Expose to iframe // Expose to iframe
window.react = React; (window as any).react = React;
window['react-dom'] = ReactDOM; (window as any)['react-dom'] = ReactDOM;
// eslint-disable-next-line global-require // eslint-disable-next-line global-require
window.antd = require('antd'); (window as any).antd = require('antd');
// eslint-disable-next-line global-require // 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 // Error log statistic
window.addEventListener('error', e => { window.addEventListener('error', e => {
@ -56,7 +60,7 @@ if (typeof window !== 'undefined') {
const RESPONSIVE_MOBILE = 768; const RESPONSIVE_MOBILE = 768;
// for dark.css timestamp to remove cache // for dark.css timestamp to remove cache
const timestamp = new Date().getTime(); const timestamp = Date.now();
const themeMap = { const themeMap = {
dark: `/dark.css?${timestamp}`, dark: `/dark.css?${timestamp}`,
compact: `/compact.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 // Pass to global since bisheng do not have the process for wrapper
const styleCache = createCache(); const styleCache = createCache();
if (typeof global !== 'undefined') { 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; static contextType = SiteContext;
timer: NodeJS.Timeout | null = null;
isBeforeComponent = false; isBeforeComponent = false;
syncIframeThemeId = null; syncIframeThemeId?: number;
constructor(props) { constructor(props: LayoutPropsType) {
super(props); super(props);
const { pathname } = props.location; const { pathname } = props.location;
const appLocale = utils.isZhCN(pathname) ? cnLocale : enLocale; const appLocale = utils.isZhCN(pathname) ? cnLocale : enLocale;
@ -87,8 +112,9 @@ export default class Layout extends React.Component {
this.state = { this.state = {
appLocale, appLocale,
theme: 'default', theme: 'default',
setTheme: this.setTheme,
direction: 'ltr', direction: 'ltr',
isMobile: false,
setTheme: this.setTheme,
setIframeTheme: this.setIframeTheme, setIframeTheme: this.setIframeTheme,
v5theme: 'default', v5theme: 'default',
designToken: defaultSeedToken, designToken: defaultSeedToken,
@ -98,50 +124,40 @@ export default class Layout extends React.Component {
componentDidMount() { componentDidMount() {
const { location, router } = this.props; const { location, router } = this.props;
router.listen(({ pathname, search }) => { router.listen(({ pathname, search }: any) => {
const { theme } = this.props.location.query; const { theme } = this.props.location.query;
if (typeof window.ga !== 'undefined') { if (typeof (window as any).ga !== 'undefined') {
window.ga('send', 'pageview', pathname + search); (window as any).ga('send', 'pageview', pathname + search);
} }
// eslint-disable-next-line if (typeof (window as any)._hmt !== 'undefined') {
if (typeof window._hmt !== 'undefined') { (window as any)._hmt.push(['_trackPageview', pathname + search]);
// eslint-disable-next-line
window._hmt.push(['_trackPageview', pathname + search]);
} }
const componentPage = /^\/?components/.test(pathname); const componentPage = /^\/?components/.test(pathname);
// only component page can use `dark` theme // only component page can use `dark` theme
if (!componentPage) { if (!componentPage) {
this.isBeforeComponent = false; this.isBeforeComponent = false;
this.setTheme('default', false); this.setTheme?.('default', false);
} else if (theme && !this.isBeforeComponent) { } else if (theme && !this.isBeforeComponent) {
this.isBeforeComponent = true; this.isBeforeComponent = true;
this.setTheme(theme, false); this.setTheme?.(theme, false);
} }
}); });
if (location.query.theme && /^\/?components/.test(location.pathname)) { if (location.query.theme && /^\/?components/.test(location.pathname)) {
this.isBeforeComponent = true; this.isBeforeComponent = true;
this.setTheme(location.query.theme, false); this.setTheme?.(location.query.theme, false);
} else { } else {
this.isBeforeComponent = false; this.isBeforeComponent = false;
this.setTheme('default', false); this.setTheme?.('default', false);
} }
if (location.query.direction) { this.setState({ direction: location.query.direction || 'ltr' });
this.setState({
direction: location.query.direction,
});
} else {
this.setState({
direction: 'ltr',
});
}
const nprogressHiddenStyle = document.getElementById('nprogress-style'); const nprogressHiddenStyle = document.getElementById('nprogress-style');
if (nprogressHiddenStyle) { if (nprogressHiddenStyle) {
this.timer = setTimeout(() => { this.timer = setTimeout(() => {
nprogressHiddenStyle.parentNode.removeChild(nprogressHiddenStyle); nprogressHiddenStyle.parentNode?.removeChild(nprogressHiddenStyle);
}, 0); }, 0);
} }
@ -149,7 +165,7 @@ export default class Layout extends React.Component {
window.addEventListener('resize', this.updateMobileMode); window.addEventListener('resize', this.updateMobileMode);
// Sync iframe theme with current theme // Sync iframe theme with current theme
this.syncIframeThemeId = setInterval(() => { this.syncIframeThemeId = window.setInterval(() => {
const { designToken, hashedStyle } = this.state; const { designToken, hashedStyle } = this.state;
const content = JSON.stringify({ const content = JSON.stringify({
action: 'sync.theme', action: 'sync.theme',
@ -157,16 +173,19 @@ export default class Layout extends React.Component {
hashed: hashedStyle, hashed: hashedStyle,
}); });
document.querySelectorAll('iframe.iframe-demo').forEach(iframe => { document.querySelectorAll<HTMLIFrameElement>('iframe.iframe-demo').forEach(iframe => {
iframe.contentWindow.postMessage(content); iframe.contentWindow?.postMessage(content);
}); });
}, 1000); }, 1000);
} }
componentWillUnmount() { componentWillUnmount() {
clearTimeout(this.timer); clearTimeout(this.timer as unknown as number);
clearInterval(this.syncIframeThemeId); clearInterval(this.syncIframeThemeId as unknown as number);
window.removeEventListener('resize', this.updateMobileMode); window.removeEventListener('resize', this.updateMobileMode);
if (this.timer) {
clearTimeout(this.timer);
}
} }
updateMobileMode = () => { updateMobileMode = () => {
@ -179,19 +198,16 @@ export default class Layout extends React.Component {
} }
}; };
setIframeTheme = (iframeNode, theme) => { setIframeTheme: LayoutStateType['setIframeTheme'] = (iframeNode, theme) => {
iframeNode.contentWindow.postMessage( iframeNode.contentWindow?.postMessage(
JSON.stringify({ JSON.stringify({
action: 'change.theme', action: 'change.theme',
data: { data: { themeConfig, theme },
themeConfig,
theme,
},
}), }),
); );
}; };
setTheme = (theme, persist = true) => { setTheme: LayoutStateType['setTheme'] = (theme, persist = true) => {
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
return; return;
} }
@ -203,24 +219,23 @@ export default class Layout extends React.Component {
const iframeNodes = document.querySelectorAll('.iframe-demo'); const iframeNodes = document.querySelectorAll('.iframe-demo');
// loop element node // loop element node
[].forEach.call(iframeNodes, iframeNode => { [].forEach.call(iframeNodes, (iframeNode: HTMLIFrameElement) => {
this.setIframeTheme(iframeNode, theme); this.setIframeTheme?.(iframeNode, theme);
}); });
this.setState({ this.setState({ theme });
theme,
});
const iconTwoToneThemeMap = { const iconTwoToneThemeMap = {
dark: [presetDarkPalettes.blue.primary, '#111d2c'], dark: [presetDarkPalettes.blue.primary, '#111d2c'],
default: presetPalettes.blue.primary, default: presetPalettes.blue.primary,
}; } as const;
setTwoToneColor(iconTwoToneThemeMap[theme] || iconTwoToneThemeMap.default); setTwoToneColor(
(iconTwoToneThemeMap[theme as keyof typeof iconTwoToneThemeMap] ||
iconTwoToneThemeMap.default) as TwoToneColor,
);
}; };
changeDirection = direction => { changeDirection = (direction: DirectionType): void => {
this.setState({ this.setState({ direction });
direction,
});
const { pathname, hash, query } = this.props.location; const { pathname, hash, query } = this.props.location;
if (direction === 'ltr') { if (direction === 'ltr') {
delete query.direction; 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'; : '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 ( return (
<StyleProvider cache={styleCache}> <StyleProvider cache={styleCache}>
{/* eslint-disable-next-line react/jsx-no-constructed-context-values */}
<SiteContext.Provider value={{ isMobile, direction, theme, setTheme, setIframeTheme }}> <SiteContext.Provider value={{ isMobile, direction, theme, setTheme, setIframeTheme }}>
<HelmetProvider context={helmetContext}> <HelmetProvider context={helmetContext}>
<Helmet encodeSpecialCharacters={false}> <Helmet encodeSpecialCharacters={false}>
<html <html
lang={appLocale.locale === 'zh-CN' ? 'zh' : 'en'} lang={appLocale.locale === 'zh-CN' ? 'zh' : 'en'}
data-direction={direction} data-direction={direction}
className={classNames({ className={classNames({ [`rtl`]: direction === 'rtl' })}
[`rtl`]: direction === 'rtl',
})}
/> />
<title>{title}</title> <title>{title}</title>
<link <link
@ -286,7 +300,7 @@ export default class Layout extends React.Component {
defaultLocale="en-US" defaultLocale="en-US"
> >
<ConfigProvider <ConfigProvider
locale={appLocale.locale === 'zh-CN' ? zhCN : null} locale={appLocale.locale === 'zh-CN' ? zhCN : undefined}
direction={direction} direction={direction}
theme={{ theme={{
token: designToken, token: designToken,
@ -299,17 +313,19 @@ export default class Layout extends React.Component {
{children} {children}
<DynamicTheme <DynamicTheme
componentName={this.props.params?.children?.replace('-cn', '')} componentName={(this.props as any).params?.children?.replace('-cn', '')}
defaultToken={{ defaultToken={
theme: v5theme, {
...designToken, theme: v5theme,
hashed: hashedStyle, ...designToken,
}} hashed: hashedStyle,
} as any
}
onChangeTheme={newToken => { onChangeTheme={newToken => {
console.log('Change Theme:', newToken); console.log('Change Theme:', newToken);
const { hashed, theme, ...restToken } = newToken; const { hashed, newTheme, ...restToken } = newToken as any;
this.setState({ this.setState({
v5theme: theme, v5theme: newTheme,
designToken: restToken, designToken: restToken,
hashedStyle: hashed, hashedStyle: hashed,
}); });

9
site/theme/zh-CN.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
interface CNLocale {
locale: 'zh-CN';
messages: {
[key: PropertyKey]: string;
};
}
const cnLocale: CNLocale;
export default cnLocale;

View File

@ -1,9 +1,8 @@
import type { RenderOptions } from '@testing-library/react';
import { render } from '@testing-library/react';
import MockDate from 'mockdate'; import MockDate from 'mockdate';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import React, { StrictMode } 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 onLibResize } from 'rc-resize-observer/lib/utils/observerUtil';
import { _rs as onEsResize } from 'rc-resize-observer/es/utils/observerUtil'; import { _rs as onEsResize } from 'rc-resize-observer/es/utils/observerUtil';