mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-21 00:14:44 +08:00
commit
0b2b27341c
2
.github/workflows/preview-build.yml
vendored
2
.github/workflows/preview-build.yml
vendored
@ -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: |
|
||||
|
2
.github/workflows/site-deploy.yml
vendored
2
.github/workflows/site-deploy.yml
vendored
@ -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: |
|
||||
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -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: |
|
||||
|
2
.github/workflows/ui.yml
vendored
2
.github/workflows/ui.yml
vendored
@ -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: |
|
||||
|
@ -37,7 +37,8 @@
|
||||
"alpha-value-notation": "number",
|
||||
"color-function-notation": "legacy",
|
||||
"selector-class-pattern": null,
|
||||
"selector-id-pattern": null
|
||||
"selector-id-pattern": null,
|
||||
"selector-not-notation": null
|
||||
},
|
||||
"ignoreFiles": ["components/style/color/{bezierEasing,colorPalette,tinyColor}.less"]
|
||||
}
|
||||
|
@ -15,6 +15,22 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.21.1
|
||||
|
||||
`2022-06-13`
|
||||
|
||||
- 🐞 Fixed Image the `getContainer` property not reading from ConfigProvider. [#36002](https://github.com/ant-design/ant-design/pull/36002) [@robothot](https://github.com/robothot)
|
||||
- 🐞 Fixed Button issue [#35952](https://github.com/ant-design/ant-design/issues/35952) where the `disabled` attribute does not take effect with `href`. [#35975](https://github.com/ant-design/ant-design/pull/35975) [@MuxinFeng](https://github.com/MuxinFeng)
|
||||
- 🐞 Fix less color palette algorithm according to `@ant-design/colors`. [#35954](https://github.com/ant-design/ant-design/pull/35954) [@christian-lechner](https://github.com/christian-lechner)
|
||||
- 🐞 Fix Upload image flickering. [#35943](https://github.com/ant-design/ant-design/pull/35943)
|
||||
- 💄 Remove styles from Form such as `status` for children of Modal and Drawer. [#35849](https://github.com/ant-design/ant-design/pull/35849)
|
||||
- TypeScript
|
||||
- 🤖 Fix type definition for `autoFocus` in Dropdown. [#35990](https://github.com/ant-design/ant-design/pull/35990) [@robothot](https://github.com/robothot)
|
||||
- 🤖 Fix type definition for `MenuItemGroupType` in Menu. [#35790](https://github.com/ant-design/ant-design/pull/35790) [@MasaoBlue](https://github.com/MasaoBlue)
|
||||
- 🤖 Fix Carousel type definition in React 18. [#35959](https://github.com/ant-design/ant-design/pull/35959)
|
||||
- 🌐 Localization
|
||||
- 🇮🇹 Fix italian translation for `Table.cancelSort` key. [#35970](https://github.com/ant-design/ant-design/pull/35970) [@gariggio](https://github.com/gariggio)
|
||||
|
||||
## 4.21.0
|
||||
|
||||
`2022-06-06`
|
||||
|
@ -15,6 +15,22 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.21.1
|
||||
|
||||
`2022-06-13`
|
||||
|
||||
- 🐞 修复 Image `getContainer` 属性没有从 ConfigProvider 中读取的问题。[#36002](https://github.com/ant-design/ant-design/pull/36002) [@robothot](https://github.com/robothot)
|
||||
- 🐞 修复 Button 有 `href` 时 `disabled` 属性不生效的问题。[#35952](https://github.com/ant-design/ant-design/issues/35952)。[#35975](https://github.com/ant-design/ant-design/pull/35975) [@MuxinFeng](https://github.com/MuxinFeng)
|
||||
- 🐞 修复 Upload 组件动画闪烁的问题。[#35943](https://github.com/ant-design/ant-design/pull/35943)
|
||||
- 🐞 修复 less 色彩算法,使其和 `@ant-design/colors` 保持一致。[#35954](https://github.com/ant-design/ant-design/pull/35954) [@christian-lechner](https://github.com/christian-lechner)
|
||||
- 💄 Form.Item 中的 Modal 或 Drawer 组件包含的控件去除 `status` 等受 Form 影响的样式。[#35849](https://github.com/ant-design/ant-design/pull/35849)
|
||||
- TypeScript
|
||||
- 🤖 修复 Dropdown `autoFocus` 属性定义。[#35990](https://github.com/ant-design/ant-design/pull/35990) [@robothot](https://github.com/robothot)
|
||||
- 🤖 修复 Menu 中 `MenuItemGroupType` 的类型定义。[#35790](https://github.com/ant-design/ant-design/pull/35790) [@MasaoBlue](https://github.com/MasaoBlue)
|
||||
- 🤖 修复 Carousel 在 React 18 下的 TS 定义问题。[#35959](https://github.com/ant-design/ant-design/pull/35959)
|
||||
- 🌐 国际化
|
||||
- 🇮🇹 修复 `Table.cancelSort` 的意大利语翻译。[#35970](https://github.com/ant-design/ant-design/pull/35970) [@gariggio](https://github.com/gariggio)
|
||||
|
||||
## 4.21.0
|
||||
|
||||
`2022-06-06`
|
||||
|
@ -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`] = `
|
||||
|
@ -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`] = `
|
||||
|
@ -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;
|
||||
|
@ -1,6 +1,7 @@
|
||||
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 +24,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 +62,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 => {
|
||||
|
@ -1,30 +1,30 @@
|
||||
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 +34,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 +54,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 +82,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();
|
||||
});
|
||||
});
|
||||
|
@ -41,7 +41,7 @@ The differences with Select are:
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| value | Selected option | string | - | |
|
||||
| onBlur | Called when leaving the component | function() | - | |
|
||||
| onChange | Called when select an option or input value change, or value of input is changed | function(value) | - | |
|
||||
| onChange | Called when selecting an option or changing an input value | function(value) | - | |
|
||||
| onDropdownVisibleChange | Call when dropdown open | function(open) | - | |
|
||||
| onFocus | Called when entering the component | function() | - | |
|
||||
| onSearch | Called when searching items | function(value) | - | |
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import { sleep, render, fireEvent } from '../../../tests/utils';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import BackTop from '..';
|
||||
@ -10,7 +9,7 @@ describe('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 +17,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 +25,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();
|
||||
});
|
||||
});
|
||||
|
@ -328,6 +328,15 @@ describe('Button', () => {
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should match class .ant-btn-disabled when button is disabled and href is not undefined', () => {
|
||||
const wrapper = render(
|
||||
<Button href="https://ant.design" disabled>
|
||||
click me
|
||||
</Button>,
|
||||
);
|
||||
expect(wrapper.container.querySelector('.ant-btn')).toHaveClass('ant-btn-disabled');
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/30953
|
||||
it('should handle fragment as children', () => {
|
||||
const wrapper = render(
|
||||
|
@ -247,6 +247,8 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
|
||||
|
||||
const iconType = innerLoading ? 'loading' : icon;
|
||||
|
||||
const linkButtonRestProps = omit(rest as AnchorButtonProps & { navigate: any }, ['navigate']);
|
||||
|
||||
const classes = classNames(
|
||||
prefixCls,
|
||||
{
|
||||
@ -260,6 +262,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
|
||||
[`${prefixCls}-block`]: block,
|
||||
[`${prefixCls}-dangerous`]: !!danger,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-disabled`]: linkButtonRestProps.href !== undefined && mergedDisabled,
|
||||
},
|
||||
className,
|
||||
);
|
||||
@ -276,7 +279,6 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
|
||||
? spaceChildren(children, isNeedInserted() && autoInsertSpace)
|
||||
: null;
|
||||
|
||||
const linkButtonRestProps = omit(rest as AnchorButtonProps & { navigate: any }, ['navigate']);
|
||||
if (linkButtonRestProps.href !== undefined) {
|
||||
return (
|
||||
<a {...linkButtonRestProps} className={classes} onClick={handleClick} ref={buttonRef}>
|
||||
|
@ -278,6 +278,10 @@ a.@{btn-prefix-cls} {
|
||||
padding-top: 0.01px !important;
|
||||
line-height: @btn-height-base - 2px;
|
||||
|
||||
&-disabled {
|
||||
.btn-href-disabled();
|
||||
}
|
||||
|
||||
&-lg {
|
||||
line-height: @btn-height-lg - 2px;
|
||||
}
|
||||
|
@ -385,6 +385,24 @@
|
||||
}
|
||||
.button-disabled(@disabled-color; transparent; transparent);
|
||||
}
|
||||
// link button disabled style
|
||||
.btn-href-disabled() {
|
||||
cursor: not-allowed;
|
||||
|
||||
> * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&,
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
.button-color(@btn-disable-color,transparent, transparent);
|
||||
|
||||
text-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
// text button style
|
||||
.btn-text() {
|
||||
.button-variant-other(@text-color, transparent, transparent);
|
||||
|
@ -1,15 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import RcDropdown from 'rc-dropdown';
|
||||
import classNames from 'classnames';
|
||||
import RightOutlined from '@ant-design/icons/RightOutlined';
|
||||
import DropdownButton from './dropdown-button';
|
||||
import classNames from 'classnames';
|
||||
import RcDropdown from 'rc-dropdown';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import warning from '../_util/warning';
|
||||
import { tuple } from '../_util/type';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import getPlacements from '../_util/placements';
|
||||
import OverrideContext from '../menu/OverrideContext';
|
||||
import type { OverrideContextProps } from '../menu/OverrideContext';
|
||||
import OverrideContext from '../menu/OverrideContext';
|
||||
import getPlacements from '../_util/placements';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import { tuple } from '../_util/type';
|
||||
import warning from '../_util/warning';
|
||||
import DropdownButton from './dropdown-button';
|
||||
|
||||
const Placements = tuple(
|
||||
'topLeft',
|
||||
@ -44,6 +44,7 @@ export type DropdownArrowOptions = {
|
||||
};
|
||||
|
||||
export interface DropdownProps {
|
||||
autoFocus?: boolean;
|
||||
arrow?: boolean | DropdownArrowOptions;
|
||||
trigger?: ('click' | 'hover' | 'contextMenu')[];
|
||||
overlay: React.ReactElement | OverlayFunc;
|
||||
|
@ -9,7 +9,7 @@ export interface FormListFieldData {
|
||||
name: number;
|
||||
key: number;
|
||||
/** @deprecated No need anymore Use key instead */
|
||||
fieldKey?:number
|
||||
fieldKey?: number;
|
||||
}
|
||||
|
||||
export interface FormListOperation {
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '../../../tests/utils';
|
||||
import Image from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
|
||||
const src = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png';
|
||||
|
||||
@ -76,4 +77,17 @@ describe('Image', () => {
|
||||
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('abc');
|
||||
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('def');
|
||||
});
|
||||
it('ConfigProvider getPopupContainer', () => {
|
||||
const { container: wrapper, baseElement } = render(
|
||||
<>
|
||||
<div className="container" />
|
||||
<ConfigProvider getPopupContainer={() => document.querySelector('.container')}>
|
||||
<Image src={src} />
|
||||
</ConfigProvider>
|
||||
</>,
|
||||
);
|
||||
fireEvent.click(wrapper.querySelector('.ant-image'));
|
||||
const containerElement = baseElement.querySelector('.container');
|
||||
expect(containerElement.children.length).not.toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -1,11 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import EyeOutlined from '@ant-design/icons/EyeOutlined';
|
||||
import RcImage, { ImageProps } from 'rc-image';
|
||||
import defaultLocale from '../locale/en_US';
|
||||
import PreviewGroup, { icons } from './PreviewGroup';
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import defaultLocale from '../locale/en_US';
|
||||
import { getTransitionName } from '../_util/motion';
|
||||
import PreviewGroup, { icons } from './PreviewGroup';
|
||||
|
||||
export interface CompositionImage<P> extends React.FC<P> {
|
||||
PreviewGroup: typeof PreviewGroup;
|
||||
@ -16,11 +16,15 @@ const Image: CompositionImage<ImageProps> = ({
|
||||
preview,
|
||||
...otherProps
|
||||
}) => {
|
||||
const { getPrefixCls } = useContext(ConfigContext);
|
||||
const {
|
||||
getPrefixCls,
|
||||
locale: contextLocale = defaultLocale,
|
||||
getPopupContainer: getContextPopupContainer,
|
||||
} = useContext(ConfigContext);
|
||||
|
||||
const prefixCls = getPrefixCls('image', customizePrefixCls);
|
||||
const rootPrefixCls = getPrefixCls();
|
||||
|
||||
const { locale: contextLocale = defaultLocale } = useContext(ConfigContext);
|
||||
const imageLocale = contextLocale.Image || defaultLocale.Image;
|
||||
|
||||
const mergedPreview = React.useMemo(() => {
|
||||
@ -28,7 +32,7 @@ const Image: CompositionImage<ImageProps> = ({
|
||||
return preview;
|
||||
}
|
||||
const _preview = typeof preview === 'object' ? preview : {};
|
||||
|
||||
const { getContainer, ...restPreviewProps } = _preview;
|
||||
return {
|
||||
mask: (
|
||||
<div className={`${prefixCls}-mask-info`}>
|
||||
@ -37,7 +41,8 @@ const Image: CompositionImage<ImageProps> = ({
|
||||
</div>
|
||||
),
|
||||
icons,
|
||||
..._preview,
|
||||
...restPreviewProps,
|
||||
getContainer: getContainer || getContextPopupContainer,
|
||||
transitionName: getTransitionName(rootPrefixCls, 'zoom', _preview.transitionName),
|
||||
maskTransitionName: getTransitionName(rootPrefixCls, 'fade', _preview.maskTransitionName),
|
||||
};
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
40
components/menu/__tests__/type.test.tsx
Normal file
40
components/menu/__tests__/type.test.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import * as React from 'react';
|
||||
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();
|
||||
});
|
||||
});
|
@ -23,7 +23,7 @@ interface SubMenuType extends Omit<RcSubMenuType, 'children'> {
|
||||
}
|
||||
|
||||
interface MenuItemGroupType extends Omit<RcMenuItemGroupType, 'children'> {
|
||||
children?: MenuItemType[];
|
||||
children?: ItemType[];
|
||||
key?: React.Key;
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ The legacy demo code for version `<4.20.0` could be found at [https://github.com
|
||||
#### SubMenuType
|
||||
|
||||
| Param | Description | Type | Default value | Version |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| children | Sub-menus or sub-menu items | [ItemType\[\]](#ItemType) | - | |
|
||||
| disabled | Whether sub-menu is disabled | boolean | false | |
|
||||
| icon | Icon of sub menu | ReactNode | - | |
|
||||
@ -127,7 +127,6 @@ The legacy demo code for version `<4.20.0` could be found at [https://github.com
|
||||
| label | Menu label | ReactNode | - | |
|
||||
| popupClassName | Sub-menu class name, not working when `mode="inline"` | string | - | |
|
||||
| popupOffset | Sub-menu offset, not working when `mode="inline"` | \[number, number] | - | |
|
||||
| title | Title of sub menu | ReactNode | - | |
|
||||
| theme | Color theme of the SubMenu (inherits from Menu by default) | | `light` \| `dark` | - | |
|
||||
| onTitleClick | Callback executed when the sub-menu title is clicked | function({ key, domEvent }) | - | |
|
||||
|
||||
|
@ -120,7 +120,7 @@ return <Menu items={items} />;
|
||||
#### SubMenuType
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| children | 子菜单的菜单项 | [ItemType\[\]](#ItemType) | - | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| icon | 菜单图标 | ReactNode | - | |
|
||||
|
@ -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 | | |
|
||||
|
@ -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 | | |
|
||||
|
@ -1,11 +1,10 @@
|
||||
import React from 'react';
|
||||
import MockDate from 'mockdate';
|
||||
import moment from 'moment';
|
||||
import { mount } from 'enzyme';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import Statistic from '..';
|
||||
import type Countdown from '../Countdown';
|
||||
import { formatTimeStr } from '../utils';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import { sleep, render, fireEvent } from '../../../tests/utils';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
|
||||
@ -23,63 +22,69 @@ describe('Statistic', () => {
|
||||
});
|
||||
|
||||
it('`-` is not a number', () => {
|
||||
const wrapper = mount(<Statistic value="-" />);
|
||||
expect(wrapper.find('.ant-statistic-content').text()).toEqual('-');
|
||||
const { container } = render(<Statistic value="-" />);
|
||||
expect(container.querySelector('.ant-statistic-content')!.textContent).toEqual('-');
|
||||
});
|
||||
|
||||
it('customize formatter', () => {
|
||||
const formatter = jest.fn(() => 93);
|
||||
const wrapper = mount(<Statistic value={1128} formatter={formatter} />);
|
||||
const { container } = render(<Statistic value={1128} formatter={formatter} />);
|
||||
expect(formatter).toHaveBeenCalledWith(1128);
|
||||
expect(wrapper.find('.ant-statistic-content-value').text()).toEqual('93');
|
||||
expect(container.querySelector('.ant-statistic-content-value')!.textContent).toEqual('93');
|
||||
});
|
||||
|
||||
it('groupSeparator', () => {
|
||||
const wrapper = mount(<Statistic value={1128} groupSeparator="__TEST__" />);
|
||||
expect(wrapper.find('.ant-statistic-content-value').text()).toEqual('1__TEST__128');
|
||||
const { container } = render(<Statistic value={1128} groupSeparator="__TEST__" />);
|
||||
expect(container.querySelector('.ant-statistic-content-value')!.textContent).toEqual(
|
||||
'1__TEST__128',
|
||||
);
|
||||
});
|
||||
|
||||
it('not a number', () => {
|
||||
const wrapper = mount(<Statistic value="bamboo" />);
|
||||
expect(wrapper.find('.ant-statistic-content-value').text()).toEqual('bamboo');
|
||||
const { container } = render(<Statistic value="bamboo" />);
|
||||
expect(container.querySelector('.ant-statistic-content-value')!.textContent).toEqual('bamboo');
|
||||
});
|
||||
|
||||
it('support negetive number', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Statistic title="Account Balance (CNY)" value={-112893.12345} precision={2} />,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('allow negetive precision', () => {
|
||||
[
|
||||
[-1, -1112893.1212, '-1,112,893'],
|
||||
[-2, -1112893.1212, '-1,112,893'],
|
||||
[-3, -1112893.1212, '-1,112,893'],
|
||||
[-1, -1112893, '-1,112,893'],
|
||||
[-1, 1112893, '1,112,893'],
|
||||
].forEach(([precision, value, expectValue]) => {
|
||||
const wrapper = mount(<Statistic precision={precision} value={value} />);
|
||||
expect(wrapper.find('.ant-statistic-content-value-int').text()).toEqual(expectValue);
|
||||
expect(wrapper.find('.ant-statistic-content-value-decimal').length).toBe(0);
|
||||
})
|
||||
[-3, -1112893.1212, '-1,112,893'],
|
||||
[-1, -1112893, '-1,112,893'],
|
||||
[-1, 1112893, '1,112,893'],
|
||||
].forEach(([precision, value, expectValue]: [number, number, string]) => {
|
||||
const { container } = render(<Statistic precision={precision} value={value} />);
|
||||
expect(container.querySelector('.ant-statistic-content-value-int')!.textContent).toEqual(
|
||||
expectValue,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-statistic-content-value-decimal').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('loading with skeleton', async () => {
|
||||
let loading = false;
|
||||
const wrapper = mount(<Statistic title="Active Users" value={112112} loading={loading} />);
|
||||
expect(wrapper.find('.ant-skeleton')).toHaveLength(0);
|
||||
expect(wrapper.find('.ant-statistic-content')).toHaveLength(1);
|
||||
const { container, rerender } = render(
|
||||
<Statistic title="Active Users" value={112112} loading={loading} />,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-skeleton')).toHaveLength(0);
|
||||
expect(container.querySelectorAll('.ant-statistic-content')).toHaveLength(1);
|
||||
|
||||
loading = true;
|
||||
wrapper.setProps({ loading });
|
||||
expect(wrapper.find('.ant-skeleton')).toHaveLength(1);
|
||||
expect(wrapper.find('.ant-statistic-content')).toHaveLength(0);
|
||||
rerender(<Statistic title="Active Users" value={112112} loading={loading} />);
|
||||
expect(container.querySelectorAll('.ant-skeleton')).toHaveLength(1);
|
||||
expect(container.querySelectorAll('.ant-statistic-content')).toHaveLength(0);
|
||||
});
|
||||
|
||||
describe('Countdown', () => {
|
||||
it('render correctly', () => {
|
||||
const now = moment().add(2, 'd').add(11, 'h').add(28, 'm').add(9, 's').add(3, 'ms');
|
||||
const now = moment().add(2, 'd').add(11, 'h').add(28, 'm').add(9, 's').add(3, 'ms').valueOf();
|
||||
|
||||
[
|
||||
['H:m:s', '59:28:9'],
|
||||
@ -87,25 +92,31 @@ describe('Statistic', () => {
|
||||
['HH:mm:ss:SSS', '59:28:09:003'],
|
||||
['DD-HH:mm:ss', '02-11:28:09'],
|
||||
].forEach(([format, value]) => {
|
||||
const wrapper = mount(<Statistic.Countdown format={format} value={now} />);
|
||||
expect(wrapper.find('.ant-statistic-content-value').text()).toEqual(value);
|
||||
const { container } = render(<Statistic.Countdown format={format} value={now} />);
|
||||
expect(container.querySelector('.ant-statistic-content-value')!.textContent).toEqual(value);
|
||||
});
|
||||
});
|
||||
|
||||
it('time going', async () => {
|
||||
const now = Date.now() + 1000;
|
||||
const onFinish = jest.fn();
|
||||
const wrapper = mount(<Statistic.Countdown value={now} onFinish={onFinish} />);
|
||||
wrapper.update();
|
||||
let instance: Countdown | null;
|
||||
const { unmount } = render(
|
||||
<Statistic.Countdown
|
||||
ref={n => {
|
||||
instance = n;
|
||||
}}
|
||||
value={now}
|
||||
onFinish={onFinish}
|
||||
/>,
|
||||
);
|
||||
|
||||
// setInterval should work
|
||||
const instance = wrapper.find('Countdown').instance();
|
||||
expect(instance.countdownId).not.toBe(undefined);
|
||||
expect(instance!.countdownId).not.toBe(undefined);
|
||||
|
||||
await sleep(10);
|
||||
|
||||
wrapper.unmount();
|
||||
expect(instance.countdownId).toBe(undefined);
|
||||
unmount();
|
||||
expect(onFinish).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -115,21 +126,21 @@ describe('Statistic', () => {
|
||||
const { container } = render(
|
||||
<Statistic onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />,
|
||||
);
|
||||
fireEvent.mouseEnter(container.firstChild);
|
||||
fireEvent.mouseEnter(container.firstChild!);
|
||||
expect(onMouseEnter).toHaveBeenCalled();
|
||||
fireEvent.mouseLeave(container.firstChild);
|
||||
fireEvent.mouseLeave(container.firstChild!);
|
||||
expect(onMouseLeave).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('responses hover events for Countdown', () => {
|
||||
const onMouseEnter = jest.fn();
|
||||
const onMouseLeave = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Statistic.Countdown onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />,
|
||||
);
|
||||
wrapper.simulate('mouseenter');
|
||||
fireEvent.mouseEnter(container.firstChild!);
|
||||
expect(onMouseEnter).toHaveBeenCalled();
|
||||
wrapper.simulate('mouseleave');
|
||||
fireEvent.mouseLeave(container.firstChild!);
|
||||
expect(onMouseLeave).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -138,11 +149,11 @@ describe('Statistic', () => {
|
||||
const deadline = Date.now() + 10 * 1000;
|
||||
let remainingTime;
|
||||
|
||||
const onChange = value => {
|
||||
const onChange = (value: number) => {
|
||||
remainingTime = value;
|
||||
};
|
||||
const wrapper = mount(<Statistic.Countdown value={deadline} onChange={onChange} />);
|
||||
wrapper.update();
|
||||
render(<Statistic.Countdown value={deadline} onChange={onChange} />);
|
||||
// container.update();
|
||||
await sleep(100);
|
||||
expect(remainingTime).toBeGreaterThan(0);
|
||||
});
|
||||
@ -151,20 +162,26 @@ describe('Statistic', () => {
|
||||
describe('time finished', () => {
|
||||
it('not call if time already passed', () => {
|
||||
const now = Date.now() - 1000;
|
||||
|
||||
let instance: Countdown | null;
|
||||
const onFinish = jest.fn();
|
||||
const wrapper = mount(<Statistic.Countdown value={now} onFinish={onFinish} />);
|
||||
wrapper.update();
|
||||
render(
|
||||
<Statistic.Countdown
|
||||
ref={n => {
|
||||
instance = n;
|
||||
}}
|
||||
value={now}
|
||||
onFinish={onFinish}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('Countdown').instance().countdownId).toBe(undefined);
|
||||
expect(instance!.countdownId).toBe(undefined);
|
||||
expect(onFinish).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('called if finished', async () => {
|
||||
const now = Date.now() + 10;
|
||||
const onFinish = jest.fn();
|
||||
const wrapper = mount(<Statistic.Countdown value={now} onFinish={onFinish} />);
|
||||
wrapper.update();
|
||||
render(<Statistic.Countdown value={now} onFinish={onFinish} />);
|
||||
MockDate.set(moment('2019-11-28 00:00:00').valueOf());
|
||||
await sleep(100);
|
||||
expect(onFinish).toHaveBeenCalled();
|
@ -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;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -976,7 +976,60 @@ exports[`Table.rowSelection fix selection column on the left when any other colu
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table.rowSelection render with default selection correctly 1`] = `<div />`;
|
||||
exports[`Table.rowSelection render with default selection correctly 1`] = `
|
||||
<div
|
||||
class="ant-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
|
||||
style="opacity: 0;"
|
||||
>
|
||||
<ul
|
||||
class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-vertical ant-dropdown-menu-light"
|
||||
data-menu-list="true"
|
||||
role="menu"
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
|
||||
data-menu-id="rc-menu-uuid-test-all"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
Select all data
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
|
||||
data-menu-id="rc-menu-uuid-test-invert"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
Invert current page
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
|
||||
data-menu-id="rc-menu-uuid-test-none"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
Clear all data
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
style="display: none;"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table.rowSelection should support getPopupContainer 1`] = `
|
||||
<div
|
||||
|
@ -1,9 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type RcTree from 'rc-tree';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { conductExpandParent } from 'rc-tree/lib/util';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import type { EventDataNode, DataNode, Key } from 'rc-tree/lib/interface';
|
||||
import { convertDataToEntities, convertTreeToData } from 'rc-tree/lib/utils/treeUtil';
|
||||
import FileOutlined from '@ant-design/icons/FileOutlined';
|
||||
@ -87,21 +85,6 @@ const DirectoryTree: React.ForwardRefRenderFunction<RcTree, DirectoryTreeProps>
|
||||
}
|
||||
}, [props.expandedKeys]);
|
||||
|
||||
const expandFolderNode = (event: React.MouseEvent<HTMLElement>, node: any) => {
|
||||
const { isLeaf } = node;
|
||||
|
||||
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call internal rc-tree expand function
|
||||
// https://github.com/ant-design/ant-design/issues/12567
|
||||
treeRef.current!.onNodeExpand(event as any, node);
|
||||
};
|
||||
|
||||
const onDebounceExpand = debounce(expandFolderNode, 200, {
|
||||
leading: true,
|
||||
});
|
||||
const onExpand = (
|
||||
keys: Key[],
|
||||
info: {
|
||||
@ -117,28 +100,6 @@ const DirectoryTree: React.ForwardRefRenderFunction<RcTree, DirectoryTreeProps>
|
||||
return props.onExpand?.(keys, info);
|
||||
};
|
||||
|
||||
const onClick = (event: React.MouseEvent<HTMLElement>, node: EventDataNode<any>) => {
|
||||
const { expandAction } = props;
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'click') {
|
||||
onDebounceExpand(event, node);
|
||||
}
|
||||
|
||||
props.onClick?.(event, node);
|
||||
};
|
||||
|
||||
const onDoubleClick = (event: React.MouseEvent<HTMLElement>, node: EventDataNode<any>) => {
|
||||
const { expandAction } = props;
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'doubleClick') {
|
||||
onDebounceExpand(event, node);
|
||||
}
|
||||
|
||||
props.onDoubleClick?.(event, node);
|
||||
};
|
||||
|
||||
const onSelect = (
|
||||
keys: Key[],
|
||||
event: {
|
||||
@ -219,14 +180,12 @@ const DirectoryTree: React.ForwardRefRenderFunction<RcTree, DirectoryTreeProps>
|
||||
icon={getIcon}
|
||||
ref={treeRef}
|
||||
blockNode
|
||||
{...omit(otherProps, ['expandAction'])}
|
||||
{...otherProps}
|
||||
prefixCls={prefixCls}
|
||||
className={connectClassName}
|
||||
expandedKeys={expandedKeys}
|
||||
selectedKeys={selectedKeys}
|
||||
onSelect={onSelect}
|
||||
onClick={onClick}
|
||||
onDoubleClick={onDoubleClick}
|
||||
onExpand={onExpand}
|
||||
/>
|
||||
);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { mount, render } from 'enzyme';
|
||||
import debounce from 'lodash/debounce';
|
||||
import Tree from '../index';
|
||||
import React from 'react';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { act, fireEvent, render } from '../../../tests/utils';
|
||||
import Tree from '../index';
|
||||
|
||||
const { DirectoryTree, TreeNode } = Tree;
|
||||
|
||||
@ -44,23 +44,45 @@ describe('Directory Tree', () => {
|
||||
|
||||
describe('expand', () => {
|
||||
it('click', () => {
|
||||
const wrapper = mount(createTree());
|
||||
const onExpand = jest.fn();
|
||||
const { container } = render(createTree({ onExpand }));
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
jest.runAllTimers();
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
fireEvent.click(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(onExpand).toHaveBeenCalledWith(['0-0'], expect.anything());
|
||||
onExpand.mockReset();
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.click(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(onExpand).toHaveBeenCalledWith([], expect.anything());
|
||||
});
|
||||
|
||||
it('double click', () => {
|
||||
const wrapper = mount(createTree({ expandAction: 'doubleClick' }));
|
||||
const onExpand = jest.fn();
|
||||
const { container } = render(createTree({ expandAction: 'doubleClick', onExpand }));
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('doubleClick');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
jest.runAllTimers();
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('doubleClick');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
fireEvent.doubleClick(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(onExpand).toHaveBeenCalledWith(['0-0'], expect.anything());
|
||||
onExpand.mockReset();
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.doubleClick(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(onExpand).toHaveBeenCalledWith([], expect.anything());
|
||||
});
|
||||
|
||||
describe('with state control', () => {
|
||||
@ -86,21 +108,26 @@ describe('Directory Tree', () => {
|
||||
}
|
||||
}
|
||||
|
||||
['click', 'doubleClick'].forEach(action => {
|
||||
it(action, () => {
|
||||
const wrapper = mount(<StateDirTree expandAction={action} />);
|
||||
it('click', () => {
|
||||
const { container, asFragment } = render(<StateDirTree expandAction="click" />);
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate(action);
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
fireEvent.click(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
jest.runAllTimers();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
it('doubleClick', () => {
|
||||
const { container, asFragment } = render(<StateDirTree expandAction="doubleClick" />);
|
||||
|
||||
fireEvent.doubleClick(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
jest.runAllTimers();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('defaultExpandAll', () => {
|
||||
const wrapper = render(createTree({ defaultExpandAll: true }));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const { asFragment } = render(createTree({ defaultExpandAll: true }));
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('DirectoryTree should expend all when use treeData and defaultExpandAll is true', () => {
|
||||
@ -123,90 +150,78 @@ describe('Directory Tree', () => {
|
||||
],
|
||||
},
|
||||
];
|
||||
const wrapper = render(createTree({ defaultExpandAll: true, treeData }));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const { asFragment } = render(createTree({ defaultExpandAll: true, treeData }));
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('defaultExpandParent', () => {
|
||||
const wrapper = render(createTree({ defaultExpandParent: true }));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const { asFragment } = render(createTree({ defaultExpandParent: true }));
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('expandedKeys update', () => {
|
||||
const wrapper = mount(createTree());
|
||||
wrapper.setProps({ expandedKeys: ['0-1'] });
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { rerender, asFragment } = render(createTree());
|
||||
rerender(createTree({ expandedKeys: ['0-1'] }));
|
||||
jest.runAllTimers();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('selectedKeys update', () => {
|
||||
const wrapper = mount(createTree({ defaultExpandAll: true }));
|
||||
wrapper.setProps({ selectedKeys: ['0-1-0'] });
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
const { rerender, asFragment } = render(createTree({ defaultExpandAll: true }));
|
||||
rerender(createTree({ selectedKeys: ['0-1-0'] }));
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('group select', () => {
|
||||
let nativeEventProto = null;
|
||||
const onSelect = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container, asFragment } = render(
|
||||
createTree({
|
||||
defaultExpandAll: true,
|
||||
expandAction: 'doubleClick',
|
||||
multiple: true,
|
||||
onClick: e => {
|
||||
nativeEventProto = Object.getPrototypeOf(e.nativeEvent);
|
||||
},
|
||||
onSelect,
|
||||
}),
|
||||
);
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
fireEvent.click(container.querySelectorAll('.ant-tree-node-content-wrapper')[0]);
|
||||
expect(onSelect.mock.calls[0][1].selected).toBeTruthy();
|
||||
expect(onSelect.mock.calls[0][1].selectedNodes.length).toBe(1);
|
||||
|
||||
// Click twice should keep selected
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
fireEvent.click(container.querySelectorAll('.ant-tree-node-content-wrapper')[0]);
|
||||
expect(onSelect.mock.calls[1][1].selected).toBeTruthy();
|
||||
expect(onSelect.mock.calls[0][0]).toEqual(onSelect.mock.calls[1][0]);
|
||||
expect(onSelect.mock.calls[1][1].selectedNodes.length).toBe(1);
|
||||
|
||||
// React not simulate full of NativeEvent. Hook it.
|
||||
// Ref: https://github.com/facebook/react/blob/master/packages/react-dom/src/test-utils/ReactTestUtils.js#L360
|
||||
nativeEventProto.ctrlKey = true;
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(1).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
fireEvent.click(container.querySelectorAll('.ant-tree-node-content-wrapper')[1], {
|
||||
ctrlKey: true,
|
||||
});
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(onSelect.mock.calls[2][0].length).toBe(2);
|
||||
expect(onSelect.mock.calls[2][1].selected).toBeTruthy();
|
||||
expect(onSelect.mock.calls[2][1].selectedNodes.length).toBe(2);
|
||||
|
||||
delete nativeEventProto.ctrlKey;
|
||||
nativeEventProto.shiftKey = true;
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(4).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
fireEvent.click(container.querySelectorAll('.ant-tree-node-content-wrapper')[4], {
|
||||
shiftKey: true,
|
||||
});
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(onSelect.mock.calls[3][0].length).toBe(5);
|
||||
expect(onSelect.mock.calls[3][1].selected).toBeTruthy();
|
||||
expect(onSelect.mock.calls[3][1].selectedNodes.length).toBe(5);
|
||||
|
||||
delete nativeEventProto.shiftKey;
|
||||
});
|
||||
|
||||
it('onDoubleClick', () => {
|
||||
const onDoubleClick = jest.fn();
|
||||
const wrapper = mount(createTree({ onDoubleClick }));
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('doubleclick');
|
||||
const { container } = render(createTree({ onDoubleClick }));
|
||||
fireEvent.doubleClick(container.querySelector('.ant-tree-node-content-wrapper'));
|
||||
expect(onDoubleClick).toBeCalled();
|
||||
});
|
||||
|
||||
it('should not expand tree now when pressing ctrl', () => {
|
||||
const onExpand = jest.fn();
|
||||
const onSelect = jest.fn();
|
||||
const wrapper = mount(createTree({ onExpand, onSelect }));
|
||||
wrapper
|
||||
.find(TreeNode)
|
||||
.find('.ant-tree-node-content-wrapper')
|
||||
.at(0)
|
||||
.simulate('click', { ctrlKey: true });
|
||||
const { container } = render(createTree({ onExpand, onSelect }));
|
||||
fireEvent.click(container.querySelector('.ant-tree-node-content-wrapper'), { ctrlKey: true });
|
||||
expect(onExpand).not.toHaveBeenCalled();
|
||||
expect(onSelect).toHaveBeenCalledWith(
|
||||
['0-0'],
|
||||
@ -217,7 +232,7 @@ describe('Directory Tree', () => {
|
||||
it('should not expand tree now when click leaf node', () => {
|
||||
const onExpand = jest.fn();
|
||||
const onSelect = jest.fn();
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
createTree({
|
||||
onExpand,
|
||||
onSelect,
|
||||
@ -243,7 +258,8 @@ describe('Directory Tree', () => {
|
||||
],
|
||||
}),
|
||||
);
|
||||
wrapper.find(TreeNode).last().find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
const nodeList = container.querySelectorAll('.ant-tree-node-content-wrapper');
|
||||
fireEvent.click(nodeList[nodeList.length - 1]);
|
||||
expect(onExpand).not.toHaveBeenCalled();
|
||||
expect(onSelect).toHaveBeenCalledWith(
|
||||
['0-0-2'],
|
||||
@ -253,7 +269,7 @@ describe('Directory Tree', () => {
|
||||
|
||||
it('ref support', () => {
|
||||
const treeRef = React.createRef();
|
||||
mount(createTree({ ref: treeRef }));
|
||||
render(createTree({ ref: treeRef }));
|
||||
|
||||
expect('scrollTo' in treeRef.current).toBeTruthy();
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { mount } from 'enzyme';
|
||||
import { render } from '../../../tests/utils';
|
||||
import dropIndicatorRender, { offset } from '../utils/dropIndicator';
|
||||
|
||||
describe('dropIndicatorRender', () => {
|
||||
@ -10,8 +10,8 @@ describe('dropIndicatorRender', () => {
|
||||
prefixCls: 'ant',
|
||||
direction: 'ltr',
|
||||
});
|
||||
const wrapper = mount(indicator);
|
||||
expect(wrapper.find('div').props().style!.bottom).toEqual(-3);
|
||||
const { container } = render(indicator);
|
||||
expect(container.querySelector('div')?.style.bottom).toEqual('-3px');
|
||||
});
|
||||
it('work with dropPosition inner (-0)', () => {
|
||||
const indicator = dropIndicatorRender({
|
||||
@ -21,9 +21,9 @@ describe('dropIndicatorRender', () => {
|
||||
prefixCls: 'ant',
|
||||
direction: 'ltr',
|
||||
});
|
||||
const wrapper = mount(indicator);
|
||||
expect(wrapper.find('div').props().style!.bottom).toEqual(-3);
|
||||
expect(wrapper.find('div').props().style!.left).toEqual(24 + offset);
|
||||
const { container } = render(indicator);
|
||||
expect(container.querySelector('div')?.style.bottom).toEqual('-3px');
|
||||
expect(container.querySelector('div')?.style.left).toEqual(`${24 + offset}px`);
|
||||
});
|
||||
it('work with dropPosition after (-1)', () => {
|
||||
const indicator = dropIndicatorRender({
|
||||
@ -33,8 +33,8 @@ describe('dropIndicatorRender', () => {
|
||||
prefixCls: 'ant',
|
||||
direction: 'ltr',
|
||||
});
|
||||
const wrapper = mount(indicator);
|
||||
expect(wrapper.find('div').props().style!.top).toEqual(-3);
|
||||
const { container } = render(indicator);
|
||||
expect(container.querySelector('div')?.style.top).toEqual('-3px');
|
||||
});
|
||||
it('work with drop level', () => {
|
||||
const indicator = dropIndicatorRender({
|
||||
@ -44,8 +44,8 @@ describe('dropIndicatorRender', () => {
|
||||
prefixCls: 'ant',
|
||||
direction: 'ltr',
|
||||
});
|
||||
const wrapper = mount(indicator);
|
||||
expect(wrapper.find('div').props().style!.left).toEqual(-2 * 24 + offset);
|
||||
const { container } = render(indicator);
|
||||
expect(container.querySelector('div')?.style.left).toEqual(`${-2 * 24 + offset}px`);
|
||||
});
|
||||
it('work with drop level (rtl)', () => {
|
||||
const indicator = dropIndicatorRender({
|
||||
@ -55,7 +55,7 @@ describe('dropIndicatorRender', () => {
|
||||
prefixCls: 'ant',
|
||||
direction: 'rtl',
|
||||
});
|
||||
const wrapper = mount(indicator);
|
||||
expect(wrapper.find('div').props().style!.right).toEqual(-2 * 24 + offset);
|
||||
const { container } = render(indicator);
|
||||
expect(container.querySelector('div')?.style.right).toEqual(`${-2 * 24 + offset}px`);
|
||||
});
|
||||
});
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { render } from '../../../tests/utils';
|
||||
import Tree from '../index';
|
||||
|
||||
const { TreeNode } = Tree;
|
||||
|
||||
describe('Tree', () => {
|
||||
it('icon and switcherIcon of Tree with showLine should render correctly', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Tree showLine showIcon>
|
||||
<TreeNode icon="icon" switcherIcon="switcherIcon" key="0-0">
|
||||
<TreeNode icon="icon" switcherIcon="switcherIcon" key="0-0-0" />
|
||||
@ -28,11 +28,11 @@ describe('Tree', () => {
|
||||
</TreeNode>
|
||||
</Tree>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('switcherIcon in Tree should not render at leaf nodes', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Tree switcherIcon={<i className="switcherIcon" />} defaultExpandAll>
|
||||
<TreeNode icon="icon">
|
||||
<TreeNode id="node1" title="node1" icon="icon" key="0-0-2" />
|
||||
@ -40,11 +40,11 @@ describe('Tree', () => {
|
||||
</TreeNode>
|
||||
</Tree>,
|
||||
);
|
||||
expect(wrapper.find('.switcherIcon').length).toBe(1);
|
||||
expect(container.querySelectorAll('.switcherIcon').length).toBe(1);
|
||||
});
|
||||
|
||||
it('switcherIcon in Tree could be string', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Tree switcherIcon="switcherIcon" defaultExpandAll>
|
||||
<TreeNode icon="icon">
|
||||
<TreeNode id="node1" title="node1" icon="icon" key="0-0-2" />
|
||||
@ -52,7 +52,7 @@ describe('Tree', () => {
|
||||
</TreeNode>
|
||||
</Tree>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('switcherIcon should be loading icon when loadData', () => {
|
||||
@ -62,7 +62,7 @@ describe('Tree', () => {
|
||||
resolve();
|
||||
}, 1000);
|
||||
});
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Tree switcherIcon="switcherIcon" defaultExpandAll loadData={onLoadData}>
|
||||
<TreeNode icon="icon">
|
||||
<TreeNode id="node1" title="node1" icon="icon" key="0-0-2" />
|
||||
@ -70,11 +70,11 @@ describe('Tree', () => {
|
||||
</TreeNode>
|
||||
</Tree>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('switcherIcon in Tree could be render prop function', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Tree
|
||||
switcherIcon={expanded =>
|
||||
expanded ? <span className="open" /> : <span className="close" />
|
||||
@ -87,12 +87,12 @@ describe('Tree', () => {
|
||||
</TreeNode>
|
||||
</Tree>,
|
||||
);
|
||||
expect(wrapper.find('.open').length).toBe(1);
|
||||
expect(container.querySelectorAll('.open').length).toBe(1);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/23261
|
||||
it('showLine is object type should render correctly', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Tree showLine={{ showLeafIcon: false }} defaultExpandedKeys={['0-0-0']}>
|
||||
<TreeNode title="parent 1" key="0-0">
|
||||
<TreeNode title="parent 1-0" key="0-0-0">
|
||||
@ -110,7 +110,7 @@ describe('Tree', () => {
|
||||
</TreeNode>
|
||||
</Tree>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('draggable', () => {
|
||||
@ -122,26 +122,26 @@ describe('Tree', () => {
|
||||
];
|
||||
|
||||
it('hide icon', () => {
|
||||
const wrapper = mount(<Tree treeData={dragTreeData} draggable={{ icon: false }} />);
|
||||
expect(wrapper.exists('.anticon-holder')).toBeFalsy();
|
||||
const { container } = render(<Tree treeData={dragTreeData} draggable={{ icon: false }} />);
|
||||
expect(container.querySelector('.anticon-holder')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('customize icon', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Tree treeData={dragTreeData} draggable={{ icon: <span className="little" /> }} />,
|
||||
);
|
||||
expect(wrapper.exists('.little')).toBeTruthy();
|
||||
expect(container.querySelector('.little')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('nodeDraggable', () => {
|
||||
const nodeDraggable = jest.fn(() => false);
|
||||
mount(<Tree treeData={dragTreeData} draggable={{ nodeDraggable }} />);
|
||||
render(<Tree treeData={dragTreeData} draggable={{ nodeDraggable }} />);
|
||||
expect(nodeDraggable).toHaveBeenCalledWith(dragTreeData[0]);
|
||||
});
|
||||
|
||||
it('nodeDraggable func', () => {
|
||||
const nodeDraggable = jest.fn(() => false);
|
||||
mount(<Tree treeData={dragTreeData} draggable={nodeDraggable} />);
|
||||
render(<Tree treeData={dragTreeData} draggable={nodeDraggable} />);
|
||||
expect(nodeDraggable).toHaveBeenCalledWith(dragTreeData[0]);
|
||||
});
|
||||
});
|
||||
|
@ -1,11 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import type { BasicDataNode } from 'rc-tree';
|
||||
import * as React from 'react';
|
||||
import { render } from '../../../tests/utils';
|
||||
import Tree from '../index';
|
||||
|
||||
describe('Tree.TypeScript', () => {
|
||||
it('without generic', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Tree
|
||||
treeData={[
|
||||
{
|
||||
@ -22,7 +22,7 @@ describe('Tree.TypeScript', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toBeTruthy();
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
|
||||
it('support generic', () => {
|
||||
@ -31,7 +31,7 @@ describe('Tree.TypeScript', () => {
|
||||
list?: MyDataNode[];
|
||||
}
|
||||
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Tree<MyDataNode>
|
||||
treeData={[
|
||||
{
|
||||
@ -46,6 +46,6 @@ describe('Tree.TypeScript', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toBeTruthy();
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -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 { 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);
|
||||
|
@ -1,9 +1,7 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
|
||||
import Base from '../Base';
|
||||
import Typography from '../Typography';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, triggerResize, waitFor } from '../../../tests/utils';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import * as styleChecker from '../../_util/styleChecker';
|
||||
|
||||
@ -53,47 +51,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 +115,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 +203,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 +266,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 +332,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();
|
||||
});
|
||||
});
|
||||
|
@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import Paragraph from '../Paragraph';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
|
||||
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 +23,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);
|
||||
|
@ -1,9 +1,7 @@
|
||||
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 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 Link from '../Link';
|
||||
@ -13,7 +11,7 @@ import Base from '../Base';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import Typography from '../Typography';
|
||||
import { sleep, render } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, waitFor } 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);
|
||||
});
|
||||
});
|
||||
|
@ -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,
|
||||
|
21
package.json
21
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "antd",
|
||||
"version": "4.21.0",
|
||||
"version": "4.21.1",
|
||||
"description": "An enterprise-class UI design language and React components implementation",
|
||||
"title": "Ant Design",
|
||||
"keywords": [
|
||||
@ -115,7 +115,7 @@
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^6.0.0",
|
||||
"@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",
|
||||
@ -126,11 +126,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",
|
||||
@ -151,7 +151,7 @@
|
||||
"rc-tabs": "~11.16.0",
|
||||
"rc-textarea": "~0.3.0",
|
||||
"rc-tooltip": "~5.1.1",
|
||||
"rc-tree": "~5.6.4",
|
||||
"rc-tree": "~5.6.5",
|
||||
"rc-tree-select": "~5.4.0",
|
||||
"rc-trigger": "^5.2.10",
|
||||
"rc-upload": "~4.3.0",
|
||||
@ -159,9 +159,9 @@
|
||||
"scroll-into-view-if-needed": "^2.2.25"
|
||||
},
|
||||
"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",
|
||||
@ -191,7 +191,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",
|
||||
@ -272,7 +272,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",
|
||||
@ -283,10 +282,10 @@
|
||||
"scrollama": "^3.0.0",
|
||||
"semver": "^7.3.5",
|
||||
"simple-git": "^3.0.0",
|
||||
"stylelint": "14.8.3",
|
||||
"stylelint": "^14.9.0",
|
||||
"stylelint-config-prettier": "^9.0.2",
|
||||
"stylelint-config-rational-order": "^0.1.2",
|
||||
"stylelint-config-standard": "^25.0.0",
|
||||
"stylelint-config-standard": "^26.0.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "^2.1.0",
|
||||
"stylelint-order": "^5.0.0",
|
||||
"theme-switcher": "^1.0.2",
|
||||
|
@ -10,7 +10,9 @@
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"jsx": "preserve",
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment",
|
||||
"noUnusedParameters": true,
|
||||
"noUnusedLocals": true,
|
||||
"noImplicitAny": true,
|
||||
|
Loading…
Reference in New Issue
Block a user