chore: merge master

This commit is contained in:
zombiej 2022-06-10 17:17:54 +08:00
commit eda63041ff
26 changed files with 701 additions and 482 deletions

View File

@ -30,7 +30,7 @@ jobs:
key: lock-${{ github.sha }}
- name: create package-lock.json
run: npm i --package-lock-only --ignore-scripts --legacy-peer-deps
run: npm i --package-lock-only --ignore-scripts
- name: hack for single file
run: |

View File

@ -22,7 +22,7 @@ jobs:
key: lock-${{ github.sha }}
- name: create package-lock.json
run: npm i --package-lock-only --ignore-scripts --legacy-peer-deps
run: npm i --package-lock-only --ignore-scripts
- name: hack for single file
run: |

View File

@ -26,7 +26,7 @@ jobs:
key: lock-${{ github.sha }}
- name: create package-lock.json
run: npm i --package-lock-only --ignore-scripts --legacy-peer-deps
run: npm i --package-lock-only --ignore-scripts
- name: hack for single file
run: |

View File

@ -28,7 +28,7 @@ jobs:
node-version: '16'
- name: create package-lock.json
run: npm i --package-lock-only --ignore-scripts --legacy-peer-deps
run: npm i --package-lock-only --ignore-scripts
- name: hack for single file
run: |

View File

@ -1323,89 +1323,38 @@ Array [
`;
exports[`renders ./components/alert/demo/loop-banner.md extend context correctly 1`] = `
Array [
<div
class="ant-alert ant-alert-warning ant-alert-banner"
data-show="true"
role="alert"
<div
class="ant-alert ant-alert-warning ant-alert-banner"
data-show="true"
role="alert"
>
<span
aria-label="exclamation-circle"
class="anticon anticon-exclamation-circle ant-alert-icon"
role="img"
>
<span
aria-label="exclamation-circle"
class="anticon anticon-exclamation-circle ant-alert-icon"
role="img"
<svg
aria-hidden="true"
data-icon="exclamation-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<svg
aria-hidden="true"
data-icon="exclamation-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
/>
</svg>
</span>
<div
class="ant-alert-content"
>
<div
class="ant-alert-message"
>
<div
class="x0 x1 x2 x3 "
>
<div
style="transition:width 150ms linear;height:auto;width:auto"
>
<div
class="x1 x4 x5 x6"
style="opacity:1;transform:translateY(0px);position:relative"
>
<div>
Notice message one
</div>
</div>
</div>
</div>
</div>
</div>
</div>,
<div
class="ant-alert ant-alert-warning ant-alert-banner"
data-show="true"
role="alert"
>
<span
aria-label="exclamation-circle"
class="anticon anticon-exclamation-circle ant-alert-icon"
role="img"
>
<svg
aria-hidden="true"
data-icon="exclamation-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
/>
</svg>
</span>
<div
class="ant-alert-content"
>
<div
class="ant-alert-message"
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
/>
</div>
</div>,
]
</svg>
</span>
<div
class="ant-alert-content"
>
<div
class="ant-alert-message"
/>
</div>
</div>
`;
exports[`renders ./components/alert/demo/smooth-closed.md extend context correctly 1`] = `

View File

@ -1323,89 +1323,38 @@ Array [
`;
exports[`renders ./components/alert/demo/loop-banner.md correctly 1`] = `
Array [
<div
class="ant-alert ant-alert-warning ant-alert-banner"
data-show="true"
role="alert"
<div
class="ant-alert ant-alert-warning ant-alert-banner"
data-show="true"
role="alert"
>
<span
aria-label="exclamation-circle"
class="anticon anticon-exclamation-circle ant-alert-icon"
role="img"
>
<span
aria-label="exclamation-circle"
class="anticon anticon-exclamation-circle ant-alert-icon"
role="img"
<svg
aria-hidden="true"
data-icon="exclamation-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<svg
aria-hidden="true"
data-icon="exclamation-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
/>
</svg>
</span>
<div
class="ant-alert-content"
>
<div
class="ant-alert-message"
>
<div
class="x0 x1 x2 x3 "
>
<div
style="transition:width 150ms linear;height:auto;width:auto"
>
<div
class="x1 x4 x5 x6"
style="opacity:1;transform:translateY(0px);position:relative"
>
<div>
Notice message one
</div>
</div>
</div>
</div>
</div>
</div>
</div>,
<div
class="ant-alert ant-alert-warning ant-alert-banner"
data-show="true"
role="alert"
>
<span
aria-label="exclamation-circle"
class="anticon anticon-exclamation-circle ant-alert-icon"
role="img"
>
<svg
aria-hidden="true"
data-icon="exclamation-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
/>
</svg>
</span>
<div
class="ant-alert-content"
>
<div
class="ant-alert-message"
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
/>
</div>
</div>,
]
</svg>
</span>
<div
class="ant-alert-content"
>
<div
class="ant-alert-message"
/>
</div>
</div>
`;
exports[`renders ./components/alert/demo/smooth-closed.md correctly 1`] = `

View File

@ -17,30 +17,16 @@ Show a loop banner by using with [react-text-loop-next](https://npmjs.com/packag
import { Alert } from 'antd';
import React from 'react';
import Marquee from 'react-fast-marquee';
import { TextLoop } from 'react-text-loop-next';
const App: React.FC = () => (
<>
<Alert
banner
message={
<TextLoop mask>
<div>Notice message one</div>
<div>Notice message two</div>
<div>Notice message three</div>
<div>Notice message four</div>
</TextLoop>
}
/>
<Alert
banner
message={
<Marquee pauseOnHover gradient={false}>
I can be a React component, multiple React components, or just some text.
</Marquee>
}
/>
</>
<Alert
banner
message={
<Marquee pauseOnHover gradient={false}>
I can be a React component, multiple React components, or just some text.
</Marquee>
}
/>
);
export default App;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import AutoComplete from '..';
import { render } from '../../../tests/utils';
describe('AutoComplete children could be focus', () => {
beforeAll(() => {
@ -23,25 +23,35 @@ describe('AutoComplete children could be focus', () => {
it('focus() and onFocus', () => {
const handleFocus = jest.fn();
const wrapper = mount(<AutoComplete onFocus={handleFocus} />, { attachTo: container });
wrapper.find('input').instance().focus();
jest.runAllTimers();
const { container: wrapper } = render(<AutoComplete onFocus={handleFocus} />, {
attachTo: container,
});
wrapper.querySelector('input').focus();
act(() => {
jest.runAllTimers();
});
expect(handleFocus).toHaveBeenCalled();
});
it('blur() and onBlur', () => {
const handleBlur = jest.fn();
const wrapper = mount(<AutoComplete onBlur={handleBlur} />, { attachTo: container });
wrapper.find('input').instance().focus();
jest.runAllTimers();
wrapper.find('input').instance().blur();
jest.runAllTimers();
const { container: wrapper } = render(<AutoComplete onBlur={handleBlur} />, {
attachTo: container,
});
wrapper.querySelector('input').focus();
act(() => {
jest.runAllTimers();
});
wrapper.querySelector('input').blur();
act(() => {
jest.runAllTimers();
});
expect(handleBlur).toHaveBeenCalled();
});
it('child.ref should work', () => {
const mockRef = jest.fn();
mount(
render(
<AutoComplete dataSource={[]}>
<input ref={mockRef} />
</AutoComplete>,
@ -51,7 +61,7 @@ describe('AutoComplete children could be focus', () => {
it('child.ref instance should support be focused and blured', () => {
let inputRef;
mount(
render(
<AutoComplete dataSource={[]}>
<input
ref={node => {

View File

@ -1,30 +1,29 @@
import React from 'react';
import { mount } from 'enzyme';
import AutoComplete from '..';
import Input from '../../input';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render } from '../../../tests/utils';
import Input from '../../input';
describe('AutoComplete', () => {
mountTest(AutoComplete);
rtlTest(AutoComplete);
it('AutoComplete with custom Input render perfectly', () => {
const wrapper = mount(
const { container } = render(
<AutoComplete dataSource={['12345', '23456', '34567']}>
<textarea />
</AutoComplete>,
);
expect(wrapper.find('textarea').length).toBe(1);
wrapper.find('textarea').simulate('change', { target: { value: '123' } });
expect(container.querySelectorAll('textarea').length).toBe(1);
fireEvent.change(container.querySelector('textarea'), { target: { value: '123' } });
// should not filter data source defaultly
expect(wrapper.find('.ant-select-item-option').length).toBe(3);
expect(container.querySelectorAll('.ant-select-item-option').length).toBe(3);
});
it('AutoComplete should work when dataSource is object array', () => {
const wrapper = mount(
const { container } = render(
<AutoComplete
dataSource={[
{ text: 'text', value: 'value' },
@ -34,17 +33,17 @@ describe('AutoComplete', () => {
<input />
</AutoComplete>,
);
expect(wrapper.find('input').length).toBe(1);
wrapper.find('input').simulate('change', { target: { value: 'a' } });
expect(container.querySelectorAll('input').length).toBe(1);
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
// should not filter data source defaultly
expect(wrapper.find('.ant-select-item-option').length).toBe(2);
expect(container.querySelectorAll('.ant-select-item-option').length).toBe(2);
});
it('AutoComplete throws error when contains invalid dataSource', () => {
const spy = jest.spyOn(console, 'error').mockImplementation(() => undefined);
mount(
render(
<AutoComplete dataSource={[() => {}]}>
<textarea />
</AutoComplete>,
@ -54,25 +53,27 @@ describe('AutoComplete', () => {
});
it('legacy dataSource should accept react element option', () => {
const wrapper = mount(<AutoComplete open dataSource={[<span key="key">ReactNode</span>]} />);
expect(wrapper.render()).toMatchSnapshot();
const { asFragment } = render(
<AutoComplete open dataSource={[<span key="key">ReactNode</span>]} />,
);
expect(asFragment().firstChild).toMatchSnapshot();
});
it('legacy AutoComplete.Option should be compatiable', () => {
const wrapper = mount(
const { container } = render(
<AutoComplete>
<AutoComplete.Option value="111">111</AutoComplete.Option>
<AutoComplete.Option value="222">222</AutoComplete.Option>
</AutoComplete>,
);
expect(wrapper.find('input').length).toBe(1);
wrapper.find('input').simulate('change', { target: { value: '1' } });
expect(wrapper.find('.ant-select-item-option').length).toBe(2);
expect(container.querySelectorAll('input').length).toBe(1);
fireEvent.change(container.querySelector('input'), { target: { value: '1' } });
expect(container.querySelectorAll('.ant-select-item-option').length).toBe(2);
});
it('should not warning when getInputElement is null', () => {
jest.spyOn(console, 'warn').mockImplementation(() => undefined);
mount(<AutoComplete placeholder="input here" allowClear />);
render(<AutoComplete placeholder="input here" allowClear />);
// eslint-disable-next-line no-console
expect(console.warn).not.toBeCalled();
// eslint-disable-next-line no-console
@ -80,11 +81,11 @@ describe('AutoComplete', () => {
});
it('should not override custom input className', () => {
const wrapper = mount(
const { container } = render(
<AutoComplete>
<Input className="custom" />
</AutoComplete>,
);
expect(wrapper.find('input').hasClass('custom')).toBe(true);
expect(container.querySelector('input').classList.contains('custom')).toBeTruthy();
});
});

View File

@ -1,16 +1,14 @@
import React from 'react';
import { mount } from 'enzyme';
import { sleep } from '../../../tests/utils';
import BackTop from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import BackTop from '..';
import { fireEvent, render, sleep } from '../../../tests/utils';
describe('BackTop', () => {
mountTest(BackTop);
rtlTest(BackTop);
it('should scroll to top after click it', async () => {
const wrapper = mount(<BackTop visibilityHeight={-1} />);
const { container } = render(<BackTop visibilityHeight={-1} />);
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
window.scrollY = y;
window.pageYOffset = y;
@ -18,7 +16,7 @@ describe('BackTop', () => {
});
window.scrollTo(0, 400);
expect(document.documentElement.scrollTop).toBe(400);
wrapper.find('.ant-back-top').simulate('click');
fireEvent.click(container.querySelector('.ant-back-top'));
await sleep(500);
expect(document.documentElement.scrollTop).toBe(0);
scrollToSpy.mockRestore();
@ -26,24 +24,24 @@ describe('BackTop', () => {
it('support onClick', async () => {
const onClick = jest.fn();
const wrapper = mount(<BackTop onClick={onClick} visibilityHeight={-1} />);
const { container } = render(<BackTop onClick={onClick} visibilityHeight={-1} />);
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
window.scrollY = y;
window.pageYOffset = y;
});
document.dispatchEvent(new Event('scroll'));
window.scrollTo(0, 400);
wrapper.find('.ant-back-top').simulate('click');
fireEvent.click(container.querySelector('.ant-back-top'));
expect(onClick).toHaveBeenCalled();
scrollToSpy.mockRestore();
});
it('invalid target', async () => {
const onClick = jest.fn();
const wrapper = mount(
const { container } = render(
<BackTop onClick={onClick} visible target={() => ({ documentElement: {} })} />,
);
wrapper.find('.ant-back-top').simulate('click');
fireEvent.click(container.querySelector('.ant-back-top'));
expect(onClick).toHaveBeenCalled();
});
});

View File

@ -1,13 +1,13 @@
import EyeOutlined from '@ant-design/icons/EyeOutlined';
import classNames from 'classnames';
import RcImage, { ImageProps } from 'rc-image';
import * as React from 'react';
import { useContext } from 'react';
import EyeOutlined from '@ant-design/icons/EyeOutlined';
import RcImage, { ImageProps } from 'rc-image';
import classNames from 'classnames';
import defaultLocale from '../locale/en_US';
import PreviewGroup, { icons } from './PreviewGroup';
import { ConfigContext } from '../config-provider';
import defaultLocale from '../locale/en_US';
import { getTransitionName } from '../_util/motion';
// CSSINJS
import PreviewGroup, { icons } from './PreviewGroup';
import useStyle from './style';
export interface CompositionImage<P> extends React.FC<P> {
@ -20,11 +20,10 @@ const Image: CompositionImage<ImageProps> = ({
rootClassName,
...otherProps
}) => {
const { getPrefixCls } = useContext(ConfigContext);
const { getPrefixCls, locale: contextLocale = defaultLocale } = useContext(ConfigContext);
const prefixCls = getPrefixCls('image', customizePrefixCls);
const rootPrefixCls = getPrefixCls();
const { locale: contextLocale = defaultLocale } = useContext(ConfigContext);
const imageLocale = contextLocale.Image || defaultLocale.Image;
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);

View File

@ -1,8 +1,8 @@
import Pagination from 'rc-pagination/lib/locale/it_IT';
import DatePicker from '../date-picker/locale/it_IT';
import TimePicker from '../time-picker/locale/it_IT';
import Calendar from '../calendar/locale/it_IT';
import DatePicker from '../date-picker/locale/it_IT';
import type { Locale } from '../locale-provider';
import TimePicker from '../time-picker/locale/it_IT';
const localeValues: Locale = {
locale: 'it',
@ -23,8 +23,8 @@ const localeValues: Locale = {
selectInvert: 'Inverti selezione nella pagina corrente',
sortTitle: 'Ordina',
triggerDesc: 'Clicca per ordinare in modo discendente',
triggerAsc: 'Clicca per ordinare in modo ascendente',
cancelSort: 'Clicca per eliminare i filtri',
triggerAsc: 'Clicca per ordinare in modo ascendente',
cancelSort: "Clicca per eliminare l'ordinamento",
},
Modal: {
okText: 'OK',

View File

@ -162,6 +162,167 @@ Array [
]
`;
exports[`Menu all types must be available in the "items" syntax 1`] = `
Array [
<ul
class="ant-menu ant-menu-root ant-menu-inline ant-menu-light"
data-menu-list="true"
role="menu"
tabindex="0"
>
<li
class="ant-menu-submenu ant-menu-submenu-inline ant-menu-submenu-open"
role="none"
>
<div
aria-controls="rc-menu-uuid-test-submenu-popup"
aria-expanded="true"
aria-haspopup="true"
class="ant-menu-submenu-title"
data-menu-id="rc-menu-uuid-test-submenu"
role="menuitem"
style="padding-left: 24px;"
tabindex="-1"
>
<span
class="ant-menu-title-content"
>
Submenu
</span>
<i
class="ant-menu-submenu-arrow"
/>
</div>
<ul
class="ant-menu ant-menu-sub ant-menu-inline"
data-menu-list="true"
id="rc-menu-uuid-test-submenu-popup"
>
<li
class="ant-menu-item ant-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-submenu-item1"
role="menuitem"
style="padding-left: 48px;"
tabindex="-1"
>
<span
class="ant-menu-title-content"
>
SubmenuItem 1
</span>
</li>
<li
class="ant-menu-item ant-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-submenu-item2"
role="menuitem"
style="padding-left: 48px;"
tabindex="-1"
>
<span
class="ant-menu-title-content"
>
SubmenuItem 2
</span>
</li>
</ul>
</li>
<li
class="ant-menu-item-divider"
/>
<li
class="ant-menu-item-group"
>
<div
class="ant-menu-item-group-title"
title="Group"
>
Group
</div>
<ul
class="ant-menu-item-group-list"
>
<li
class="ant-menu-item ant-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-group-item"
role="menuitem"
style="padding-left: 24px;"
tabindex="-1"
>
<span
class="ant-menu-title-content"
>
GroupItem
</span>
</li>
<li
class="ant-menu-item-divider"
/>
<li
class="ant-menu-submenu ant-menu-submenu-inline ant-menu-submenu-open"
role="none"
>
<div
aria-controls="rc-menu-uuid-test-group-submenu-popup"
aria-expanded="true"
aria-haspopup="true"
class="ant-menu-submenu-title"
data-menu-id="rc-menu-uuid-test-group-submenu"
role="menuitem"
style="padding-left: 24px;"
tabindex="-1"
>
<span
class="ant-menu-title-content"
>
GroupSubmenu
</span>
<i
class="ant-menu-submenu-arrow"
/>
</div>
<ul
class="ant-menu ant-menu-sub ant-menu-inline"
data-menu-list="true"
id="rc-menu-uuid-test-group-submenu-popup"
>
<li
class="ant-menu-item ant-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-group-submenu-item1"
role="menuitem"
style="padding-left: 48px;"
tabindex="-1"
>
<span
class="ant-menu-title-content"
>
GroupSubmenuItem 1
</span>
</li>
<li
class="ant-menu-item ant-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-group-submenu-item2"
role="menuitem"
style="padding-left: 48px;"
tabindex="-1"
>
<span
class="ant-menu-title-content"
>
GroupSubmenuItem 2
</span>
</li>
</ul>
</li>
</ul>
</li>
</ul>,
<div
aria-hidden="true"
style="display: none;"
/>,
]
`;
exports[`Menu rtl render component should be rendered correctly in RTL direction 1`] = `
Array [
<ul

View File

@ -1,19 +1,19 @@
import React, { useState } from 'react';
import { mount } from 'enzyme';
import {
MailOutlined,
InboxOutlined,
AppstoreOutlined,
InboxOutlined,
MailOutlined,
PieChartOutlined,
UserOutlined,
} from '@ant-design/icons';
import { mount } from 'enzyme';
import React, { useState } from 'react';
import { act } from 'react-dom/test-utils';
import Menu from '..';
import Layout from '../../layout';
import Tooltip from '../../tooltip';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { render, fireEvent } from '../../../tests/utils';
import { fireEvent, render } from '../../../tests/utils';
import Layout from '../../layout';
import Tooltip from '../../tooltip';
import collapseMotion from '../../_util/motion';
import { noop } from '../../_util/warning';
@ -960,4 +960,46 @@ describe('Menu', () => {
expect(wrapper.exists('.bamboo')).toBeTruthy();
});
it('all types must be available in the "items" syntax', () => {
const wrapper = mount(
<Menu
mode="inline"
defaultOpenKeys={['submenu', 'group-submenu']}
items={[
{
key: 'submenu',
label: 'Submenu',
children: [
{ key: 'submenu-item1', label: 'SubmenuItem 1' },
{ key: 'submenu-item2', label: 'SubmenuItem 2' },
],
},
{ key: 'divider', type: 'divider' },
{
key: 'group',
type: 'group',
label: 'Group',
children: [
{
key: 'group-item',
label: 'GroupItem',
},
{ key: 'group-divider', type: 'divider' },
{
key: 'group-submenu',
label: 'GroupSubmenu',
children: [
{ key: 'group-submenu-item1', label: 'GroupSubmenuItem 1' },
{ key: 'group-submenu-item2', label: 'GroupSubmenuItem 2' },
],
},
],
},
]}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
});
});

View File

@ -0,0 +1,39 @@
import Menu from '..';
describe('Menu.typescript', () => {
it('Menu.items', () => {
const menu = (
<Menu
items={[
{ key: 'item', title: 'Item' },
{
key: 'submenu',
theme: 'light',
children: [
{ key: 'submenu-item', title: 'SubmenuItem' },
{ key: 'submenu-submenu', theme: 'light', children: [] },
{ key: 'submenu-divider', type: 'divider' },
{ key: 'submenu-group', type: 'group' },
null,
],
},
{
key: 'group',
type: 'group',
children: [
{ key: 'group-item', label: 'GroupItem' },
{ key: 'group-submenu', theme: 'light', children: [] },
{ key: 'group-divider', type: 'divider' },
{ key: 'group-group', type: 'group' },
null,
],
},
{ key: 'divider', type: 'divider' },
null,
]}
/>
);
expect(menu).toBeTruthy();
});
});

View File

@ -1,14 +1,14 @@
import * as React from 'react';
import { ItemGroup } from 'rc-menu';
import type {
MenuItemType as RcMenuItemType,
MenuDividerType as RcMenuDividerType,
SubMenuType as RcSubMenuType,
MenuItemGroupType as RcMenuItemGroupType,
MenuItemType as RcMenuItemType,
SubMenuType as RcSubMenuType,
} from 'rc-menu/lib/interface';
import SubMenu from '../SubMenu';
import * as React from 'react';
import MenuDivider from '../MenuDivider';
import MenuItem from '../MenuItem';
import SubMenu from '../SubMenu';
interface MenuItemType extends RcMenuItemType {
danger?: boolean;
@ -23,7 +23,7 @@ interface SubMenuType extends Omit<RcSubMenuType, 'children'> {
}
interface MenuItemGroupType extends Omit<RcMenuItemGroupType, 'children'> {
children?: MenuItemType[];
children?: ItemType[];
key?: React.Key;
}

View File

@ -22,6 +22,6 @@ Segmented Controls. This component is available since `antd@4.20.0`.
| defaultValue | Default selected value | string \| number | | |
| disabled | Disable all segments | boolean | false | |
| onChange | The callback function that is triggered when the state changes | function(value: string \| number) | | |
| options | Set children optional | string\[] \| number\[] \| Array<{ label: string value: string icon? ReactNode disabled?: boolean className?: string }> | [] | |
| options | Set children optional | string\[] \| number\[] \| Array<{ label: ReactNode value: string icon? ReactNode disabled?: boolean className?: string }> | [] | |
| size | The size of the Segmented. | `large` \| `middle` \| `small` | - | |
| value | Currently selected value | string \| number | | |

View File

@ -25,6 +25,6 @@ cover: https://gw.alipayobjects.com/zos/bmw-prod/a3ff040f-24ba-43e0-92e9-c845df1
| defaultValue | 默认选中的值 | string \| number | | |
| disabled | 是否禁用 | boolean | false | |
| onChange | 选项变化时的回调函数 | function(value: string \| number) | | |
| options | 数据化配置选项内容 | string\[] \| number\[] \| Array<{ label: string value: string icon? ReactNode disabled?: boolean className?: string }> | [] | |
| options | 数据化配置选项内容 | string\[] \| number\[] \| Array<{ label: ReactNode value: string icon? ReactNode disabled?: boolean className?: string }> | [] | |
| size | 控件尺寸 | `large` \| `middle` \| `small` | - | |
| value | 当前选中的值 | string \| number | | |

View File

@ -31,6 +31,10 @@
return Math.round(hue);
};
var getSaturation = function(hsv, i, isLight) {
// grey color don't change saturation
if (hsv.h === 0 && hsv.s === 0) {
return hsv.s;
}
var saturation;
if (isLight) {
saturation = hsv.s - saturationStep * i;

View File

@ -1,7 +1,7 @@
import React from 'react';
import { mount } from 'enzyme';
import { SmileOutlined, LikeOutlined } from '@ant-design/icons';
import { LikeOutlined, SmileOutlined } from '@ant-design/icons';
import * as copyObj from 'copy-to-clipboard';
import React from 'react';
import { fireEvent, render, waitFor } from '../../../tests/utils';
import Base from '../Base';
@ -33,57 +33,69 @@ describe('Typography copy', () => {
}) {
it(name, async () => {
jest.useFakeTimers();
const wrapper = mount(
const { container: wrapper, unmount } = render(
<Base component="p" copyable={{ icon, tooltips }}>
test copy
</Base>,
);
if (iconClassNames[0] !== undefined) {
expect(wrapper.exists(iconClassNames[0])).toBeTruthy();
expect(wrapper.querySelector(iconClassNames[0])).not.toBeNull();
}
if (iconTexts[0] !== undefined) {
expect(wrapper.find('.ant-typography-copy').at(0).text()).toBe(iconTexts[0]);
expect(wrapper.querySelectorAll('.ant-typography-copy')[0].textContent).toBe(
iconTexts[0],
);
}
wrapper.find('.ant-typography-copy').first().simulate('mouseenter');
fireEvent.mouseEnter(wrapper.querySelectorAll('.ant-typography-copy')[0]);
jest.runAllTimers();
wrapper.update();
if (tooltipTexts[0] !== undefined) {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe(tooltipTexts[0]);
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner')?.textContent).toBe(
tooltipTexts[0],
);
});
}
if (tooltipLength !== undefined) {
expect(wrapper.find('.ant-tooltip-inner').length).toBe(tooltipLength);
await waitFor(() => {
expect(wrapper.querySelectorAll('.ant-tooltip-inner').length).toBe(tooltipLength);
});
}
wrapper.find('.ant-typography-copy').first().simulate('click');
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
jest.useRealTimers();
if (iconClassNames[1] !== undefined) {
expect(wrapper.exists(iconClassNames[1])).toBeTruthy();
expect(wrapper.querySelector(iconClassNames[1])).not.toBeNull();
}
wrapper.find('.ant-typography-copy').first().simulate('mouseenter');
wrapper.update();
fireEvent.mouseEnter(wrapper.querySelectorAll('.ant-typography-copy')[0]);
wrapper.find('.ant-typography-copy').first().simulate('mouseenter');
fireEvent.mouseEnter(wrapper.querySelectorAll('.ant-typography-copy')[0]);
if (tooltipTexts[1] !== undefined) {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe(tooltipTexts[1]);
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner')?.textContent).toBe(
tooltipTexts[1],
);
});
}
if (iconTexts[1] !== undefined) {
expect(wrapper.find('.ant-typography-copy').at(0).text()).toBe(iconTexts[1]);
expect(wrapper.querySelectorAll('.ant-typography-copy')[0].textContent).toBe(
iconTexts[1],
);
}
jest.useFakeTimers();
wrapper.find('.ant-typography-copy').first().simulate('click');
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
jest.runAllTimers();
wrapper.update();
wrapper.unmount();
unmount();
jest.useRealTimers();
});
}
const dom = (
<>
<span>1</span>2
@ -197,14 +209,14 @@ describe('Typography copy', () => {
it('copy click event stopPropagation', () => {
const onDivClick = jest.fn();
const wrapper = mount(
const { container: wrapper } = render(
<div onClick={onDivClick}>
<Base component="p" copyable>
test copy
</Base>
</div>,
);
wrapper.find('.ant-typography-copy').first().simulate('click');
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
expect(onDivClick).not.toBeCalled();
});
@ -212,12 +224,13 @@ describe('Typography copy', () => {
function onCopy(e: React.MouseEvent<HTMLDivElement>) {
expect(e).not.toBeUndefined();
}
const wrapper = mount(
const { container: wrapper } = render(
<Base component="p" copyable={{ onCopy }}>
test copy
</Base>,
);
wrapper.find('.ant-typography-copy').first().simulate('click');
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
});
it('copy to clipboard', done => {
@ -237,13 +250,13 @@ describe('Typography copy', () => {
</Base>
);
};
const wrapper = mount(<Test />);
const copyBtn = wrapper.find('.ant-typography-copy').first();
copyBtn.simulate('click');
const { container: wrapper } = render(<Test />);
const copyBtn = wrapper.querySelectorAll('.ant-typography-copy')[0];
fireEvent.click(copyBtn);
expect(spy.mock.calls[0][0]).toEqual(originText);
setTimeout(() => {
spy.mockReset();
copyBtn.simulate('click');
fireEvent.click(copyBtn);
expect(spy.mock.calls[0][0]).toEqual(nextText);
done();
}, 500);

View File

@ -1,11 +1,8 @@
import React from 'react';
import { mount } from 'enzyme';
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
import React from 'react';
import { fireEvent, render, sleep, triggerResize, waitFor } from '../../../tests/utils';
import Base from '../Base';
import Typography from '../Typography';
import { sleep } from '../../../tests/utils';
// eslint-disable-next-line no-unused-vars
import * as styleChecker from '../../_util/styleChecker';
jest.mock('copy-to-clipboard');
@ -53,47 +50,59 @@ describe('Typography.Ellipsis', () => {
'Bamboo is Little Light Bamboo is Little Light Bamboo is Little Light Bamboo is Little Light Bamboo is Little Light';
it('should trigger update', async () => {
const ref = React.createRef();
const onEllipsis = jest.fn();
const wrapper = mount(
<Base ellipsis={{ onEllipsis }} component="p" editable>
const {
container: wrapper,
rerender,
unmount,
} = render(
<Base ellipsis={{ onEllipsis }} component="p" editable ref={ref}>
{fullStr}
</Base>,
);
// First resize
wrapper.triggerResize();
triggerResize(ref.current);
await sleep(20);
wrapper.update();
expect(wrapper.text()).toEqual('Bamboo is Little ...');
expect(wrapper.firstChild.textContent).toEqual('Bamboo is Little ...');
expect(onEllipsis).toHaveBeenCalledWith(true);
onEllipsis.mockReset();
// Second resize
wrapper.setProps({ ellipsis: { rows: 2, onEllipsis } });
await sleep(20);
wrapper.update();
expect(wrapper.text()).toEqual('Bamboo is Little Light Bamboo is Litt...');
rerender(
<Base ellipsis={{ rows: 2, onEllipsis }} component="p" editable>
{fullStr}
</Base>,
);
expect(wrapper.textContent).toEqual('Bamboo is Little Light Bamboo is Litt...');
expect(onEllipsis).not.toHaveBeenCalled();
// Third resize
wrapper.setProps({ ellipsis: { rows: 99, onEllipsis } });
await sleep(20);
wrapper.update();
expect(wrapper.find('p').text()).toEqual(fullStr);
rerender(
<Base ellipsis={{ rows: 99, onEllipsis }} component="p" editable>
{fullStr}
</Base>,
);
expect(wrapper.querySelector('p').textContent).toEqual(fullStr);
expect(onEllipsis).toHaveBeenCalledWith(false);
wrapper.unmount();
unmount();
});
it('support css multiple lines', async () => {
const wrapper = mount(
const { container: wrapper } = render(
<Base ellipsis={{ rows: 2 }} component="p">
{fullStr}
</Base>,
);
expect(wrapper.exists('.ant-typography-ellipsis-multiple-line')).toBeTruthy();
expect(wrapper.find(Typography).prop('style').WebkitLineClamp).toEqual(2);
expect(
wrapper.querySelectorAll('.ant-typography-ellipsis-multiple-line').length,
).toBeGreaterThan(0);
expect(
wrapper.querySelector('.ant-typography-ellipsis-multiple-line').style.WebkitLineClamp,
).toEqual('2');
});
it('string with parentheses', async () => {
@ -105,71 +114,87 @@ describe('Typography.Ellipsis', () => {
design language for background applications, is refined by Ant UED Team.
Ant Design, a design language for background applications, is refined by
Ant UED Team.`;
const ref = React.createRef();
const onEllipsis = jest.fn();
const wrapper = mount(
<Base ellipsis={{ onEllipsis }} component="p" editable>
const { container: wrapper, unmount } = render(
<Base ellipsis={{ onEllipsis }} component="p" editable ref={ref}>
{parenthesesStr}
</Base>,
);
wrapper.triggerResize();
triggerResize(ref.current);
await sleep(20);
wrapper.update();
expect(wrapper.text()).toEqual('Ant Design, a des...');
const ellipsisSpan = wrapper.find('span[aria-hidden]').last();
expect(ellipsisSpan.text()).toEqual('...');
expect(wrapper.firstChild.textContent).toEqual('Ant Design, a des...');
const ellipsisSpans = wrapper.querySelectorAll('span[aria-hidden]');
expect(ellipsisSpans[ellipsisSpans.length - 1].textContent).toEqual('...');
onEllipsis.mockReset();
wrapper.unmount();
unmount();
});
it('should middle ellipsis', async () => {
const suffix = '--suffix';
const wrapper = mount(
<Base ellipsis={{ rows: 1, suffix }} component="p">
const ref = React.createRef();
const { container: wrapper, unmount } = render(
<Base ellipsis={{ rows: 1, suffix }} component="p" ref={ref}>
{fullStr}
</Base>,
);
wrapper.triggerResize();
triggerResize(ref.current);
await sleep(20);
wrapper.update();
expect(wrapper.find('p').text()).toEqual('Bamboo is...--suffix');
wrapper.unmount();
expect(wrapper.querySelector('p').textContent).toEqual('Bamboo is...--suffix');
unmount();
});
it('should front or middle ellipsis', async () => {
const suffix = '--The information is very important';
const wrapper = mount(
<Base ellipsis={{ rows: 1, suffix }} component="p">
const ref = React.createRef();
const {
container: wrapper,
rerender,
unmount,
} = render(
<Base ellipsis={{ rows: 1, suffix }} component="p" ref={ref}>
{fullStr}
</Base>,
);
wrapper.triggerResize();
triggerResize(ref.current);
await sleep(20);
wrapper.update();
expect(wrapper.find('p').text()).toEqual('...--The information is very important');
wrapper.setProps({ ellipsis: { rows: 2, suffix } });
await sleep(20);
wrapper.update();
expect(wrapper.find('p').text()).toEqual('Ba...--The information is very important');
expect(wrapper.querySelector('p').textContent).toEqual(
'...--The information is very important',
);
wrapper.setProps({ ellipsis: { rows: 99, suffix } });
await sleep(20);
wrapper.update();
expect(wrapper.find('p').text()).toEqual(fullStr + suffix);
rerender(
<Base ellipsis={{ rows: 2, suffix }} component="p">
{fullStr}
</Base>,
);
expect(wrapper.querySelector('p').textContent).toEqual(
'Ba...--The information is very important',
);
wrapper.unmount();
rerender(
<Base ellipsis={{ rows: 99, suffix }} component="p">
{fullStr}
</Base>,
);
expect(wrapper.querySelector('p').textContent).toEqual(fullStr + suffix);
unmount();
});
it('connect children', async () => {
const bamboo = 'Bamboo';
const is = ' is ';
const wrapper = mount(
<Base ellipsis component="p" editable>
const ref = React.createRef();
const { container: wrapper } = render(
<Base ellipsis component="p" editable ref={ref}>
{bamboo}
{is}
<code>Little</code>
@ -177,54 +202,49 @@ describe('Typography.Ellipsis', () => {
</Base>,
);
wrapper.triggerResize();
triggerResize(ref.current);
await sleep(20);
wrapper.update();
expect(wrapper.text()).toEqual('Bamboo is Little...');
expect(wrapper.textContent).toEqual('Bamboo is Little...');
});
it('should expandable work', async () => {
const onExpand = jest.fn();
const wrapper = mount(
const { container: wrapper } = render(
<Base ellipsis={{ expandable: true, onExpand }} component="p" copyable editable>
{fullStr}
</Base>,
);
await sleep(20);
wrapper.update();
wrapper.find('.ant-typography-expand').simulate('click');
fireEvent.click(wrapper.querySelector('.ant-typography-expand'));
expect(onExpand).toHaveBeenCalled();
await sleep(20);
wrapper.update();
expect(wrapper.find('p').text()).toEqual(fullStr);
expect(wrapper.querySelector('p').textContent).toEqual(fullStr);
});
it('should have custom expand style', async () => {
const symbol = 'more';
const wrapper = mount(
const { container: wrapper } = render(
<Base ellipsis={{ expandable: true, symbol }} component="p">
{fullStr}
</Base>,
);
await sleep(20);
wrapper.update();
expect(wrapper.find('.ant-typography-expand').text()).toEqual('more');
expect(wrapper.querySelector('.ant-typography-expand').textContent).toEqual('more');
});
it('can use css ellipsis', () => {
const wrapper = mount(<Base ellipsis component="p" />);
expect(wrapper.find('.ant-typography-ellipsis-single-line').length).toBeTruthy();
const { container: wrapper } = render(<Base ellipsis component="p" />);
expect(wrapper.querySelectorAll('.ant-typography-ellipsis-single-line').length).toBeGreaterThan(
0,
);
});
it('should calculate padding', () => {
const wrapper = mount(
const { container: wrapper } = render(
<Base ellipsis component="p" style={{ paddingTop: '12px', paddingBottom: '12px' }} />,
);
expect(wrapper.find('.ant-typography-ellipsis-single-line').length).toBeTruthy();
expect(wrapper.querySelectorAll('.ant-typography-ellipsis-single-line').length).toBeGreaterThan(
0,
);
});
describe('should tooltip support', () => {
@ -245,40 +265,52 @@ describe('Typography.Ellipsis', () => {
domSpy.mockRestore();
});
function getWrapper(tooltip) {
return mount(
<Base ellipsis={{ tooltip }} component="p">
async function getWrapper(tooltip) {
const ref = React.createRef();
const wrapper = render(
<Base ellipsis={{ tooltip }} component="p" ref={ref}>
{fullStr}
</Base>,
);
triggerResize(ref.current);
await sleep(20);
return wrapper;
}
it('boolean', async () => {
const wrapper = getWrapper(true);
await sleep(20);
wrapper.update();
expect(wrapper.find('Tooltip').prop('title')).toEqual(fullStr);
const { container, baseElement } = await getWrapper(true);
fireEvent.mouseEnter(container.firstChild);
await waitFor(() => {
expect(baseElement.querySelector('.ant-tooltip-open')).not.toBeNull();
});
});
it('customize', async () => {
const wrapper = getWrapper('Bamboo is Light');
await sleep(20);
wrapper.update();
expect(wrapper.find('Tooltip').prop('title')).toEqual('Bamboo is Light');
const { container, baseElement } = await getWrapper('Bamboo is Light');
fireEvent.mouseEnter(container.firstChild);
await waitFor(() => {
expect(baseElement.querySelector('.ant-tooltip-open')).not.toBeNull();
});
});
});
it('js ellipsis should show aria-label', () => {
const titleWrapper = mount(<Base title="bamboo" ellipsis={{ expandable: true }} />);
expect(titleWrapper.find('.ant-typography').prop('aria-label')).toEqual('bamboo');
const { container: titleWrapper } = render(
<Base title="bamboo" ellipsis={{ expandable: true }} />,
);
expect(titleWrapper.querySelector('.ant-typography').getAttribute('aria-label')).toEqual(
'bamboo',
);
const tooltipWrapper = mount(<Base ellipsis={{ expandable: true, tooltip: 'little' }} />);
expect(tooltipWrapper.find('.ant-typography').prop('aria-label')).toEqual('little');
const { container: tooltipWrapper } = render(
<Base ellipsis={{ expandable: true, tooltip: 'little' }} />,
);
expect(tooltipWrapper.querySelector('.ant-typography').getAttribute('aria-label')).toEqual(
'little',
);
});
it('should display tooltip if line clamp', () => {
it('should display tooltip if line clamp', async () => {
mockRectSpy = spyElementPrototypes(HTMLElement, {
scrollHeight: {
get() {
@ -299,12 +331,19 @@ describe('Typography.Ellipsis', () => {
},
});
const wrapper = mount(
<Base ellipsis={{ tooltip: 'This is tooltip', rows: 2 }}>
const ref = React.createRef();
const { container: wrapper, baseElement } = render(
<Base ellipsis={{ tooltip: 'This is tooltip', rows: 2 }} ref={ref}>
Ant Design, a design language for background applications, is refined by Ant UED Team.
</Base>,
);
expect(wrapper.find('EllipsisTooltip').prop('isEllipsis')).toBeTruthy();
triggerResize(ref.current);
await sleep(20);
fireEvent.mouseEnter(wrapper.firstChild);
await waitFor(() => {
expect(baseElement.querySelector('.ant-tooltip-open')).not.toBeNull();
});
mockRectSpy.mockRestore();
});
});

View File

@ -1,13 +1,12 @@
import React from 'react';
import { mount } from 'enzyme';
import KeyCode from 'rc-util/lib/KeyCode';
import { fireEvent, render } from '../../../tests/utils';
import Paragraph from '../Paragraph';
test('Callback on enter key is triggered', () => {
const onEditStart = jest.fn();
const onCopy = jest.fn();
const wrapper = mount(
const { container: wrapper } = render(
<Paragraph
copyable={{
onCopy,
@ -23,8 +22,8 @@ test('Callback on enter key is triggered', () => {
jest.spyOn(window, 'setTimeout').mockReturnValue(timer);
jest.spyOn(window, 'clearTimeout');
// must copy first, because editing button will hide copy button
wrapper.find('.ant-typography-copy').at(0).simulate('keyup', { keyCode: KeyCode.ENTER });
wrapper.find('.anticon-edit').at(0).simulate('keyup', { keyCode: KeyCode.ENTER });
fireEvent.keyUp(wrapper.querySelectorAll('.ant-typography-copy')[0], { keyCode: KeyCode.ENTER });
fireEvent.keyUp(wrapper.querySelectorAll('.anticon-edit')[0], { keyCode: KeyCode.ENTER });
expect(onEditStart.mock.calls.length).toBe(1);
expect(onCopy.mock.calls.length).toBe(1);

View File

@ -1,19 +1,17 @@
import React from 'react';
import { mount } from 'enzyme';
import { SmileOutlined, LikeOutlined, HighlightOutlined, CheckOutlined } from '@ant-design/icons';
import { CheckOutlined, HighlightOutlined, LikeOutlined, SmileOutlined } from '@ant-design/icons';
import copy from 'copy-to-clipboard';
import KeyCode from 'rc-util/lib/KeyCode';
import { resetWarned } from 'rc-util/lib/warning';
import { spyElementPrototype } from 'rc-util/lib/test/domHook';
import copy from 'copy-to-clipboard';
import Title from '../Title';
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 Base from '../Base';
import Link from '../Link';
import Paragraph from '../Paragraph';
import Text from '../Text';
import Base from '../Base';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import Title from '../Title';
import Typography from '../Typography';
import { sleep, render } from '../../../tests/utils';
jest.mock('copy-to-clipboard');
@ -79,7 +77,7 @@ describe('Typography', () => {
describe('Title', () => {
it('warning if `level` not correct', () => {
mount(<Title level={false} />);
render(<Title level={false} />);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Typography.Title] Title only accept `1 | 2 | 3 | 4 | 5` as `level` value. And `5` need 4.6.0+ version.',
@ -93,47 +91,57 @@ describe('Typography', () => {
it(name, async () => {
jest.useFakeTimers();
const onCopy = jest.fn();
const wrapper = mount(
const { container: wrapper, unmount } = render(
<Base component="p" copyable={{ text, onCopy, icon, tooltips, format }}>
test copy
</Base>,
);
if (icon) {
expect(wrapper.find('.anticon-smile').length).toBeTruthy();
expect(wrapper.querySelectorAll('.anticon-smile').length).toBeGreaterThan(0);
} else {
expect(wrapper.find('.anticon-copy').length).toBeTruthy();
expect(wrapper.querySelectorAll('.anticon-copy').length).toBeGreaterThan(0);
}
wrapper.find('.ant-typography-copy').first().simulate('mouseenter');
fireEvent.mouseEnter(wrapper.querySelector('.ant-typography-copy'));
jest.runAllTimers();
wrapper.update();
if (tooltips === undefined || tooltips === true) {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe('Copy');
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner').textContent).toBe('Copy');
});
} else if (tooltips === false) {
expect(wrapper.find('.ant-tooltip-inner').length).toBeFalsy();
await waitFor(() => {
expect(wrapper.querySelectorAll('.ant-tooltip-inner').length).toBe(0);
});
} else if (tooltips[0] === '' && tooltips[1] === '') {
expect(wrapper.find('.ant-tooltip-inner').length).toBeFalsy();
await waitFor(() => {
expect(wrapper.querySelectorAll('.ant-tooltip-inner').length).toBe(0);
});
} else if (tooltips[0] === '' && tooltips[1]) {
expect(wrapper.find('.ant-tooltip-inner').length).toBeFalsy();
await waitFor(() => {
expect(wrapper.querySelectorAll('.ant-tooltip-inner').length).toBe(0);
});
} else if (tooltips[1] === '' && tooltips[0]) {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe(tooltips[0]);
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner').textContent).toBe(tooltips[0]);
});
} else {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe(tooltips[0]);
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner').textContent).toBe(tooltips[0]);
});
}
wrapper.find('.ant-typography-copy').first().simulate('click');
fireEvent.click(wrapper.querySelector('.ant-typography-copy'));
jest.useRealTimers();
wrapper.find('.ant-typography-copy').first().simulate('mouseenter');
// tooltips 为 ['', 'xxx'] 时,切换时需要延时 mousenEnterDelay 的时长
fireEvent.mouseEnter(wrapper.querySelectorAll('.ant-typography-copy')[0]);
// tooltips 为 ['', 'xxx'] 时,切换时需要延时 mouseEnterDelay 的时长
if (tooltips && tooltips[0] === '' && tooltips[1]) {
await sleep(150);
}
expect(copy.lastStr).toEqual(target);
expect(copy.lastOptions.format).toEqual(format);
wrapper.update();
expect(onCopy).toHaveBeenCalled();
let copiedIcon = '.anticon-check';
@ -143,31 +151,43 @@ describe('Typography', () => {
copiedIcon = '.anticon-check';
}
expect(wrapper.find(copiedIcon).length).toBeTruthy();
wrapper.find('.ant-typography-copy').first().simulate('mouseenter');
expect(wrapper.querySelectorAll(copiedIcon).length).toBeGreaterThan(0);
fireEvent.mouseEnter(wrapper.querySelectorAll('.ant-typography-copy')[0]);
if (tooltips === undefined || tooltips === true) {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe('Copied');
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner').textContent).toBe('Copied');
});
} else if (tooltips === false) {
expect(wrapper.find('.ant-tooltip-inner').length).toBeFalsy();
await waitFor(() => {
expect(wrapper.querySelectorAll('.ant-tooltip-inner').length).toBe(0);
});
} else if (tooltips[0] === '' && tooltips[1] === '') {
expect(wrapper.find('.ant-tooltip-inner').length).toBeFalsy();
await waitFor(() => {
expect(wrapper.querySelectorAll('.ant-tooltip-inner').length).toBe(0);
});
} else if (tooltips[0] === '' && tooltips[1]) {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe(tooltips[1]);
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner').textContent).toBe(tooltips[1]);
});
} else if (tooltips[1] === '' && tooltips[0]) {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe('');
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner').textContent).toBe('');
});
} else {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe(tooltips[1]);
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner').textContent).toBe(tooltips[1]);
});
}
jest.useFakeTimers();
wrapper.find('.ant-typography-copy').first().simulate('click');
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
jest.runAllTimers();
wrapper.update();
// Will set back when 3 seconds pass
expect(wrapper.find(copiedIcon).length).toBeFalsy();
wrapper.unmount();
await sleep(3000);
expect(wrapper.querySelectorAll(copiedIcon).length).toBe(0);
unmount();
jest.useRealTimers();
});
}
@ -221,15 +241,15 @@ describe('Typography', () => {
submitFunc,
expectFunc,
) {
it(name, () => {
it(name, async () => {
jest.useFakeTimers();
const onStart = jest.fn();
const onChange = jest.fn();
const className = 'test';
const style = {};
const style = { padding: 'unset' };
const wrapper = mount(
const { container: wrapper } = render(
<Paragraph
editable={{ onChange, onStart, icon, tooltip, triggerType, enterIcon }}
className={className}
@ -241,64 +261,71 @@ describe('Typography', () => {
if (triggerType === undefined || triggerType.indexOf('icon') !== -1) {
if (icon) {
expect(wrapper.find('.anticon-highlight').length).toBeTruthy();
expect(wrapper.querySelectorAll('.anticon-highlight').length).toBeGreaterThan(0);
} else {
expect(wrapper.find('.anticon-edit').length).toBeTruthy();
expect(wrapper.querySelectorAll('.anticon-edit').length).toBeGreaterThan(0);
}
if (triggerType === undefined || triggerType.indexOf('text') === -1) {
wrapper.simulate('click');
fireEvent.click(wrapper.firstChild);
expect(onStart).not.toHaveBeenCalled();
}
wrapper.find('.ant-typography-edit').first().simulate('mouseenter');
fireEvent.mouseEnter(wrapper.querySelectorAll('.ant-typography-edit')[0]);
jest.runAllTimers();
wrapper.update();
if (tooltip === undefined || tooltip === true) {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe('Edit');
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner').textContent).toBe('Edit');
});
} else if (tooltip === false) {
expect(wrapper.find('.ant-tooltip-inner').length).toBeFalsy();
await waitFor(() => {
expect(wrapper.querySelectorAll('.ant-tooltip-inner').length).toBe(0);
});
} else {
expect(wrapper.find('.ant-tooltip-inner').text()).toBe(tooltip);
await waitFor(() => {
expect(wrapper.querySelector('.ant-tooltip-inner').textContent).toBe(tooltip);
});
}
wrapper.find('.ant-typography-edit').first().simulate('click');
fireEvent.click(wrapper.querySelectorAll('.ant-typography-edit')[0]);
expect(onStart).toHaveBeenCalled();
if (triggerType !== undefined && triggerType.indexOf('text') !== -1) {
wrapper.find('textarea').simulate('keyDown', { keyCode: KeyCode.ESC });
wrapper.find('textarea').simulate('keyUp', { keyCode: KeyCode.ESC });
fireEvent.keyDown(wrapper.querySelector('textarea'), { keyCode: KeyCode.ESC });
fireEvent.keyUp(wrapper.querySelector('textarea'), { keyCode: KeyCode.ESC });
expect(onChange).not.toHaveBeenCalled();
}
}
if (triggerType !== undefined && triggerType.indexOf('text') !== -1) {
if (triggerType.indexOf('icon') === -1) {
expect(wrapper.find('.anticon-highlight').length).toBeFalsy();
expect(wrapper.find('.anticon-edit').length).toBeFalsy();
expect(wrapper.querySelectorAll('.anticon-highlight').length).toBe(0);
expect(wrapper.querySelectorAll('.anticon-edit').length).toBe(0);
}
wrapper.simulate('click');
fireEvent.click(wrapper.firstChild);
expect(onStart).toHaveBeenCalled();
}
// Should have className
const props = wrapper.find('div').first().props();
expect(props.style).toEqual(style);
const props = wrapper.querySelectorAll('div')[0];
expect(props.getAttribute('style')).toContain('padding: unset');
expect(props.className.includes(className)).toBeTruthy();
wrapper.find('textarea').simulate('change', {
fireEvent.change(wrapper.querySelector('textarea'), {
target: { value: 'Bamboo' },
});
if (enterIcon === undefined) {
expect(
wrapper.find('span.ant-typography-edit-content-confirm').first().props().className,
wrapper.querySelectorAll('span.ant-typography-edit-content-confirm')[0].className,
).toContain('anticon-enter');
} else if (enterIcon === null) {
expect(wrapper.find('span.ant-typography-edit-content-confirm').length).toBe(0);
expect(
wrapper.querySelectorAll('span.ant-typography-edit-content-confirm').length,
).toBe(0);
} else {
expect(
wrapper.find('span.ant-typography-edit-content-confirm').first().props().className,
wrapper.querySelectorAll('span.ant-typography-edit-content-confirm')[0].className,
).not.toContain('anticon-enter');
}
@ -319,21 +346,21 @@ describe('Typography', () => {
testStep({ name: 'by key up' }, wrapper => {
// Not trigger when inComposition
wrapper.find('textarea').simulate('compositionStart');
wrapper.find('textarea').simulate('keyDown', { keyCode: KeyCode.ENTER });
wrapper.find('textarea').simulate('compositionEnd');
wrapper.find('textarea').simulate('keyUp', { keyCode: KeyCode.ENTER });
fireEvent.compositionStart(wrapper.querySelector('textarea'));
fireEvent.keyDown(wrapper.querySelector('textarea'), { keyCode: KeyCode.ENTER });
fireEvent.compositionEnd(wrapper.querySelector('textarea'));
fireEvent.keyUp(wrapper.querySelector('textarea'), { keyCode: KeyCode.ENTER });
// Now trigger
wrapper.find('textarea').simulate('keyDown', { keyCode: KeyCode.ENTER });
wrapper.find('textarea').simulate('keyUp', { keyCode: KeyCode.ENTER });
fireEvent.keyDown(wrapper.querySelector('textarea'), { keyCode: KeyCode.ENTER });
fireEvent.keyUp(wrapper.querySelector('textarea'), { keyCode: KeyCode.ENTER });
});
testStep(
{ name: 'by esc key' },
wrapper => {
wrapper.find('textarea').simulate('keyDown', { keyCode: KeyCode.ESC });
wrapper.find('textarea').simulate('keyUp', { keyCode: KeyCode.ESC });
fireEvent.keyDown(wrapper.querySelector('textarea'), { keyCode: KeyCode.ESC });
fireEvent.keyUp(wrapper.querySelector('textarea'), { keyCode: KeyCode.ESC });
},
onChange => {
// eslint-disable-next-line jest/no-standalone-expect
@ -342,7 +369,7 @@ describe('Typography', () => {
);
testStep({ name: 'by blur' }, wrapper => {
wrapper.find('textarea').simulate('blur');
fireEvent.blur(wrapper.querySelector('textarea'));
});
testStep({ name: 'customize edit icon', icon: <HighlightOutlined /> });
@ -359,47 +386,51 @@ describe('Typography', () => {
it('should trigger onEnd when type Enter', () => {
const onEnd = jest.fn();
const wrapper = mount(<Paragraph editable={{ onEnd }}>Bamboo</Paragraph>);
wrapper.find('.ant-typography-edit').first().simulate('click');
wrapper.find('textarea').simulate('keyDown', { keyCode: KeyCode.ENTER });
wrapper.find('textarea').simulate('keyUp', { keyCode: KeyCode.ENTER });
const { container: wrapper } = render(<Paragraph editable={{ onEnd }}>Bamboo</Paragraph>);
fireEvent.click(wrapper.querySelectorAll('.ant-typography-edit')[0]);
fireEvent.keyDown(wrapper.querySelector('textarea'), { keyCode: KeyCode.ENTER });
fireEvent.keyUp(wrapper.querySelector('textarea'), { keyCode: KeyCode.ENTER });
expect(onEnd).toHaveBeenCalledTimes(1);
});
it('should trigger onCancel when type ESC', () => {
const onCancel = jest.fn();
const wrapper = mount(<Paragraph editable={{ onCancel }}>Bamboo</Paragraph>);
wrapper.find('.ant-typography-edit').first().simulate('click');
wrapper.find('textarea').simulate('keyDown', { keyCode: KeyCode.ESC });
wrapper.find('textarea').simulate('keyUp', { keyCode: KeyCode.ESC });
const { container: wrapper } = render(
<Paragraph editable={{ onCancel }}>Bamboo</Paragraph>,
);
fireEvent.click(wrapper.querySelectorAll('.ant-typography-edit')[0]);
fireEvent.keyDown(wrapper.querySelector('textarea'), { keyCode: KeyCode.ESC });
fireEvent.keyUp(wrapper.querySelector('textarea'), { keyCode: KeyCode.ESC });
expect(onCancel).toHaveBeenCalledTimes(1);
});
it('should only trigger focus on the first time', () => {
let triggerTimes = 0;
const mockFocus = spyElementPrototype(HTMLElement, 'focus', () => {
const { container: wrapper } = render(<Paragraph editable>Bamboo</Paragraph>);
const editIcon = wrapper.querySelectorAll('.ant-typography-edit')[0];
editIcon.addEventListener('focus', () => {
triggerTimes += 1;
});
const wrapper = mount(<Paragraph editable>Bamboo</Paragraph>);
wrapper.find('.ant-typography-edit').first().simulate('click');
fireEvent.focus(editIcon);
expect(triggerTimes).toEqual(1);
wrapper.find('textarea').simulate('change', {
fireEvent.click(editIcon);
expect(triggerTimes).toEqual(1);
fireEvent.change(wrapper.querySelector('textarea'), {
target: { value: 'good' },
});
expect(triggerTimes).toEqual(1);
mockFocus.mockRestore();
});
});
it('should focus at the end of textarea', () => {
const wrapper = mount(<Paragraph editable>content</Paragraph>);
wrapper.find('.ant-typography-edit').first().simulate('click');
const textareaNode = wrapper.find('textarea').getDOMNode();
const { container: wrapper } = render(<Paragraph editable>content</Paragraph>);
fireEvent.click(wrapper.querySelectorAll('.ant-typography-edit')[0]);
const textareaNode = wrapper.querySelector('textarea');
expect(textareaNode.selectionStart).toBe(7);
expect(textareaNode.selectionEnd).toBe(7);
});
@ -407,7 +438,7 @@ describe('Typography', () => {
it('warning if use setContentRef', () => {
const refFunc = () => {};
mount(<Typography setContentRef={refFunc} />);
render(<Typography setContentRef={refFunc} />);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Typography] `setContentRef` is deprecated. Please use `ref` instead.',
);
@ -423,21 +454,21 @@ describe('Typography', () => {
it('should get HTMLHeadingElement ref from Title', () => {
const ref = React.createRef();
mount(<Title level={1} ref={ref} />);
render(<Title level={1} ref={ref} />);
expect(ref.current instanceof HTMLHeadingElement).toBe(true);
});
it('should get HTMLDivElement ref from Paragraph', () => {
const ref = React.createRef();
mount(<Paragraph ref={ref} />);
render(<Paragraph ref={ref} />);
expect(ref.current instanceof HTMLDivElement).toBe(true);
});
it('should get HTMLSpanElement ref from Text', () => {
const ref = React.createRef();
mount(<Text ref={ref} />);
render(<Text ref={ref} />);
expect(ref.current instanceof HTMLSpanElement).toBe(true);
});
});

View File

@ -527,7 +527,7 @@
// .@{upload-prefix-cls}-animate-inline-enter,
// .@{upload-prefix-cls}-animate-inline-leave {
// animation-duration: @animation-duration-slow;
// animation-fill-mode: @ease-in-out-circ;
// animation-timing-function: @ease-in-out-circ;
// }
// .@{upload-prefix-cls}-animate-inline-appear,

View File

@ -31,7 +31,7 @@ const genMotionStyle: GenerateStyle<UploadToken> = token => {
[`${componentCls}-wrapper`]: {
[`${inlineCls}-appear, ${inlineCls}-enter, ${inlineCls}-leave`]: {
animationDuration: token.motionDurationSlow,
animationFillMode: token.motionEaseInOutCirc,
animationTimingFunction: token.motionEaseInOutCirc,
},
[`${inlineCls}-appear, ${inlineCls}-enter`]: {

View File

@ -117,7 +117,7 @@
"@ant-design/colors": "^6.0.0",
"@ant-design/cssinjs": "^0.0.0-alpha.34",
"@ant-design/icons": "^4.7.0",
"@ant-design/react-slick": "~0.28.1",
"@ant-design/react-slick": "~0.29.1",
"@babel/runtime": "^7.12.5",
"@ctrl/tinycolor": "^3.4.0",
"classnames": "^2.2.6",
@ -128,11 +128,11 @@
"rc-cascader": "~3.6.0",
"rc-checkbox": "~2.3.0",
"rc-collapse": "~3.3.0",
"rc-dialog": "~8.8.2",
"rc-dialog": "~8.9.0",
"rc-drawer": "~4.4.2",
"rc-dropdown": "~4.0.0",
"rc-field-form": "~1.26.1",
"rc-image": "~5.6.0",
"rc-image": "~5.7.0",
"rc-input": "~0.0.1-alpha.5",
"rc-input-number": "~7.3.0",
"rc-mentions": "~1.8.0",
@ -162,9 +162,9 @@
"shallowequal": "^1.1.0"
},
"devDependencies": {
"@ant-design/bisheng-plugin": "^3.2.0",
"@ant-design/bisheng-plugin": "^3.3.0-alpha.4",
"@ant-design/hitu": "^0.0.0-alpha.13",
"@ant-design/tools": "^15.0.2",
"@ant-design/tools": "^15.0.3",
"@docsearch/css": "^3.0.0",
"@qixian.cs/github-contributors-list": "^1.0.3",
"@stackblitz/sdk": "^1.3.0",
@ -195,7 +195,7 @@
"antd-img-crop": "^4.0.0",
"array-move": "^4.0.0",
"babel-plugin-add-react-displayname": "^0.0.5",
"bisheng": "^3.5.0",
"bisheng": "^3.7.0-alpha.4",
"bisheng-plugin-description": "^0.1.4",
"bisheng-plugin-react": "^1.2.0",
"bisheng-plugin-toc": "^0.4.4",
@ -277,7 +277,6 @@
"react-router-dom": "^6.0.2",
"react-sortable-hoc": "^2.0.0",
"react-sticky": "^6.0.3",
"react-text-loop-next": "0.0.3",
"react-window": "^1.8.5",
"remark": "^14.0.1",
"remark-cli": "^10.0.0",