mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-24 02:59:58 +08:00
Merge branch 'master' into next-merge-master
This commit is contained in:
commit
9dd5e9b5e9
@ -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,28 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.21.2
|
||||
|
||||
`2022-06-15`
|
||||
|
||||
- 🐞 Fix incorrect Form status with `noStyle`. [#36054](https://github.com/ant-design/ant-design/pull/36054)
|
||||
|
||||
## 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,28 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.21.2
|
||||
|
||||
`2022-06-15`
|
||||
|
||||
- 🐞 修复 Form 有 `noStyle` 属性时校验状态错误的问题。[#36054](https://github.com/ant-design/ant-design/pull/36054)
|
||||
|
||||
## 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`
|
||||
|
@ -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,11 +1,10 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import Badge from '../index';
|
||||
import Tooltip from '../../tooltip';
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import Tooltip from '../../tooltip';
|
||||
import Badge from '../index';
|
||||
|
||||
describe('Badge', () => {
|
||||
mountTest(Badge);
|
||||
@ -27,34 +26,37 @@ describe('Badge', () => {
|
||||
});
|
||||
|
||||
it('badge dot not scaling count > 9', () => {
|
||||
const badge = mount(<Badge count={10} dot />);
|
||||
expect(badge.find('.ant-card-multiple-words').length).toBe(0);
|
||||
const { container } = render(<Badge count={10} dot />);
|
||||
expect(container.querySelectorAll('.ant-card-multiple-words').length).toBe(0);
|
||||
});
|
||||
|
||||
it('badge should support float number', () => {
|
||||
let wrapper = mount(<Badge count={3.5} />);
|
||||
expect(wrapper.find('.ant-badge-multiple-words').first().text()).toEqual('3.5');
|
||||
const { container } = render(<Badge count={3.5} />);
|
||||
expect(container.querySelectorAll('.ant-badge-multiple-words')[0].textContent).toEqual('3.5');
|
||||
|
||||
wrapper = mount(<Badge count="3.5" />);
|
||||
expect(wrapper.find('.ant-badge-multiple-words').first().text()).toEqual('3.5');
|
||||
expect(() => wrapper.unmount()).not.toThrow();
|
||||
const { container: anotherContainer, unmount } = render(<Badge count="3.5" />);
|
||||
expect(anotherContainer.querySelectorAll('.ant-badge-multiple-words')[0].textContent).toEqual(
|
||||
'3.5',
|
||||
);
|
||||
|
||||
expect(() => unmount()).not.toThrow();
|
||||
});
|
||||
|
||||
it('badge dot not showing count == 0', () => {
|
||||
const badge = mount(<Badge count={0} dot />);
|
||||
expect(badge.find('.ant-badge-dot').length).toBe(0);
|
||||
const { container } = render(<Badge count={0} dot />);
|
||||
expect(container.querySelectorAll('.ant-badge-dot').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should have an overriden title attribute', () => {
|
||||
const badge = mount(<Badge count={10} title="Custom title" />);
|
||||
expect(
|
||||
badge.find('.ant-scroll-number').getDOMNode().attributes.getNamedItem('title').value,
|
||||
).toEqual('Custom title');
|
||||
const { container } = render(<Badge count={10} title="Custom title" />);
|
||||
expect((container.querySelector('.ant-scroll-number')! as HTMLElement).title).toEqual(
|
||||
'Custom title',
|
||||
);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/10626
|
||||
it('should be composable with Tooltip', () => {
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<typeof Tooltip>();
|
||||
const { container } = render(
|
||||
<Tooltip title="Fix the error" ref={ref}>
|
||||
<Badge status="error" />
|
||||
@ -62,22 +64,21 @@ describe('Badge', () => {
|
||||
);
|
||||
|
||||
act(() => {
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-badge'));
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-badge')!);
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(ref.current.props.visible).toBeTruthy();
|
||||
expect((container.firstChild! as HTMLElement).classList).toContain('ant-tooltip-open');
|
||||
});
|
||||
|
||||
it('should render when count is changed', () => {
|
||||
const wrapper = mount(<Badge count={9} />);
|
||||
const { asFragment, rerender } = render(<Badge count={9} />);
|
||||
|
||||
function updateMatch(count) {
|
||||
wrapper.setProps({ count });
|
||||
function updateMatch(count: number) {
|
||||
rerender(<Badge count={count} />);
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
}
|
||||
|
||||
@ -90,48 +91,52 @@ describe('Badge', () => {
|
||||
});
|
||||
|
||||
it('should be compatible with borderColor style', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Badge
|
||||
count={4}
|
||||
style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/13694
|
||||
it('should support offset when count is a ReactNode', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<Badge count={<span className="custom" style={{ color: '#f5222d' }} />} offset={[10, 20]}>
|
||||
<a href="#" className="head-example">
|
||||
head
|
||||
</a>
|
||||
</Badge>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/15349
|
||||
it('should color style works on Badge', () => {
|
||||
const wrapper = mount(<Badge style={{ color: 'red' }} status="success" text="Success" />);
|
||||
expect(wrapper.find('.ant-badge-status-text').props().style.color).toBe('red');
|
||||
const { container } = render(
|
||||
<Badge style={{ color: 'red' }} status="success" text="Success" />,
|
||||
);
|
||||
expect((container.querySelector('.ant-badge-status-text')! as HTMLElement).style.color).toEqual(
|
||||
'red',
|
||||
);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/15799
|
||||
it('render correct with negative number', () => {
|
||||
const wrapper = mount(
|
||||
const { asFragment } = render(
|
||||
<div>
|
||||
<Badge count="-10" />
|
||||
<Badge count={-10} />
|
||||
</div>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/21331
|
||||
// https://github.com/ant-design/ant-design/issues/31590
|
||||
it('render Badge status/color when contains children', () => {
|
||||
const wrapper = mount(
|
||||
const { container, asFragment } = render(
|
||||
<div>
|
||||
<Badge count={5} status="success">
|
||||
<a />
|
||||
@ -144,20 +149,20 @@ describe('Badge', () => {
|
||||
</Badge>
|
||||
</div>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapper.find(Badge).at(0).find('.ant-scroll-number-only-unit').text()).toBe('5');
|
||||
expect(wrapper.find(Badge).at(1).find('.ant-scroll-number-only-unit').text()).toBe('5');
|
||||
expect(wrapper.find(Badge).at(2).find('.ant-scroll-number-only-unit').text()).toBe('5');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(container.querySelectorAll('.ant-scroll-number-only-unit')[0].textContent).toBe('5');
|
||||
expect(container.querySelectorAll('.ant-scroll-number-only-unit')[1].textContent).toBe('5');
|
||||
expect(container.querySelectorAll('.ant-scroll-number-only-unit')[2].textContent).toBe('5');
|
||||
});
|
||||
|
||||
it('Badge should work when status/color is empty string', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<>
|
||||
<Badge color="" text="text" />
|
||||
<Badge status="" text="text" />
|
||||
<Badge status={'' as any} text="text" />
|
||||
</>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.ant-badge')).toHaveLength(2);
|
||||
expect(container.querySelectorAll('.ant-badge')).toHaveLength(2);
|
||||
});
|
||||
});
|
@ -1,8 +1,8 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Badge from '../index';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import Badge from '../index';
|
||||
|
||||
describe('Ribbon', () => {
|
||||
mountTest(Badge.Ribbon);
|
||||
@ -10,65 +10,73 @@ describe('Ribbon', () => {
|
||||
|
||||
describe('placement', () => {
|
||||
it('works with `start` & `end` placement', () => {
|
||||
const wrapperStart = mount(
|
||||
const { container: wrapperStart } = render(
|
||||
<Badge.Ribbon placement="start">
|
||||
<div />
|
||||
</Badge.Ribbon>,
|
||||
);
|
||||
expect(wrapperStart.find('.ant-ribbon-placement-start').length).toEqual(1);
|
||||
expect(wrapperStart.querySelectorAll('.ant-ribbon-placement-start').length).toEqual(1);
|
||||
|
||||
const wrapperEnd = mount(
|
||||
const { container: wrapperEnd } = render(
|
||||
<Badge.Ribbon placement="end">
|
||||
<div />
|
||||
</Badge.Ribbon>,
|
||||
);
|
||||
expect(wrapperEnd.find('.ant-ribbon-placement-end').length).toEqual(1);
|
||||
expect(wrapperEnd.querySelectorAll('.ant-ribbon-placement-end').length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('color', () => {
|
||||
it('works with preset color', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Badge.Ribbon color="green">
|
||||
<div />
|
||||
</Badge.Ribbon>,
|
||||
);
|
||||
expect(wrapper.find('.ant-ribbon-color-green').length).toEqual(1);
|
||||
expect(container.querySelectorAll('.ant-ribbon-color-green').length).toEqual(1);
|
||||
});
|
||||
it('works with custom color', () => {
|
||||
const wrapperLeft = mount(
|
||||
const { container: wrapperLeft } = render(
|
||||
<Badge.Ribbon color="#888" placement="start">
|
||||
<div />
|
||||
</Badge.Ribbon>,
|
||||
);
|
||||
expect(wrapperLeft.find('.ant-ribbon').prop('style')?.background).toEqual('#888');
|
||||
expect(wrapperLeft.find('.ant-ribbon-corner').prop('style')?.color).toEqual('#888');
|
||||
const wrapperRight = mount(
|
||||
expect((wrapperLeft.querySelector('.ant-ribbon')! as HTMLElement).style.background).toEqual(
|
||||
'rgb(136, 136, 136)',
|
||||
);
|
||||
expect((wrapperLeft.querySelector('.ant-ribbon-corner')! as HTMLElement).style.color).toEqual(
|
||||
'rgb(136, 136, 136)',
|
||||
);
|
||||
const { container: wrapperRight } = render(
|
||||
<Badge.Ribbon color="#888" placement="end">
|
||||
<div />
|
||||
</Badge.Ribbon>,
|
||||
);
|
||||
expect(wrapperRight.find('.ant-ribbon').prop('style')?.background).toEqual('#888');
|
||||
expect(wrapperRight.find('.ant-ribbon-corner').prop('style')?.color).toEqual('#888');
|
||||
expect((wrapperRight.querySelector('.ant-ribbon')! as HTMLElement).style.background).toEqual(
|
||||
'rgb(136, 136, 136)',
|
||||
);
|
||||
expect(
|
||||
(wrapperRight.querySelector('.ant-ribbon-corner')! as HTMLElement).style.color,
|
||||
).toEqual('rgb(136, 136, 136)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('text', () => {
|
||||
it('works with string', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Badge.Ribbon text="cool">
|
||||
<div />
|
||||
</Badge.Ribbon>,
|
||||
);
|
||||
expect(wrapper.find('.ant-ribbon').text()).toEqual('cool');
|
||||
expect(container.querySelector('.ant-ribbon')?.textContent).toEqual('cool');
|
||||
});
|
||||
it('works with element', () => {
|
||||
const wrapper = mount(
|
||||
const { container } = render(
|
||||
<Badge.Ribbon text={<span className="cool" />}>
|
||||
<div />
|
||||
</Badge.Ribbon>,
|
||||
);
|
||||
expect(wrapper.find('.cool').length).toEqual(1);
|
||||
expect(container.querySelectorAll('.cool').length).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,13 +1,13 @@
|
||||
import React, { Component } from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import { mount } from 'enzyme';
|
||||
import { resetWarned } from 'rc-util/lib/warning';
|
||||
import React, { Component } from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import Button from '..';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { sleep, render, fireEvent } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import type { SizeType } from '../../config-provider/SizeContext';
|
||||
|
||||
describe('Button', () => {
|
||||
@ -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(
|
||||
|
@ -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,18 +1,18 @@
|
||||
/* eslint-disable react/button-has-type */
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import * as React from 'react';
|
||||
|
||||
import Group, { GroupSizeContext } from './button-group';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import Wave from '../_util/wave';
|
||||
import { tuple } from '../_util/type';
|
||||
import warning from '../_util/warning';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import LoadingIcon from './LoadingIcon';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import { tuple } from '../_util/type';
|
||||
import warning from '../_util/warning';
|
||||
import Wave from '../_util/wave';
|
||||
import Group, { GroupSizeContext } from './button-group';
|
||||
import LoadingIcon from './LoadingIcon';
|
||||
|
||||
// CSSINJS
|
||||
import useStyle from './style';
|
||||
@ -254,6 +254,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,
|
||||
hashId,
|
||||
@ -268,6 +270,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,
|
||||
);
|
||||
@ -284,7 +287,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 wrapSSR(
|
||||
<a {...linkButtonRestProps} className={classes} onClick={handleClick} ref={buttonRef}>
|
||||
|
@ -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';
|
||||
import useStyle from './style';
|
||||
|
||||
const Placements = tuple(
|
||||
@ -45,6 +45,7 @@ export type DropdownArrowOptions = {
|
||||
};
|
||||
|
||||
export interface DropdownProps {
|
||||
autoFocus?: boolean;
|
||||
arrow?: boolean | DropdownArrowOptions;
|
||||
trigger?: ('click' | 'hover' | 'contextMenu')[];
|
||||
overlay: React.ReactElement | OverlayFunc;
|
||||
|
@ -27,8 +27,8 @@ import FormItemLabel from './FormItemLabel';
|
||||
import useDebounce from './hooks/useDebounce';
|
||||
import useFrameState from './hooks/useFrameState';
|
||||
import useItemRef from './hooks/useItemRef';
|
||||
import { getFieldId, toArray } from './util';
|
||||
import useStyle from './style';
|
||||
import { getFieldId, toArray } from './util';
|
||||
|
||||
const NAME_SPLIT = '__SPLIT__';
|
||||
|
||||
@ -221,14 +221,9 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
const getItemRef = useItemRef();
|
||||
|
||||
// ======================== Status ========================
|
||||
const { status: contextStatus, hasFeedback: contextHasFeedback } =
|
||||
useContext(FormItemInputContext);
|
||||
|
||||
let mergedValidateStatus: ValidateStatus | undefined;
|
||||
let mergedValidateStatus: ValidateStatus = '';
|
||||
if (validateStatus !== undefined) {
|
||||
mergedValidateStatus = validateStatus;
|
||||
} else if (contextStatus !== undefined) {
|
||||
mergedValidateStatus = contextStatus;
|
||||
} else if (meta?.validating) {
|
||||
mergedValidateStatus = 'validating';
|
||||
} else if (debounceErrors.length) {
|
||||
@ -239,11 +234,9 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
mergedValidateStatus = 'success';
|
||||
}
|
||||
|
||||
const mergedHasFeedback = hasFeedback || contextHasFeedback;
|
||||
|
||||
const formItemStatusContext = useMemo<FormItemStatusContextProps>(() => {
|
||||
let feedbackIcon: ReactNode;
|
||||
if (mergedHasFeedback) {
|
||||
if (hasFeedback) {
|
||||
const IconNode = mergedValidateStatus && iconMap[mergedValidateStatus];
|
||||
feedbackIcon = IconNode ? (
|
||||
<span
|
||||
@ -259,19 +252,11 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
|
||||
return {
|
||||
status: mergedValidateStatus,
|
||||
hasFeedback: mergedHasFeedback,
|
||||
hasFeedback,
|
||||
feedbackIcon,
|
||||
isFormItemInput: true,
|
||||
};
|
||||
}, [mergedValidateStatus, mergedHasFeedback]);
|
||||
|
||||
const noOverrideFormItemContext = useMemo<FormItemStatusContextProps>(
|
||||
() => ({
|
||||
...formItemStatusContext,
|
||||
isFormItemInput: false,
|
||||
}),
|
||||
[formItemStatusContext],
|
||||
);
|
||||
}, [mergedValidateStatus, hasFeedback]);
|
||||
|
||||
// ======================== Render ========================
|
||||
function renderLayout(
|
||||
@ -280,11 +265,7 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
isRequired?: boolean,
|
||||
): React.ReactNode {
|
||||
if (noStyle && !hidden) {
|
||||
return (
|
||||
<FormItemInputContext.Provider value={noOverrideFormItemContext}>
|
||||
{baseChildren}
|
||||
</FormItemInputContext.Provider>
|
||||
);
|
||||
return baseChildren;
|
||||
}
|
||||
|
||||
const itemClassName = {
|
||||
@ -294,7 +275,7 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
[`${className}`]: !!className,
|
||||
|
||||
// Status
|
||||
[`${prefixCls}-item-has-feedback`]: mergedValidateStatus && mergedHasFeedback,
|
||||
[`${prefixCls}-item-has-feedback`]: mergedValidateStatus && hasFeedback,
|
||||
[`${prefixCls}-item-has-success`]: mergedValidateStatus === 'success',
|
||||
[`${prefixCls}-item-has-warning`]: mergedValidateStatus === 'warning',
|
||||
[`${prefixCls}-item-has-error`]: mergedValidateStatus === 'error',
|
||||
|
@ -6836,7 +6836,7 @@ exports[`renders ./components/form/demo/normal-login.md extend context correctly
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-in-form-item"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-checkbox-checked"
|
||||
@ -18031,7 +18031,7 @@ exports[`renders ./components/form/demo/validate-other.md extend context correct
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number"
|
||||
class="ant-input-number ant-input-number-in-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
|
@ -4451,7 +4451,7 @@ exports[`renders ./components/form/demo/normal-login.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-in-form-item"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-checkbox-checked"
|
||||
@ -7257,7 +7257,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number"
|
||||
class="ant-input-number ant-input-number-in-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
|
@ -1207,20 +1207,6 @@ describe('Form', () => {
|
||||
expect(subFormInstance).toBe(formInstance);
|
||||
});
|
||||
|
||||
it('noStyle should not be affected by parent', () => {
|
||||
const Demo = () => (
|
||||
<Form>
|
||||
<Form.Item>
|
||||
<Form.Item noStyle>
|
||||
<Select className="custom-select" />
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
const { container } = render(<Demo />);
|
||||
expect(container.querySelector('.custom-select')?.className).not.toContain('in-form-item');
|
||||
});
|
||||
|
||||
it('noStyle should not affect status', () => {
|
||||
const Demo = () => (
|
||||
<Form>
|
||||
@ -1237,12 +1223,22 @@ describe('Form', () => {
|
||||
<Select className="custom-select-c" />
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
<Form.Item noStyle>
|
||||
<Form.Item validateStatus="warning">
|
||||
<Select className="custom-select-d" />
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
const { container } = render(<Demo />);
|
||||
expect(container.querySelector('.custom-select')?.className).toContain('status-error');
|
||||
expect(container.querySelector('.custom-select')?.className).not.toContain('status-error');
|
||||
expect(container.querySelector('.custom-select')?.className).not.toContain('in-form-item');
|
||||
expect(container.querySelector('.custom-select-b')?.className).toContain('status-error');
|
||||
expect(container.querySelector('.custom-select-c')?.className).toContain('status-warning');
|
||||
expect(container.querySelector('.custom-select-b')?.className).toContain('in-form-item');
|
||||
expect(container.querySelector('.custom-select-c')?.className).toContain('status-error');
|
||||
expect(container.querySelector('.custom-select-c')?.className).toContain('in-form-item');
|
||||
expect(container.querySelector('.custom-select-d')?.className).toContain('status-warning');
|
||||
expect(container.querySelector('.custom-select-d')?.className).toContain('in-form-item');
|
||||
});
|
||||
|
||||
it('should not affect Popup children style', () => {
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -20,7 +20,12 @@ const Image: CompositionImage<ImageProps> = ({
|
||||
rootClassName,
|
||||
...otherProps
|
||||
}) => {
|
||||
const { getPrefixCls, locale: contextLocale = defaultLocale } = useContext(ConfigContext);
|
||||
const {
|
||||
getPrefixCls,
|
||||
locale: contextLocale = defaultLocale,
|
||||
getPopupContainer: getContextPopupContainer,
|
||||
} = useContext(ConfigContext);
|
||||
|
||||
const prefixCls = getPrefixCls('image', customizePrefixCls);
|
||||
const rootPrefixCls = getPrefixCls();
|
||||
|
||||
@ -34,7 +39,7 @@ const Image: CompositionImage<ImageProps> = ({
|
||||
return preview;
|
||||
}
|
||||
const _preview = typeof preview === 'object' ? preview : {};
|
||||
|
||||
const { getContainer, ...restPreviewProps } = _preview;
|
||||
return {
|
||||
mask: (
|
||||
<div className={`${prefixCls}-mask-info`}>
|
||||
@ -43,7 +48,8 @@ const Image: CompositionImage<ImageProps> = ({
|
||||
</div>
|
||||
),
|
||||
icons,
|
||||
..._preview,
|
||||
...restPreviewProps,
|
||||
getContainer: getContainer || getContextPopupContainer,
|
||||
transitionName: getTransitionName(rootPrefixCls, 'zoom', _preview.transitionName),
|
||||
maskTransitionName: getTransitionName(rootPrefixCls, 'fade', _preview.maskTransitionName),
|
||||
};
|
||||
|
@ -6,12 +6,12 @@ import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import ClearableLabeledInput from './ClearableLabeledInput';
|
||||
import type { InputFocusOptions } from './Input';
|
||||
import { fixControlledValue, resolveOnChange, triggerFocus } from './Input';
|
||||
@ -165,9 +165,8 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
|
||||
|
||||
// ============================== Reset ===============================
|
||||
const handleReset = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
handleSetValue('', () => {
|
||||
innerRef.current?.focus();
|
||||
});
|
||||
handleSetValue('');
|
||||
innerRef.current?.focus();
|
||||
resolveOnChange(innerRef.current?.resizableTextArea?.textArea!, e, onChange);
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
|
||||
import React, { useState } from 'react';
|
||||
import Input from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import { sleep, render, fireEvent, triggerResize } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, triggerResize } from '../../../tests/utils';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
@ -530,4 +531,28 @@ describe('TextArea allowClear', () => {
|
||||
rerender(<TextArea value={false} />);
|
||||
expect(container.querySelector('textarea').value).toBe('false');
|
||||
});
|
||||
|
||||
it('should focus when clearBtn is clicked in controlled case', () => {
|
||||
const handleFocus = jest.fn();
|
||||
|
||||
const textareaSpy = spyElementPrototypes(HTMLTextAreaElement, {
|
||||
focus: handleFocus,
|
||||
});
|
||||
|
||||
const Demo = () => {
|
||||
const [value, setValue] = React.useState('');
|
||||
|
||||
return <Input.TextArea allowClear value={value} onChange={e => setValue(e.target.value)} />;
|
||||
};
|
||||
|
||||
const { container } = render(<Demo />);
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'test' } });
|
||||
expect(container.querySelector('.ant-input-clear-icon')?.className).not.toContain(
|
||||
'ant-input-clear-icon-hidden',
|
||||
);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
expect(handleFocus).toHaveBeenCalledTimes(1);
|
||||
|
||||
textareaSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
@ -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 }) | - | |
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import MockDate from 'mockdate';
|
||||
import dayjs from 'dayjs';
|
||||
import { mount } from 'enzyme';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import MockDate from 'mockdate';
|
||||
import React from 'react';
|
||||
import Statistic from '..';
|
||||
import { formatTimeStr } from '../utils';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import type Countdown from '../Countdown';
|
||||
import { formatTimeStr } from '../utils';
|
||||
|
||||
describe('Statistic', () => {
|
||||
mountTest(Statistic);
|
||||
@ -23,32 +22,34 @@ 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', () => {
|
||||
@ -58,54 +59,57 @@ describe('Statistic', () => {
|
||||
[-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);
|
||||
].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 = dayjs().add(2, 'd').add(11, 'h').add(28, 'm').add(9, 's').add(3, 'ms');
|
||||
|
||||
[
|
||||
['H:m:s', '59:28:9'],
|
||||
['HH:mm:ss', '59:28:09'],
|
||||
['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();
|
||||
const instance = React.createRef<Countdown>();
|
||||
const { unmount } = render(
|
||||
<Statistic.Countdown ref={instance} value={now} onFinish={onFinish} />,
|
||||
);
|
||||
|
||||
// setInterval should work
|
||||
const instance = wrapper.find('Countdown').instance();
|
||||
expect(instance.countdownId).not.toBe(undefined);
|
||||
expect(instance.current!.countdownId).not.toBe(undefined);
|
||||
|
||||
await sleep(10);
|
||||
|
||||
wrapper.unmount();
|
||||
expect(instance.countdownId).toBe(undefined);
|
||||
unmount();
|
||||
expect(onFinish).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -115,21 +119,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 +142,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 +155,18 @@ describe('Statistic', () => {
|
||||
describe('time finished', () => {
|
||||
it('not call if time already passed', () => {
|
||||
const now = Date.now() - 1000;
|
||||
|
||||
const instance = React.createRef<Countdown>();
|
||||
const onFinish = jest.fn();
|
||||
const wrapper = mount(<Statistic.Countdown value={now} onFinish={onFinish} />);
|
||||
wrapper.update();
|
||||
render(<Statistic.Countdown ref={instance} value={now} onFinish={onFinish} />);
|
||||
|
||||
expect(wrapper.find('Countdown').instance().countdownId).toBe(undefined);
|
||||
expect(instance.current!.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(dayjs('2019-11-28 00:00:00').valueOf());
|
||||
await sleep(100);
|
||||
expect(onFinish).toHaveBeenCalled();
|
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
|
||||
|
@ -26,7 +26,7 @@ const App: React.FC = () => {
|
||||
const handleUpload = () => {
|
||||
const formData = new FormData();
|
||||
fileList.forEach(file => {
|
||||
formData.append('files[]', file.originFileObj as RcFile);
|
||||
formData.append('files[]', file as RcFile);
|
||||
});
|
||||
setUploading(true);
|
||||
// You can use any AJAX library you like
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "antd",
|
||||
"version": "4.21.0",
|
||||
"version": "4.21.2",
|
||||
"description": "An enterprise-class UI design language and React components implementation",
|
||||
"title": "Ant Design",
|
||||
"keywords": [
|
||||
@ -287,10 +287,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