mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
Merge branch 'master' into next-merge-master
This commit is contained in:
commit
d5c17a9a7c
36
.github/workflows/test.yml
vendored
36
.github/workflows/test.yml
vendored
@ -205,12 +205,18 @@ jobs:
|
||||
if: ${{ matrix.module == 'dom' }}
|
||||
run: npm test -- --maxWorkers=2 --shard=${{matrix.shard}} --coverage
|
||||
|
||||
- name: coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
- name: persist coverages
|
||||
if: ${{ matrix.module == 'dom' && matrix.react == '17' }}
|
||||
run: |
|
||||
mkdir persist-coverage
|
||||
mv coverage/coverage-final.json persist-coverage/react-${{matrix.react}}-test-${{matrix.module}}-${{strategy.job-index}}.json
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ matrix.module == 'dom' && matrix.react == '17' }}
|
||||
name: upload coverages
|
||||
with:
|
||||
# use own token to upload coverage reports
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
name: coverage-artifacts
|
||||
path: persist-coverage/
|
||||
|
||||
# node test
|
||||
- name: node test
|
||||
@ -225,6 +231,28 @@ jobs:
|
||||
LIB_DIR: dist
|
||||
needs: [setup, dist]
|
||||
|
||||
############################ Test Coverage ###########################
|
||||
upload-test-coverage:
|
||||
name: test-coverage
|
||||
runs-on: ubuntu-latest
|
||||
needs: [normal-test]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: coverage-artifacts
|
||||
path: persist-coverage
|
||||
- name: Merge Code Coverage
|
||||
run: |
|
||||
npx nyc merge persist-coverage/ coverage/coverage-final.json
|
||||
npx nyc report --reporter text -t coverage --report-dir coverage
|
||||
rm -rf persist-coverage
|
||||
- name: Upload coverage to codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
# use own token to upload coverage reports
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
########################### Compile & Test ###########################
|
||||
compile:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -15,6 +15,37 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.23.0
|
||||
|
||||
`2022-09-04`
|
||||
|
||||
- 🆕 Tooltip support nested Fragment child nodes to display bubbles. [#37045](https://github.com/ant-design/ant-design/pull/37045) [@HQ-Lin](https://github.com/HQ-Lin)
|
||||
- 🆕 Dropdown.Button support `danger` props. [#36810](https://github.com/ant-design/ant-design/pull/36810) [@nuintun](https://github.com/nuintun)
|
||||
- 🆕 Input.TextArea add `value` parameter to `showCount.formatter`. [#36793](https://github.com/ant-design/ant-design/pull/36793) [@JarvisArt](https://github.com/JarvisArt)
|
||||
- 🆕 Table support `expandable.columnTitle` now. [#36794](https://github.com/ant-design/ant-design/pull/36794) [@losgif](https://github.com/losgif)
|
||||
- Deprecate `visible` in all components and change to `open`.
|
||||
- 🛠 Dropdown changes `visible` to `open`. [#37232](https://github.com/ant-design/ant-design/pull/37232) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Modal changes `visible` to `open`. [#37084](https://github.com/ant-design/ant-design/pull/37084) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Drawer changes `visible` to `open`. [#37047](https://github.com/ant-design/ant-design/pull/37047) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Table changes `filterDropdownVisible` to `filterDropdownOpen`. [#37026](https://github.com/ant-design/ant-design/pull/37026) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Slider add `tooltip` prop for all props related with Tooltip. [#37000](https://github.com/ant-design/ant-design/pull/37000) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Tooltip Popover and Popconfirm change `visible` to `open`. [#37241](https://github.com/ant-design/ant-design/pull/37241) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Remove `visible` prop of Tag. [#36934](https://github.com/ant-design/ant-design/pull/36934) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Deprecate `dropdownClassName` prop of all components and change to `popupClassName`. [#36880](https://github.com/ant-design/ant-design/pull/36880) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🛠 Tabs support `items` props and origin jsx usage will be depreacted. [#36889](https://github.com/ant-design/ant-design/pull/36889)
|
||||
- 🐞 Fix that some css variables are not consistent with less variables.
|
||||
- [#37064](https://github.com/ant-design/ant-design/pull/37064) [@TrickyPi](https://github.com/TrickyPi)
|
||||
- [#37304](https://github.com/ant-design/ant-design/pull/37304) [@peritot](https://github.com/peritot)
|
||||
- 🐞 Fix Menu disabled item focus style. [#37332](https://github.com/ant-design/ant-design/pull/37332)
|
||||
- 💄 `@border-radius-sm` should not follow `@border-radius-base` by default. [#37309](https://github.com/ant-design/ant-design/pull/37309)
|
||||
- 💄 add `@slider-handle-margin-left` to custom type. [#37001](https://github.com/ant-design/ant-design/pull/37001) [@alanhaledc](https://github.com/alanhaledc)
|
||||
- 💄 Replace Tabs with fade switch motion to import switch experience. [#36943](https://github.com/ant-design/ant-design/pull/36943)
|
||||
- ⌨️ Improve Form validation accessibility experience. [#36762](https://github.com/ant-design/ant-design/pull/36762) [@VladimirOtroshchenko](https://github.com/VladimirOtroshchenko)
|
||||
- 🌐 Add missing translations for filterCheckall in ru_RU. [#37311](https://github.com/ant-design/ant-design/pull/37311) [@HelLuv](https://github.com/HelLuv)
|
||||
- 🌐 Add missing translations in `cs_CZ`. [#37388](https://github.com/ant-design/ant-design/pull/37388) [@ZdenekKrcal](https://github.com/ZdenekKrcal)
|
||||
|
||||
---
|
||||
|
||||
## 4.22.8
|
||||
|
||||
`2022-08-26`
|
||||
|
@ -15,6 +15,37 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.23.0
|
||||
|
||||
`2022-09-04`
|
||||
|
||||
- 🆕 Tooltip 支持 Fragment 子节点展示气泡。[#37045](https://github.com/ant-design/ant-design/pull/37045) [@HQ-Lin](https://github.com/HQ-Lin)
|
||||
- 🆕 Dropdown.Button 支持 `danger` 样式。[#36810](https://github.com/ant-design/ant-design/pull/36810) [@nuintun](https://github.com/nuintun)
|
||||
- 🆕 Input.TextArea 组件 `showCount.formatter` API 添加 `value` 参数。[#36793](https://github.com/ant-design/ant-design/pull/36793) [@JarvisArt](https://github.com/JarvisArt)
|
||||
- 🆕 Table 新增 `expandable.columnTitle` 属性以支持自定义展开列表头。[#36794](https://github.com/ant-design/ant-design/pull/36794) [@losgif](https://github.com/losgif)
|
||||
- 🛠 废弃所有弹窗组件的 `visible` 属性,统一为 `open`。
|
||||
- 🛠 Dropdown 的 `visible` 改为 `open`。[#37232](https://github.com/ant-design/ant-design/pull/37232) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Modal 组件的 `visible` 改为 `open`。[#37084](https://github.com/ant-design/ant-design/pull/37084) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Drawer 的 `visible` 改为 `open`。[#37047](https://github.com/ant-design/ant-design/pull/37047) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Table 组件 `columns` 中的 `filterDropdownVisible` 改为 `filterDropdownOpen`。[#37026](https://github.com/ant-design/ant-design/pull/37026) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Tooltip, Popover 和 Popconfirm 中的 `visible` 改为 `open`。[#37241](https://github.com/ant-design/ant-design/pull/37241) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 Slider 的 `tooltip` 相关属性合并到 `tooltip` 属性中。[#37000](https://github.com/ant-design/ant-design/pull/37000) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 移除 Tag 组件的 `visible` 属性。[#36934](https://github.com/ant-design/ant-design/pull/36934) [@yykoypj](https://github.com/yykoypj)
|
||||
- 🛠 废弃所有组件的 `dropdownClassName`,统一为 `popupClassName`。[#36880](https://github.com/ant-design/ant-design/pull/36880) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🛠 Tabs 支持 `items` 属性,并且废弃原 jsx 语法糖用法。[#36889](https://github.com/ant-design/ant-design/pull/36889)
|
||||
- 🐞 修复 css 变量与 less 变量不一致的问题。
|
||||
- [#37064](https://github.com/ant-design/ant-design/pull/37064) [@TrickyPi](https://github.com/TrickyPi)
|
||||
- [#37304](https://github.com/ant-design/ant-design/pull/37304) [@peritot](https://github.com/peritot)
|
||||
- 🐞 修复 Menu 禁用项依然有 focus 样式的问题。[#37332](https://github.com/ant-design/ant-design/pull/37332)
|
||||
- 💄 `@border-radius-sm` 变量默认值不与 `@border-radius-base` 关联,以修复 Checkbox 等组件圆角样式异常。[#37309](https://github.com/ant-design/ant-design/pull/37309)
|
||||
- 💄 支持使用 `@slider-handle-margin-left` 定制样式。[#37001](https://github.com/ant-design/ant-design/pull/37001) [@alanhaledc](https://github.com/alanhaledc)
|
||||
- 💄 替换 Tabs 切换样式为渐隐过渡,以提升在切换时的体验。[#36943](https://github.com/ant-design/ant-design/pull/36943)
|
||||
- ⌨️ 改进 Form 校验无障碍体验。[#36762](https://github.com/ant-design/ant-design/pull/36762) [@VladimirOtroshchenko](https://github.com/VladimirOtroshchenko)
|
||||
- 🌐 补全 `ru_RU` 中 `filterCheckall` 的翻译。[#37311](https://github.com/ant-design/ant-design/pull/37311) [@HelLuv](https://github.com/HelLuv)
|
||||
- 🌐 补全 `cs_CZ` 的翻译。[#37388](https://github.com/ant-design/ant-design/pull/37388) [@ZdenekKrcal](https://github.com/ZdenekKrcal)
|
||||
|
||||
---
|
||||
|
||||
## 4.22.8
|
||||
|
||||
`2022-08-26`
|
||||
|
@ -1,11 +1,9 @@
|
||||
import type { ReactWrapper } from 'enzyme';
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import type { AffixProps, AffixState, InternalAffixClass } from '..';
|
||||
import type { InternalAffixClass } from '..';
|
||||
import Affix from '..';
|
||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { render, sleep } from '../../../tests/utils';
|
||||
import { render, sleep, triggerResize } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
import { getObserverEntities } from '../utils';
|
||||
|
||||
@ -16,11 +14,10 @@ class AffixMounter extends React.Component<{
|
||||
offsetTop?: number;
|
||||
onTestUpdatePosition?(): void;
|
||||
onChange?: () => void;
|
||||
getInstance?: (inst: InternalAffixClass) => void;
|
||||
}> {
|
||||
private container: HTMLDivElement;
|
||||
|
||||
public affix: React.Component<AffixProps, AffixState>;
|
||||
|
||||
componentDidMount() {
|
||||
this.container.addEventListener = jest
|
||||
.fn()
|
||||
@ -32,6 +29,7 @@ class AffixMounter extends React.Component<{
|
||||
getTarget = () => this.container;
|
||||
|
||||
render() {
|
||||
const { getInstance, ...restProps } = this.props;
|
||||
return (
|
||||
<div
|
||||
ref={node => {
|
||||
@ -43,9 +41,9 @@ class AffixMounter extends React.Component<{
|
||||
className="fixed"
|
||||
target={this.getTarget}
|
||||
ref={ele => {
|
||||
this.affix = ele!;
|
||||
getInstance?.(ele!);
|
||||
}}
|
||||
{...this.props}
|
||||
{...restProps}
|
||||
>
|
||||
<Button type="primary">Fixed at the top of container</Button>
|
||||
</Affix>
|
||||
@ -59,7 +57,6 @@ describe('Affix Render', () => {
|
||||
accessibilityTest(Affix);
|
||||
|
||||
const domMock = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect');
|
||||
let affixMounterWrapper: ReactWrapper<unknown, unknown, AffixMounter>;
|
||||
|
||||
const classRect: Record<string, DOMRect> = {
|
||||
container: {
|
||||
@ -194,34 +191,43 @@ describe('Affix Render', () => {
|
||||
});
|
||||
|
||||
describe('updatePosition when size changed', () => {
|
||||
it.each([
|
||||
{ name: 'inner', index: 0 },
|
||||
{ name: 'outer', index: 1 },
|
||||
])('inner or outer', async ({ index }) => {
|
||||
it('add class automatically', async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
const updateCalled = jest.fn();
|
||||
affixMounterWrapper = mount(
|
||||
<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />,
|
||||
let affixInstance: InternalAffixClass | null = null;
|
||||
render(
|
||||
<AffixMounter
|
||||
getInstance={inst => {
|
||||
affixInstance = inst;
|
||||
}}
|
||||
offsetBottom={0}
|
||||
/>,
|
||||
{
|
||||
attachTo: document.getElementById('mounter'),
|
||||
container: document.getElementById('mounter')!,
|
||||
},
|
||||
);
|
||||
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(300);
|
||||
expect(
|
||||
(affixMounterWrapper.find(AffixMounter).instance() as any).affix.state.affixStyle,
|
||||
).toBeTruthy();
|
||||
await sleep(20);
|
||||
affixMounterWrapper.update();
|
||||
expect(affixInstance!.state.affixStyle).toBeTruthy();
|
||||
});
|
||||
|
||||
// Trigger inner and outer element for the two <ResizeObserver>s.
|
||||
it.each([
|
||||
{ selector: '.ant-btn' }, // inner
|
||||
{ selector: '.fixed' }, // outer
|
||||
])('trigger listener when size change', async ({ selector }) => {
|
||||
const updateCalled = jest.fn();
|
||||
const { container } = render(
|
||||
<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />,
|
||||
{
|
||||
container: document.getElementById('mounter')!,
|
||||
},
|
||||
);
|
||||
|
||||
// Mock trigger resize
|
||||
updateCalled.mockReset();
|
||||
(affixMounterWrapper as any).triggerResize(index);
|
||||
triggerResize(container.querySelector(selector)!);
|
||||
await sleep(20);
|
||||
|
||||
expect(updateCalled).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -8,7 +8,7 @@ describe('AutoComplete children could be focus', () => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
let container;
|
||||
let container: HTMLDivElement;
|
||||
beforeEach(() => {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
@ -24,10 +24,8 @@ describe('AutoComplete children could be focus', () => {
|
||||
|
||||
it('focus() and onFocus', () => {
|
||||
const handleFocus = jest.fn();
|
||||
const { container: wrapper } = render(<AutoComplete onFocus={handleFocus} />, {
|
||||
attachTo: container,
|
||||
});
|
||||
wrapper.querySelector('input').focus();
|
||||
const { container: wrapper } = render(<AutoComplete onFocus={handleFocus} />, { container });
|
||||
wrapper.querySelector('input')?.focus();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
@ -36,14 +34,12 @@ describe('AutoComplete children could be focus', () => {
|
||||
|
||||
it('blur() and onBlur', () => {
|
||||
const handleBlur = jest.fn();
|
||||
const { container: wrapper } = render(<AutoComplete onBlur={handleBlur} />, {
|
||||
attachTo: container,
|
||||
});
|
||||
wrapper.querySelector('input').focus();
|
||||
const { container: wrapper } = render(<AutoComplete onBlur={handleBlur} />, { container });
|
||||
wrapper.querySelector('input')?.focus();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.querySelector('input').blur();
|
||||
wrapper.querySelector('input')?.blur();
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
@ -61,17 +57,13 @@ describe('AutoComplete children could be focus', () => {
|
||||
});
|
||||
|
||||
it('child.ref instance should support be focused and blured', () => {
|
||||
let inputRef;
|
||||
const inputRef = React.createRef<HTMLInputElement>();
|
||||
render(
|
||||
<AutoComplete dataSource={[]}>
|
||||
<input
|
||||
ref={node => {
|
||||
inputRef = node;
|
||||
}}
|
||||
/>
|
||||
<input ref={inputRef} />
|
||||
</AutoComplete>,
|
||||
);
|
||||
expect(typeof inputRef.focus).toBe('function');
|
||||
expect(typeof inputRef.blur).toBe('function');
|
||||
expect(typeof inputRef.current?.focus).toBe('function');
|
||||
expect(typeof inputRef.current?.blur).toBe('function');
|
||||
});
|
||||
});
|
@ -10,14 +10,14 @@ describe('BackTop', () => {
|
||||
|
||||
it('should scroll to top after click it', async () => {
|
||||
const { container } = render(<BackTop visibilityHeight={-1} />);
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((_, y) => {
|
||||
window.scrollY = y;
|
||||
window.pageYOffset = y;
|
||||
document.documentElement.scrollTop = y;
|
||||
});
|
||||
window.scrollTo(0, 400);
|
||||
expect(document.documentElement.scrollTop).toBe(400);
|
||||
fireEvent.click(container.querySelector('.ant-back-top'));
|
||||
fireEvent.click(container.querySelector('.ant-back-top')!);
|
||||
await sleep(500);
|
||||
expect(document.documentElement.scrollTop).toBe(0);
|
||||
scrollToSpy.mockRestore();
|
||||
@ -26,23 +26,21 @@ describe('BackTop', () => {
|
||||
it('support onClick', async () => {
|
||||
const onClick = jest.fn();
|
||||
const { container } = render(<BackTop onClick={onClick} visibilityHeight={-1} />);
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((_, y) => {
|
||||
window.scrollY = y;
|
||||
window.pageYOffset = y;
|
||||
});
|
||||
document.dispatchEvent(new Event('scroll'));
|
||||
window.scrollTo(0, 400);
|
||||
fireEvent.click(container.querySelector('.ant-back-top'));
|
||||
fireEvent.click(container.querySelector('.ant-back-top')!);
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
scrollToSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('invalid target', async () => {
|
||||
const onClick = jest.fn();
|
||||
const { container } = render(
|
||||
<BackTop onClick={onClick} visible target={() => ({ documentElement: {} })} />,
|
||||
);
|
||||
fireEvent.click(container.querySelector('.ant-back-top'));
|
||||
const { container } = render(<BackTop onClick={onClick} visible target={undefined} />);
|
||||
fireEvent.click(container.querySelector('.ant-back-top')!);
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -3,6 +3,7 @@ import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { render } from '../../../tests/utils';
|
||||
import type { Route } from '../Breadcrumb';
|
||||
import Breadcrumb from '../index';
|
||||
|
||||
describe('Breadcrumb', () => {
|
||||
@ -22,7 +23,7 @@ describe('Breadcrumb', () => {
|
||||
|
||||
// https://github.com/airbnb/enzyme/issues/875
|
||||
it('warns on non-Breadcrumb.Item and non-Breadcrumb.Separator children', () => {
|
||||
const MyCom = () => <div>foo</div>;
|
||||
const MyCom: React.FC = () => <div>foo</div>;
|
||||
render(
|
||||
<Breadcrumb>
|
||||
<MyCom />
|
||||
@ -74,7 +75,7 @@ describe('Breadcrumb', () => {
|
||||
});
|
||||
|
||||
it('should render a menu', () => {
|
||||
const routes = [
|
||||
const routes: Route[] = [
|
||||
{
|
||||
path: 'index',
|
||||
breadcrumbName: 'home',
|
||||
@ -103,6 +104,7 @@ describe('Breadcrumb', () => {
|
||||
},
|
||||
{
|
||||
path: 'third',
|
||||
breadcrumbName: '',
|
||||
},
|
||||
];
|
||||
const { asFragment } = render(<Breadcrumb routes={routes} />);
|
||||
@ -142,7 +144,7 @@ describe('Breadcrumb', () => {
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/25975
|
||||
it('should support Breadcrumb.Item default separator', () => {
|
||||
const MockComponent = () => (
|
||||
const MockComponent: React.FC = () => (
|
||||
<span>
|
||||
<Breadcrumb.Item>Mock Node</Breadcrumb.Item>
|
||||
</span>
|
@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import type { RouterProps } from 'react-router-dom';
|
||||
import { Link, MemoryRouter, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import Breadcrumb from '../index';
|
||||
|
||||
const Apps = () => (
|
||||
const Apps: React.FC = () => (
|
||||
<ul className="app-list">
|
||||
<li>
|
||||
<Link to="/apps/1">Application1</Link>:<Link to="/apps/1/detail">Detail</Link>
|
||||
@ -33,7 +34,7 @@ describe('react router', () => {
|
||||
|
||||
// https://github.com/airbnb/enzyme/issues/875
|
||||
it('react router 6', () => {
|
||||
const Home = () => {
|
||||
const Home: React.FC = () => {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const pathSnippets = location.pathname.split('/').filter(i => i);
|
||||
@ -41,7 +42,7 @@ describe('react router', () => {
|
||||
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
|
||||
return (
|
||||
<Breadcrumb.Item key={url}>
|
||||
<Link to={url}>{breadcrumbNameMap[url]}</Link>
|
||||
<Link to={url}>{breadcrumbNameMap[url as keyof typeof breadcrumbNameMap]}</Link>
|
||||
</Breadcrumb.Item>
|
||||
);
|
||||
});
|
||||
@ -50,6 +51,14 @@ describe('react router', () => {
|
||||
<Link to="/">Home</Link>
|
||||
</Breadcrumb.Item>,
|
||||
].concat(extraBreadcrumbItems);
|
||||
const componentProps = useMemo<RouterProps>(
|
||||
() => ({ component: Apps } as unknown as RouterProps),
|
||||
[],
|
||||
);
|
||||
const renderProps = useMemo<RouterProps>(
|
||||
() => ({ render: () => <span>Home Page</span> } as unknown as RouterProps),
|
||||
[],
|
||||
);
|
||||
return (
|
||||
<div className="demo">
|
||||
<div className="demo-nav">
|
||||
@ -57,8 +66,8 @@ describe('react router', () => {
|
||||
<a onClick={() => navigate('/apps')}>Application List</a>
|
||||
</div>
|
||||
<Routes>
|
||||
<Route path="/apps" component={Apps} />
|
||||
<Route render={() => <span>Home Page</span>} />
|
||||
<Route path="/apps" {...componentProps} />
|
||||
<Route {...renderProps} />
|
||||
</Routes>
|
||||
<Breadcrumb>{breadcrumbItems}</Breadcrumb>
|
||||
</div>
|
@ -1,4 +1,6 @@
|
||||
import React from 'react';
|
||||
import type { SingleValueType } from 'rc-cascader/lib/Cascader';
|
||||
import type { BaseOptionType, DefaultOptionType } from '..';
|
||||
import Cascader from '..';
|
||||
import excludeAllWarning from '../../../tests/shared/excludeWarning';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
@ -9,22 +11,27 @@ import { fireEvent, render } from '../../../tests/utils';
|
||||
|
||||
const { SHOW_CHILD, SHOW_PARENT } = Cascader;
|
||||
|
||||
function toggleOpen(container) {
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
|
||||
function toggleOpen(container: ReturnType<typeof render>['container']) {
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector')!);
|
||||
}
|
||||
|
||||
function isOpen(container) {
|
||||
return container.querySelector('.ant-cascader').className.includes('ant-select-open');
|
||||
function isOpen(container: ReturnType<typeof render>['container']) {
|
||||
return container.querySelector('.ant-cascader')?.className.includes('ant-select-open');
|
||||
}
|
||||
|
||||
function getDropdown(container) {
|
||||
function getDropdown(container: ReturnType<typeof render>['container']) {
|
||||
return container.querySelector('.ant-select-dropdown');
|
||||
}
|
||||
|
||||
function clickOption(container, menuIndex, itemIndex, type = 'click') {
|
||||
function clickOption(
|
||||
container: ReturnType<typeof render>['container'],
|
||||
menuIndex: number,
|
||||
itemIndex: number,
|
||||
type = 'click',
|
||||
) {
|
||||
const menu = container.querySelectorAll('ul.ant-cascader-menu')[menuIndex];
|
||||
const itemList = menu.querySelectorAll('li.ant-cascader-menu-item');
|
||||
fireEvent[type](itemList[itemIndex]);
|
||||
fireEvent?.[type as keyof typeof fireEvent]?.(itemList[itemIndex]);
|
||||
}
|
||||
|
||||
const options = [
|
||||
@ -62,8 +69,11 @@ const options = [
|
||||
},
|
||||
];
|
||||
|
||||
function filter(inputValue, path) {
|
||||
return path.some(option => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
|
||||
function filter<OptionType extends BaseOptionType = DefaultOptionType>(
|
||||
inputValue: string,
|
||||
path: OptionType[],
|
||||
): boolean {
|
||||
return path.some(option => option.label.toLowerCase().includes(inputValue.toLowerCase()));
|
||||
}
|
||||
|
||||
describe('Cascader', () => {
|
||||
@ -133,31 +143,25 @@ describe('Cascader', () => {
|
||||
|
||||
it('backspace should work with `Cascader[showSearch]`', () => {
|
||||
const { container } = render(<Cascader options={options} showSearch />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: '123' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: '123' } });
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
|
||||
fireEvent.keyDown(container.querySelector('input'), {
|
||||
key: 'Backspace',
|
||||
keyCode: 8,
|
||||
});
|
||||
fireEvent.keyDown(container.querySelector('input')!, { key: 'Backspace', keyCode: 8 });
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: '' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: '' } });
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
|
||||
fireEvent.keyDown(container.querySelector('input'), {
|
||||
key: 'Backspace',
|
||||
keyCode: 8,
|
||||
});
|
||||
fireEvent.keyDown(container.querySelector('input')!, { key: 'Backspace', keyCode: 8 });
|
||||
expect(isOpen(container)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should highlight keyword and filter when search in Cascader', () => {
|
||||
const { container } = render(<Cascader options={options} showSearch={{ filter }} />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'z' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'z' } });
|
||||
|
||||
// React 18 with testing lib will have additional space. We have to compare innerHTML. Sad.
|
||||
expect(getDropdown(container).innerHTML).toMatchSnapshot();
|
||||
expect(getDropdown(container)?.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should highlight keyword and filter when search in Cascader with same field name of label and value', () => {
|
||||
@ -180,8 +184,11 @@ describe('Cascader', () => {
|
||||
],
|
||||
},
|
||||
];
|
||||
function customFilter(inputValue, path) {
|
||||
return path.some(option => option.name.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
|
||||
function customFilter<OptionType extends BaseOptionType = DefaultOptionType>(
|
||||
inputValue: string,
|
||||
path: OptionType[],
|
||||
): boolean {
|
||||
return path.some(option => option.name.toLowerCase().includes(inputValue.toLowerCase()));
|
||||
}
|
||||
const { container } = render(
|
||||
<Cascader
|
||||
@ -190,15 +197,15 @@ describe('Cascader', () => {
|
||||
showSearch={{ filter: customFilter }}
|
||||
/>,
|
||||
);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'z' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'z' } });
|
||||
|
||||
// React 18 with testing lib will have additional space. We have to compare innerHTML. Sad.
|
||||
expect(getDropdown(container).innerHTML).toMatchSnapshot();
|
||||
expect(getDropdown(container)?.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render not found content', () => {
|
||||
const { container } = render(<Cascader options={options} showSearch={{ filter }} />);
|
||||
fireEvent.change(container.querySelector('input'), {
|
||||
fireEvent.change(container.querySelector('input')!, {
|
||||
target: { value: '__notfoundkeyword__' },
|
||||
});
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
@ -208,10 +215,10 @@ describe('Cascader', () => {
|
||||
const { container } = render(
|
||||
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />,
|
||||
);
|
||||
expect(container.querySelector('.ant-select-selection-item').textContent).toEqual(
|
||||
expect(container.querySelector('.ant-select-selection-item')?.textContent).toEqual(
|
||||
'Zhejiang / Hangzhou',
|
||||
);
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-clear'));
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-clear')!);
|
||||
expect(container.querySelector('.ant-select-selection-item')).toBeFalsy();
|
||||
});
|
||||
|
||||
@ -219,14 +226,14 @@ describe('Cascader', () => {
|
||||
const { container } = render(
|
||||
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} showSearch />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'xxx' } });
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-clear'));
|
||||
expect(container.querySelector('input').value).toEqual('');
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'xxx' } });
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-clear')!);
|
||||
expect(container.querySelector('input')?.value).toEqual('');
|
||||
});
|
||||
|
||||
it('should change filtered item when options are changed', () => {
|
||||
const { container, rerender } = render(<Cascader options={options} showSearch={{ filter }} />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'a' } });
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item').length).toBe(2);
|
||||
|
||||
rerender(<Cascader options={[options[0]]} showSearch={{ filter }} />);
|
||||
@ -235,14 +242,11 @@ describe('Cascader', () => {
|
||||
|
||||
it('should select item immediately when searching and pressing down arrow key', () => {
|
||||
const { container } = render(<Cascader options={options} showSearch={{ filter }} />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'a' } });
|
||||
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item').length).toBe(2);
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item-active').length).toBe(0);
|
||||
fireEvent.keyDown(container.querySelector('input'), {
|
||||
key: 'Down',
|
||||
keyCode: 40,
|
||||
});
|
||||
fireEvent.keyDown(container.querySelector('input')!, { key: 'Down', keyCode: 40 });
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item-active').length).toBe(1);
|
||||
});
|
||||
|
||||
@ -300,14 +304,14 @@ describe('Cascader', () => {
|
||||
clickOption(container, 0, 0);
|
||||
clickOption(container, 1, 0);
|
||||
clickOption(container, 2, 0);
|
||||
expect(container.querySelector('.ant-select-selection-item').textContent).toEqual(
|
||||
expect(container.querySelector('.ant-select-selection-item')?.textContent).toEqual(
|
||||
'Zhejiang / Hangzhou / West Lake',
|
||||
);
|
||||
expect(onChange).toHaveBeenCalledWith(['zhejiang', 'hangzhou', 'xihu'], expect.anything());
|
||||
});
|
||||
|
||||
it('should show not found content when options.length is 0', () => {
|
||||
const customerOptions = [];
|
||||
const customerOptions: any[] = [];
|
||||
const { container } = render(<Cascader options={customerOptions} />);
|
||||
toggleOpen(container);
|
||||
expect(getDropdown(container)).toMatchSnapshot();
|
||||
@ -329,7 +333,7 @@ describe('Cascader', () => {
|
||||
const { container } = render(
|
||||
<Cascader options={options} showSearch={{ filter, limit: 1 }} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'a' } });
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item')).toHaveLength(1);
|
||||
});
|
||||
|
||||
@ -337,7 +341,7 @@ describe('Cascader', () => {
|
||||
const { container } = render(
|
||||
<Cascader options={options} showSearch={{ filter, limit: false }} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'a' } });
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item')).toHaveLength(2);
|
||||
});
|
||||
|
||||
@ -345,8 +349,8 @@ describe('Cascader', () => {
|
||||
const { container } = render(
|
||||
<Cascader options={options} showSearch={{ filter, limit: -1 }} />,
|
||||
);
|
||||
fireEvent.click(container.querySelector('input'));
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'a' } });
|
||||
fireEvent.click(container.querySelector('input')!);
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'a' } });
|
||||
expect(container.querySelectorAll('.ant-cascader-menu-item')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
@ -384,11 +388,11 @@ describe('Cascader', () => {
|
||||
|
||||
it('placeholder works correctly', () => {
|
||||
const { container, rerender } = render(<Cascader options={[]} />);
|
||||
expect(container.querySelector('.ant-select-selection-placeholder').textContent).toEqual('');
|
||||
expect(container.querySelector('.ant-select-selection-placeholder')?.textContent).toEqual('');
|
||||
|
||||
const customPlaceholder = 'Custom placeholder';
|
||||
rerender(<Cascader options={[]} placeholder={customPlaceholder} />);
|
||||
expect(container.querySelector('.ant-select-selection-placeholder').textContent).toEqual(
|
||||
expect(container.querySelector('.ant-select-selection-placeholder')?.textContent).toEqual(
|
||||
customPlaceholder,
|
||||
);
|
||||
});
|
||||
@ -410,7 +414,7 @@ describe('Cascader', () => {
|
||||
toggleOpen(container);
|
||||
|
||||
// Inject in tests/__mocks__/rc-trigger.js
|
||||
expect(global.triggerProps.popupPlacement).toEqual('topRight');
|
||||
expect((global as any)?.triggerProps.popupPlacement).toEqual('topRight');
|
||||
});
|
||||
|
||||
it('popup correctly with defaultValue RTL', () => {
|
||||
@ -489,7 +493,7 @@ describe('Cascader', () => {
|
||||
const { container } = render(
|
||||
<Cascader options={options} defaultValue={['options1', 'options2']} />,
|
||||
);
|
||||
expect(container.querySelector('.ant-select-selection-item').textContent).toEqual(
|
||||
expect(container.querySelector('.ant-select-selection-item')?.textContent).toEqual(
|
||||
'options1 / options2',
|
||||
);
|
||||
});
|
||||
@ -497,7 +501,7 @@ describe('Cascader', () => {
|
||||
it('can be selected when showSearch', () => {
|
||||
const onChange = jest.fn();
|
||||
const { container } = render(<Cascader options={options} onChange={onChange} showSearch />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'Zh' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'Zh' } });
|
||||
|
||||
expect(container.querySelectorAll('.ant-cascader-menu').length).toBe(1);
|
||||
clickOption(container, 0, 0);
|
||||
@ -506,14 +510,11 @@ describe('Cascader', () => {
|
||||
|
||||
it('options should open after press esc and then search', () => {
|
||||
const { container } = render(<Cascader options={options} showSearch />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'jin' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'jin' } });
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
fireEvent.keyDown(container.querySelector('input'), {
|
||||
key: 'Esc',
|
||||
keyCode: 27,
|
||||
});
|
||||
fireEvent.keyDown(container.querySelector('input')!, { key: 'Esc', keyCode: 27 });
|
||||
expect(isOpen(container)).toBeFalsy();
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'jin' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'jin' } });
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
});
|
||||
|
||||
@ -523,7 +524,7 @@ describe('Cascader', () => {
|
||||
const { container } = render(
|
||||
<Cascader options={options} onChange={onChange} showSearch fieldNames={sameNames} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'est' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'est' } });
|
||||
clickOption(container, 0, 0);
|
||||
expect(onChange).toHaveBeenCalledWith(['Zhejiang', 'Hangzhou', 'West Lake'], expect.anything());
|
||||
});
|
||||
@ -533,14 +534,14 @@ describe('Cascader', () => {
|
||||
toggleOpen(container);
|
||||
|
||||
// Inject in tests/__mocks__/rc-trigger.js
|
||||
expect(global.triggerProps.popupPlacement).toEqual('bottomRight');
|
||||
expect((global as any).triggerProps.popupPlacement).toEqual('bottomRight');
|
||||
});
|
||||
|
||||
describe('legacy props', () => {
|
||||
it('popupClassName', () => {
|
||||
render(<Cascader open popupPlacement="bottomLeft" />);
|
||||
// Inject in tests/__mocks__/rc-trigger.js
|
||||
expect(global.triggerProps.popupPlacement).toEqual('bottomLeft');
|
||||
expect((global as any).triggerProps.popupPlacement).toEqual('bottomLeft');
|
||||
});
|
||||
|
||||
it('should support showCheckedStrategy child', () => {
|
||||
@ -583,8 +584,8 @@ describe('Cascader', () => {
|
||||
},
|
||||
];
|
||||
|
||||
let selectedValue;
|
||||
const onChange = function onChange(value) {
|
||||
let selectedValue: SingleValueType[];
|
||||
const onChange = function onChange(value: SingleValueType[]) {
|
||||
selectedValue = value;
|
||||
};
|
||||
|
||||
@ -603,9 +604,9 @@ describe('Cascader', () => {
|
||||
clickOption(container, 1, 0);
|
||||
clickOption(container, 2, 0);
|
||||
clickOption(container, 2, 1);
|
||||
expect(selectedValue[0].join(',')).toBe('zhejiang,hangzhou,xihu');
|
||||
expect(selectedValue[1].join(',')).toBe('zhejiang,hangzhou,donghu');
|
||||
expect(selectedValue.join(',')).toBe('zhejiang,hangzhou,xihu,zhejiang,hangzhou,donghu');
|
||||
expect(selectedValue![0].join(',')).toBe('zhejiang,hangzhou,xihu');
|
||||
expect(selectedValue![1].join(',')).toBe('zhejiang,hangzhou,donghu');
|
||||
expect(selectedValue!.join(',')).toBe('zhejiang,hangzhou,xihu,zhejiang,hangzhou,donghu');
|
||||
});
|
||||
|
||||
it('should support showCheckedStrategy parent', () => {
|
||||
@ -648,8 +649,8 @@ describe('Cascader', () => {
|
||||
},
|
||||
];
|
||||
|
||||
let selectedValue;
|
||||
const onChange = function onChange(value) {
|
||||
let selectedValue: SingleValueType[];
|
||||
const onChange = function onChange(value: SingleValueType[]) {
|
||||
selectedValue = value;
|
||||
};
|
||||
|
||||
@ -668,8 +669,8 @@ describe('Cascader', () => {
|
||||
clickOption(container, 2, 0);
|
||||
clickOption(container, 2, 1);
|
||||
|
||||
expect(selectedValue.length).toBe(1);
|
||||
expect(selectedValue.join(',')).toBe('zhejiang');
|
||||
expect(selectedValue!.length).toBe(1);
|
||||
expect(selectedValue!.join(',')).toBe('zhejiang');
|
||||
});
|
||||
});
|
||||
});
|
@ -19,10 +19,10 @@ describe('Checkbox', () => {
|
||||
<Checkbox onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />,
|
||||
);
|
||||
|
||||
fireEvent.mouseEnter(container.querySelector('label'));
|
||||
fireEvent.mouseEnter(container.querySelector('label')!);
|
||||
expect(onMouseEnter).toHaveBeenCalled();
|
||||
|
||||
fireEvent.mouseLeave(container.querySelector('label'));
|
||||
fireEvent.mouseLeave(container.querySelector('label')!);
|
||||
expect(onMouseLeave).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -6,6 +6,8 @@ import Collapse from '../../collapse';
|
||||
import Input from '../../input';
|
||||
import Table from '../../table';
|
||||
import Checkbox from '../index';
|
||||
import type { CheckboxValueType } from '../Group';
|
||||
import type { CheckboxGroupProps } from '../index';
|
||||
|
||||
describe('CheckboxGroup', () => {
|
||||
mountTest(Checkbox.Group);
|
||||
@ -60,9 +62,11 @@ describe('CheckboxGroup', () => {
|
||||
|
||||
it('all children should have a name property', () => {
|
||||
const { container } = render(<Checkbox.Group name="checkboxgroup" options={['Yes', 'No']} />);
|
||||
[...container.querySelectorAll('input[type="checkbox"]')].forEach(el => {
|
||||
expect(el.getAttribute('name')).toEqual('checkboxgroup');
|
||||
});
|
||||
Array.from(container.querySelectorAll<HTMLInputElement>('input[type="checkbox"]')).forEach(
|
||||
el => {
|
||||
expect(el.getAttribute('name')).toEqual('checkboxgroup');
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('passes prefixCls down to checkbox', () => {
|
||||
@ -81,10 +85,10 @@ describe('CheckboxGroup', () => {
|
||||
{ label: 'Apple', value: 'Apple' },
|
||||
{ label: 'Orange', value: 'Orange' },
|
||||
];
|
||||
const renderCheckbox = props => <Checkbox.Group {...props} />;
|
||||
const renderCheckbox = (props: CheckboxGroupProps) => <Checkbox.Group {...props} />;
|
||||
const { container, rerender } = render(renderCheckbox({ options }));
|
||||
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(0);
|
||||
rerender(renderCheckbox({ options, value: 'Apple' }));
|
||||
rerender(renderCheckbox({ options, value: 'Apple' as unknown as CheckboxValueType[] }));
|
||||
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(1);
|
||||
});
|
||||
|
||||
@ -116,7 +120,7 @@ describe('CheckboxGroup', () => {
|
||||
<Checkbox key={2} value={2} />
|
||||
</Checkbox.Group>,
|
||||
);
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input'));
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith([2]);
|
||||
});
|
||||
@ -164,7 +168,7 @@ describe('CheckboxGroup', () => {
|
||||
const { container } = render(
|
||||
<Checkbox.Group>
|
||||
<Collapse bordered={false}>
|
||||
<Collapse.Panel header="test panel">
|
||||
<Collapse.Panel key="test panel" header="test panel">
|
||||
<div>
|
||||
<Checkbox value="1">item</Checkbox>
|
||||
</div>
|
||||
@ -174,11 +178,11 @@ describe('CheckboxGroup', () => {
|
||||
);
|
||||
|
||||
fireEvent.click(
|
||||
container.querySelector('.ant-collapse-item').querySelector('.ant-collapse-header'),
|
||||
container.querySelector('.ant-collapse-item')?.querySelector('.ant-collapse-header')!,
|
||||
);
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input'));
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
|
||||
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(1);
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input'));
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
|
||||
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(0);
|
||||
});
|
||||
|
||||
@ -210,12 +214,12 @@ describe('CheckboxGroup', () => {
|
||||
});
|
||||
|
||||
it('should get div ref', () => {
|
||||
const refCalls = [];
|
||||
const refCalls: HTMLDivElement[] = [];
|
||||
render(
|
||||
<Checkbox.Group
|
||||
options={['Apple', 'Pear', 'Orange']}
|
||||
ref={node => {
|
||||
refCalls.push(node);
|
||||
refCalls.push(node!);
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
@ -230,18 +234,18 @@ describe('CheckboxGroup', () => {
|
||||
<Checkbox.Group options={[1, 'Pear', 'Orange']} onChange={onChange} />,
|
||||
);
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input'));
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
|
||||
expect(onChange).toHaveBeenCalledWith([1]);
|
||||
});
|
||||
|
||||
it('should store latest checkbox value if changed', () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const Demo = () => {
|
||||
const [v, setV] = useState('');
|
||||
const Demo: React.FC = () => {
|
||||
const [v, setV] = useState<string>('');
|
||||
|
||||
React.useEffect(() => {
|
||||
setTimeout(setV('1'), 1000);
|
||||
setTimeout(setV('1') as unknown as TimerHandler, 1000);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@ -257,12 +261,12 @@ describe('CheckboxGroup', () => {
|
||||
};
|
||||
|
||||
const { container } = render(<Demo />);
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input'));
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
|
||||
expect(onChange).toHaveBeenCalledWith([]);
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input'));
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
|
||||
expect(onChange).toHaveBeenCalledWith(['length1']);
|
||||
fireEvent.change(container.querySelector('.ant-input'), { target: { value: '' } });
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input'));
|
||||
fireEvent.change(container.querySelector('.ant-input')!, { target: { value: '' } });
|
||||
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
|
||||
expect(onChange).toHaveBeenCalledWith(['A']);
|
||||
});
|
||||
});
|
@ -15519,11 +15519,10 @@ exports[`ConfigProvider components Form configProvider 1`] = `
|
||||
>
|
||||
<div
|
||||
class="config-form-item-explain config-show-help-appear config-show-help-appear-start config-show-help config-form-item-explain-connected"
|
||||
style="height: 0px; opacity: 0;"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="config-show-help-item-appear config-show-help-item-appear-start config-show-help-item config-form-item-explain-error"
|
||||
role="alert"
|
||||
style="height: 0px; opacity: 0;"
|
||||
>
|
||||
Bamboo is Light
|
||||
@ -15575,11 +15574,10 @@ exports[`ConfigProvider components Form configProvider componentDisabled 1`] = `
|
||||
>
|
||||
<div
|
||||
class="config-form-item-explain config-show-help-appear config-show-help-appear-start config-show-help config-form-item-explain-connected"
|
||||
style="height: 0px; opacity: 0;"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="config-show-help-item-appear config-show-help-item-appear-start config-show-help-item config-form-item-explain-error"
|
||||
role="alert"
|
||||
style="height: 0px; opacity: 0;"
|
||||
>
|
||||
Bamboo is Light
|
||||
@ -15630,11 +15628,10 @@ exports[`ConfigProvider components Form configProvider componentSize large 1`] =
|
||||
>
|
||||
<div
|
||||
class="config-form-item-explain config-show-help-appear config-show-help-appear-start config-show-help config-form-item-explain-connected"
|
||||
style="height: 0px; opacity: 0;"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="config-show-help-item-appear config-show-help-item-appear-start config-show-help-item config-form-item-explain-error"
|
||||
role="alert"
|
||||
style="height: 0px; opacity: 0;"
|
||||
>
|
||||
Bamboo is Light
|
||||
@ -15685,11 +15682,10 @@ exports[`ConfigProvider components Form configProvider componentSize middle 1`]
|
||||
>
|
||||
<div
|
||||
class="config-form-item-explain config-show-help-appear config-show-help-appear-start config-show-help config-form-item-explain-connected"
|
||||
style="height: 0px; opacity: 0;"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="config-show-help-item-appear config-show-help-item-appear-start config-show-help-item config-form-item-explain-error"
|
||||
role="alert"
|
||||
style="height: 0px; opacity: 0;"
|
||||
>
|
||||
Bamboo is Light
|
||||
@ -15740,11 +15736,10 @@ exports[`ConfigProvider components Form configProvider virtual and dropdownMatch
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-explain ant-show-help-appear ant-show-help-appear-start ant-show-help ant-form-item-explain-connected"
|
||||
style="height: 0px; opacity: 0;"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="ant-show-help-item-appear ant-show-help-item-appear-start ant-show-help-item ant-form-item-explain-error"
|
||||
role="alert"
|
||||
style="height: 0px; opacity: 0;"
|
||||
>
|
||||
Bamboo is Light
|
||||
@ -15795,11 +15790,10 @@ exports[`ConfigProvider components Form normal 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-explain ant-show-help-appear ant-show-help-appear-start ant-show-help ant-form-item-explain-connected"
|
||||
style="height: 0px; opacity: 0;"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="ant-show-help-item-appear ant-show-help-item-appear-start ant-show-help-item ant-form-item-explain-error"
|
||||
role="alert"
|
||||
style="height: 0px; opacity: 0;"
|
||||
>
|
||||
Bamboo is Light
|
||||
@ -15850,11 +15844,10 @@ exports[`ConfigProvider components Form prefixCls 1`] = `
|
||||
>
|
||||
<div
|
||||
class="prefix-Form-item-explain ant-show-help-appear ant-show-help-appear-start ant-show-help prefix-Form-item-explain-connected"
|
||||
style="height: 0px; opacity: 0;"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="ant-show-help-item-appear ant-show-help-item-appear-start ant-show-help-item prefix-Form-item-explain-error"
|
||||
role="alert"
|
||||
style="height: 0px; opacity: 0;"
|
||||
>
|
||||
Bamboo is Light
|
||||
|
@ -30,8 +30,8 @@ export function getStyle(globalPrefixCls: string, theme: Theme) {
|
||||
variables[`${type}-color-hover`] = colorPalettes[4];
|
||||
variables[`${type}-color-active`] = colorPalettes[6];
|
||||
variables[`${type}-color-outline`] = baseColor.clone().setAlpha(0.2).toRgbString();
|
||||
variables[`${type}-color-deprecated-bg`] = colorPalettes[1];
|
||||
variables[`${type}-color-deprecated-border`] = colorPalettes[3];
|
||||
variables[`${type}-color-deprecated-bg`] = colorPalettes[0];
|
||||
variables[`${type}-color-deprecated-border`] = colorPalettes[2];
|
||||
};
|
||||
|
||||
// ================ Primary Color ================
|
||||
|
@ -1,12 +1,13 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import type { DrawerProps } from '..';
|
||||
import Drawer 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 DrawerTest = ({ getContainer }) => (
|
||||
const DrawerTest: React.FC<DrawerProps> = ({ getContainer }) => (
|
||||
<div>
|
||||
<Drawer open width={400} getContainer={getContainer}>
|
||||
Here is content of Drawer
|
||||
@ -59,35 +60,37 @@ describe('Drawer', () => {
|
||||
});
|
||||
|
||||
it('getContainer return undefined', () => {
|
||||
const { container: wrapper, rerender } = render(<DrawerTest getContainer={() => undefined} />);
|
||||
const { container, rerender } = render(
|
||||
<DrawerTest getContainer={() => undefined as unknown as HTMLElement} />,
|
||||
);
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
|
||||
rerender(<DrawerTest getContainer={false} />);
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('render top drawer', () => {
|
||||
const { container: wrapper } = render(
|
||||
<Drawer open height={400} placement="top" getContainer={false}>
|
||||
const { container } = render(
|
||||
<Drawer visible height={400} placement="top" getContainer={false}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('have a title', () => {
|
||||
const { container: wrapper } = render(
|
||||
const { container } = render(
|
||||
<Drawer open title="Test Title" getContainer={false}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
||||
triggerMotion();
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('closable is false', () => {
|
@ -1,14 +1,15 @@
|
||||
import React from 'react';
|
||||
import type { DrawerProps } from '..';
|
||||
import Drawer from '..';
|
||||
import { act, fireEvent, render } from '../../../tests/utils';
|
||||
|
||||
describe('Drawer', () => {
|
||||
const getDrawer = props => (
|
||||
<Drawer open getContainer={false} {...props}>
|
||||
Here is content of Drawer
|
||||
</Drawer>
|
||||
);
|
||||
const DrawerTest: React.FC<DrawerProps> = props => (
|
||||
<Drawer open getContainer={false} {...props}>
|
||||
Here is content of Drawer
|
||||
</Drawer>
|
||||
);
|
||||
|
||||
describe('Drawer', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
@ -18,12 +19,12 @@ describe('Drawer', () => {
|
||||
});
|
||||
|
||||
it('render correctly', () => {
|
||||
const { container, asFragment, rerender } = render(getDrawer());
|
||||
const { container, asFragment, rerender } = render(<DrawerTest />);
|
||||
expect(container.querySelector('.ant-drawer-body')).toBeTruthy();
|
||||
|
||||
rerender(getDrawer({ open: false }));
|
||||
rerender(<DrawerTest open={false} />);
|
||||
|
||||
expect(container.querySelector('.ant-drawer-body').textContent).toEqual(
|
||||
expect(container.querySelector('.ant-drawer-body')?.textContent).toEqual(
|
||||
'Here is content of Drawer',
|
||||
);
|
||||
|
||||
@ -32,33 +33,33 @@ describe('Drawer', () => {
|
||||
|
||||
it('mask trigger onClose', () => {
|
||||
const onClose = jest.fn();
|
||||
const { container } = render(getDrawer({ onClose }));
|
||||
const { container } = render(<DrawerTest onClose={onClose} />);
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-drawer-mask'));
|
||||
fireEvent.click(container.querySelector('.ant-drawer-mask')!);
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('close button trigger onClose', () => {
|
||||
const onClose = jest.fn();
|
||||
const { container } = render(getDrawer({ onClose }));
|
||||
const { container } = render(<DrawerTest onClose={onClose} />);
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-drawer-close'));
|
||||
fireEvent.click(container.querySelector('.ant-drawer-close')!);
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('maskClosable no trigger onClose', () => {
|
||||
const onClose = jest.fn();
|
||||
const { container } = render(getDrawer({ onClose, maskClosable: false }));
|
||||
const { container } = render(<DrawerTest onClose={onClose} maskClosable={false} />);
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-drawer-mask'));
|
||||
fireEvent.click(container.querySelector('.ant-drawer-mask')!);
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('dom should be removed after close when destroyOnClose is true', () => {
|
||||
const { container, rerender } = render(getDrawer({ destroyOnClose: true }));
|
||||
const { container, rerender } = render(<DrawerTest destroyOnClose />);
|
||||
expect(container.querySelector('.ant-drawer')).toBeTruthy();
|
||||
|
||||
rerender(getDrawer({ destroyOnClose: true, open: false }));
|
||||
rerender(<DrawerTest destroyOnClose open={false} />);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
@ -67,53 +68,53 @@ describe('Drawer', () => {
|
||||
});
|
||||
|
||||
it('dom should be existed after close when destroyOnClose is false', () => {
|
||||
const { container, rerender } = render(getDrawer());
|
||||
const { container, rerender } = render(<DrawerTest />);
|
||||
expect(container.querySelector('.ant-drawer')).toBeTruthy();
|
||||
|
||||
rerender(getDrawer({ open: false }));
|
||||
rerender(<DrawerTest open={false} />);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content'));
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content')!);
|
||||
|
||||
expect(container.querySelector('.ant-drawer')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('dom should be existed after close twice when getContainer is false', () => {
|
||||
const { container, rerender } = render(getDrawer({ open: true, getContainer: false }));
|
||||
const { container, rerender } = render(<DrawerTest open getContainer={false} />);
|
||||
expect(container.querySelector('.ant-drawer-content')).toBeTruthy();
|
||||
|
||||
// Hide
|
||||
rerender(getDrawer({ open: false, getContainer: false }));
|
||||
rerender(<DrawerTest open={false} getContainer={false} />);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper'));
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper')!);
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper-hidden')).toBeTruthy();
|
||||
|
||||
// Show
|
||||
rerender(getDrawer({ open: true, getContainer: false }));
|
||||
rerender(<DrawerTest open getContainer={false} />);
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper')).toBeTruthy();
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper-hidden')).toBeFalsy();
|
||||
|
||||
// Hide
|
||||
rerender(getDrawer({ open: false, getContainer: false }));
|
||||
rerender(<DrawerTest open={false} getContainer={false} />);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper'));
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper')!);
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper-hidden')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('test afterOpenChange', async () => {
|
||||
it('test afterVisibleChange', async () => {
|
||||
const afterOpenChange = jest.fn();
|
||||
const { container, rerender } = render(getDrawer({ afterOpenChange, open: true }));
|
||||
rerender(getDrawer({ afterOpenChange, open: false }));
|
||||
const { container, rerender } = render(<DrawerTest open afterOpenChange={afterOpenChange} />);
|
||||
rerender(<DrawerTest open={false} afterOpenChange={afterOpenChange} />);
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper'));
|
||||
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper')!);
|
||||
|
||||
expect(afterOpenChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
@ -121,18 +122,18 @@ describe('Drawer', () => {
|
||||
it('should support children ref', () => {
|
||||
const fn = jest.fn();
|
||||
|
||||
const refCallback = ref => {
|
||||
const refCallback = (ref: HTMLDivElement | null) => {
|
||||
expect(typeof ref).toBe('object');
|
||||
fn();
|
||||
};
|
||||
|
||||
const RefDemo = () => {
|
||||
const ref = React.useRef();
|
||||
const RefDemo: React.FC = () => {
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open) {
|
||||
refCallback(ref.current);
|
||||
refCallback(ref.current!);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
@ -146,7 +147,7 @@ describe('Drawer', () => {
|
||||
);
|
||||
};
|
||||
const { container } = render(<RefDemo />);
|
||||
fireEvent.click(container.querySelector('a'));
|
||||
fireEvent.click(container.querySelector('a')!);
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -1,194 +0,0 @@
|
||||
import React from 'react';
|
||||
import Drawer from '..';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
|
||||
class MultiDrawer extends React.Component {
|
||||
state = { open: false, childrenDrawer: false, hasChildren: true };
|
||||
|
||||
showDrawer = () => {
|
||||
this.setState({
|
||||
open: true,
|
||||
hasChildren: true,
|
||||
});
|
||||
};
|
||||
|
||||
onClose = () => {
|
||||
this.setState({
|
||||
open: false,
|
||||
});
|
||||
};
|
||||
|
||||
showChildrenDrawer = () => {
|
||||
this.setState({
|
||||
childrenDrawer: true,
|
||||
hasChildren: true,
|
||||
});
|
||||
};
|
||||
|
||||
onChildrenDrawerClose = () => {
|
||||
this.setState({
|
||||
childrenDrawer: false,
|
||||
});
|
||||
};
|
||||
|
||||
onRemoveChildDrawer = () => {
|
||||
this.setState({
|
||||
hasChildren: false,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { childrenDrawer, open, hasChildren } = this.state;
|
||||
const { placement, push } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Button type="primary" id="open_drawer" onClick={this.showDrawer}>
|
||||
Open drawer
|
||||
</Button>
|
||||
<Button type="primary" id="remove_drawer" onClick={this.onRemoveChildDrawer}>
|
||||
rm child drawer
|
||||
</Button>
|
||||
<Drawer
|
||||
title="Multi-level drawer"
|
||||
className="test_drawer"
|
||||
width={520}
|
||||
onClose={this.onClose}
|
||||
getContainer={false}
|
||||
placement={placement}
|
||||
open={open}
|
||||
push={push}
|
||||
>
|
||||
<Button type="primary" id="open_two_drawer" onClick={this.showChildrenDrawer}>
|
||||
Two-level drawer
|
||||
</Button>
|
||||
{hasChildren && (
|
||||
<Drawer
|
||||
title="Two-level Drawer"
|
||||
width={320}
|
||||
className="Two-level"
|
||||
getContainer={false}
|
||||
placement={placement}
|
||||
onClose={this.onChildrenDrawerClose}
|
||||
open={childrenDrawer}
|
||||
>
|
||||
<div id="two_drawer_text">This is two-level drawer</div>
|
||||
</Drawer>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
borderTop: '1px solid #e8e8e8',
|
||||
padding: '10px 16px',
|
||||
textAlign: 'right',
|
||||
left: 0,
|
||||
background: '#fff',
|
||||
borderRadius: '0 0 4px 4px',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style={{
|
||||
marginRight: 8,
|
||||
}}
|
||||
onClick={this.onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={this.onClose} type="primary">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</Drawer>
|
||||
|
||||
<div className="childrenDrawer">{String(childrenDrawer)}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
describe('Drawer', () => {
|
||||
it('render right MultiDrawer', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer placement="right" />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(-180px)',
|
||||
});
|
||||
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
});
|
||||
|
||||
it('render left MultiDrawer', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer placement="left" />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(180px)',
|
||||
});
|
||||
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
fireEvent.click(wrapper.querySelector('.Two-level .ant-drawer-close'));
|
||||
expect(wrapper.querySelector('.childrenDrawer').innerHTML).toEqual('false');
|
||||
});
|
||||
|
||||
it('render top MultiDrawer', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer placement="top" />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateY(180px)',
|
||||
});
|
||||
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
});
|
||||
|
||||
it('render MultiDrawer is child in unmount', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer placement="top" mask={false} />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#remove_drawer'));
|
||||
|
||||
// Strange, testing-lib get wrong style in next branch.
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper').style).toEqual(
|
||||
expect.objectContaining({
|
||||
transform: '',
|
||||
}),
|
||||
);
|
||||
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateY(180px)',
|
||||
});
|
||||
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
});
|
||||
|
||||
it('custom MultiDrawer push distance', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer push={{ distance: 256 }} />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(-256px)',
|
||||
});
|
||||
});
|
||||
|
||||
it('custom MultiDrawer push with true', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer push />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(-180px)',
|
||||
});
|
||||
});
|
||||
|
||||
it('custom MultiDrawer push with false', () => {
|
||||
const { container: wrapper } = render(<MultiDrawer push={false} />);
|
||||
fireEvent.click(wrapper.querySelector('button#open_drawer'));
|
||||
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
|
||||
expect(wrapper.querySelector('.ant-drawer-content-wrapper').style).toEqual(
|
||||
expect.objectContaining({
|
||||
transform: '',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
193
components/drawer/__tests__/MultiDrawer.test.tsx
Normal file
193
components/drawer/__tests__/MultiDrawer.test.tsx
Normal file
@ -0,0 +1,193 @@
|
||||
import type { DrawerPopupProps } from 'rc-drawer/lib/DrawerPopup';
|
||||
import React from 'react';
|
||||
import Drawer from '..';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
|
||||
interface DrawerPropsType {
|
||||
push?: DrawerPopupProps['push'];
|
||||
placement?: DrawerPopupProps['placement'];
|
||||
}
|
||||
|
||||
interface DrawerStateType {
|
||||
open: boolean;
|
||||
hasChildren: boolean;
|
||||
childrenDrawer: boolean;
|
||||
}
|
||||
|
||||
class MultiDrawer extends React.Component<DrawerPropsType, DrawerStateType> {
|
||||
state = { open: false, childrenDrawer: false, hasChildren: true };
|
||||
|
||||
showDrawer = () => {
|
||||
this.setState({
|
||||
open: true,
|
||||
hasChildren: true,
|
||||
});
|
||||
};
|
||||
|
||||
onClose = () => {
|
||||
this.setState({
|
||||
open: false,
|
||||
});
|
||||
};
|
||||
|
||||
showChildrenDrawer = () => {
|
||||
this.setState({
|
||||
childrenDrawer: true,
|
||||
hasChildren: true,
|
||||
});
|
||||
};
|
||||
|
||||
onChildrenDrawerClose = () => {
|
||||
this.setState({
|
||||
childrenDrawer: false,
|
||||
});
|
||||
};
|
||||
|
||||
onRemoveChildDrawer = () => {
|
||||
this.setState({
|
||||
hasChildren: false,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { childrenDrawer, open, hasChildren } = this.state;
|
||||
const { placement, push } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Button type="primary" id="open_drawer" onClick={this.showDrawer}>
|
||||
Open drawer
|
||||
</Button>
|
||||
<Button type="primary" id="remove_drawer" onClick={this.onRemoveChildDrawer}>
|
||||
rm child drawer
|
||||
</Button>
|
||||
<Drawer
|
||||
title="Multi-level drawer"
|
||||
className="test_drawer"
|
||||
width={520}
|
||||
onClose={this.onClose}
|
||||
getContainer={false}
|
||||
placement={placement}
|
||||
open={open}
|
||||
push={push}
|
||||
>
|
||||
<Button type="primary" id="open_two_drawer" onClick={this.showChildrenDrawer}>
|
||||
Two-level drawer
|
||||
</Button>
|
||||
{hasChildren && (
|
||||
<Drawer
|
||||
title="Two-level Drawer"
|
||||
width={320}
|
||||
className="Two-level"
|
||||
getContainer={false}
|
||||
placement={placement}
|
||||
onClose={this.onChildrenDrawerClose}
|
||||
open={childrenDrawer}
|
||||
>
|
||||
<div id="two_drawer_text">This is two-level drawer</div>
|
||||
</Drawer>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
borderTop: '1px solid #e8e8e8',
|
||||
padding: '10px 16px',
|
||||
textAlign: 'right',
|
||||
left: 0,
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: '0 0 4px 4px',
|
||||
}}
|
||||
>
|
||||
<Button style={{ marginRight: 8 }} onClick={this.onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={this.onClose} type="primary">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</Drawer>
|
||||
|
||||
<div className="childrenDrawer">{String(childrenDrawer)}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
describe('Drawer', () => {
|
||||
it('render right MultiDrawer', () => {
|
||||
const { container } = render(<MultiDrawer placement="right" />);
|
||||
fireEvent.click(container.querySelector('button#open_drawer')!);
|
||||
fireEvent.click(container.querySelector('button#open_two_drawer')!);
|
||||
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(-180px)',
|
||||
});
|
||||
expect(container.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
});
|
||||
|
||||
it('render left MultiDrawer', () => {
|
||||
const { container } = render(<MultiDrawer placement="left" />);
|
||||
fireEvent.click(container.querySelector('button#open_drawer')!);
|
||||
fireEvent.click(container.querySelector('button#open_two_drawer')!);
|
||||
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(180px)',
|
||||
});
|
||||
expect(container.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
fireEvent.click(container.querySelector('.Two-level .ant-drawer-close')!);
|
||||
expect(container.querySelector('.childrenDrawer')?.innerHTML).toEqual('false');
|
||||
});
|
||||
|
||||
it('render top MultiDrawer', () => {
|
||||
const { container } = render(<MultiDrawer placement="top" />);
|
||||
fireEvent.click(container.querySelector('button#open_drawer')!);
|
||||
fireEvent.click(container.querySelector('button#open_two_drawer')!);
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateY(180px)',
|
||||
});
|
||||
expect(container.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
});
|
||||
|
||||
it('render MultiDrawer is child in unmount', () => {
|
||||
const mask = { mask: false };
|
||||
const { container } = render(<MultiDrawer placement="top" {...mask} />);
|
||||
fireEvent.click(container.querySelector('button#open_drawer')!);
|
||||
fireEvent.click(container.querySelector('button#open_two_drawer')!);
|
||||
fireEvent.click(container.querySelector('button#remove_drawer')!);
|
||||
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({ transform: '' });
|
||||
|
||||
fireEvent.click(container.querySelector('button#open_two_drawer')!);
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateY(180px)',
|
||||
});
|
||||
expect(container.querySelectorAll('#two_drawer_text').length).toBe(1);
|
||||
});
|
||||
|
||||
it('custom MultiDrawer push distance', () => {
|
||||
const { container } = render(<MultiDrawer push={{ distance: 256 }} />);
|
||||
fireEvent.click(container.querySelector('button#open_drawer')!);
|
||||
fireEvent.click(container.querySelector('button#open_two_drawer')!);
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(-256px)',
|
||||
});
|
||||
});
|
||||
|
||||
it('custom MultiDrawer push with true', () => {
|
||||
const { container } = render(<MultiDrawer push />);
|
||||
fireEvent.click(container.querySelector('button#open_drawer')!);
|
||||
fireEvent.click(container.querySelector('button#open_two_drawer')!);
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
|
||||
transform: 'translateX(-180px)',
|
||||
});
|
||||
});
|
||||
|
||||
it('custom MultiDrawer push with false', () => {
|
||||
const { container } = render(<MultiDrawer push={false} />);
|
||||
fireEvent.click(container.querySelector('button#open_drawer')!);
|
||||
fireEvent.click(container.querySelector('button#open_two_drawer')!);
|
||||
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({ transform: '' });
|
||||
});
|
||||
});
|
@ -600,6 +600,7 @@ HTMLCollection [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="name"
|
||||
placeholder="Please enter user name"
|
||||
@ -655,6 +656,7 @@ HTMLCollection [
|
||||
http://
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="url"
|
||||
placeholder="Please enter url"
|
||||
@ -710,6 +712,7 @@ HTMLCollection [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -724,6 +727,7 @@ HTMLCollection [
|
||||
aria-controls="owner_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="owner_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="owner"
|
||||
@ -887,6 +891,7 @@ HTMLCollection [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -901,6 +906,7 @@ HTMLCollection [
|
||||
aria-controls="type_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="type_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="type"
|
||||
@ -1069,6 +1075,7 @@ HTMLCollection [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -1083,6 +1090,7 @@ HTMLCollection [
|
||||
aria-controls="approver_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="approver_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="approver"
|
||||
@ -1246,6 +1254,7 @@ HTMLCollection [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-picker ant-picker-range"
|
||||
style="width: 100%;"
|
||||
>
|
||||
@ -2470,6 +2479,7 @@ HTMLCollection [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<textarea
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="description"
|
||||
placeholder="please enter url description"
|
||||
|
@ -47,21 +47,24 @@ describe('DropdownButton', () => {
|
||||
onOpenChange: () => {},
|
||||
};
|
||||
|
||||
render(<DropdownButton {...props} />);
|
||||
const { rerender } = render(<DropdownButton {...props} />);
|
||||
|
||||
Object.keys(props).forEach((key: keyof DropdownProps) => {
|
||||
expect(dropdownProps[key]).toBe(props[key]);
|
||||
});
|
||||
|
||||
rerender(<DropdownButton overlay={<div>123</div>} visible />);
|
||||
expect(dropdownProps.open).toBe(true);
|
||||
});
|
||||
|
||||
it("don't pass visible to Dropdown if it's not exits", () => {
|
||||
it("don't pass open to Dropdown if it's not exits", () => {
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item key="1">foo</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
render(<DropdownButton overlay={menu} />);
|
||||
expect('visible' in dropdownProps).toBe(false);
|
||||
expect('open' in dropdownProps).toBe(false);
|
||||
});
|
||||
|
||||
it('should support href like Button', () => {
|
||||
|
@ -95,6 +95,7 @@ describe('Dropdown', () => {
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("[antd: Dropdown] You are using 'topCenter'"),
|
||||
);
|
||||
error.mockRestore();
|
||||
});
|
||||
|
||||
// zombieJ: when replaced with react test lib, it may be mock fully content
|
||||
@ -166,4 +167,27 @@ describe('Dropdown', () => {
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('deprecated warning', () => {
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
const { rerender } = render(
|
||||
<Dropdown visible overlay={<div>menu</div>}>
|
||||
<a />
|
||||
</Dropdown>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Dropdown] `visible` is deprecated which will be removed in next major version, please use `open` instead.',
|
||||
);
|
||||
rerender(
|
||||
<Dropdown onVisibleChange={() => {}} overlay={<div>menu</div>}>
|
||||
<a />
|
||||
</Dropdown>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Dropdown] `onVisibleChange` is deprecated which will be removed in next major version, please use `onOpenChange` instead.',
|
||||
);
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
@ -27,8 +27,8 @@ When there are more than a few options to choose from, you can wrap them in a `D
|
||||
| overlayStyle | The style of the dropdown root element | CSSProperties | - | |
|
||||
| placement | Placement of popup menu: `bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
|
||||
| trigger | The trigger mode which executes the dropdown action. Note that hover can't be used on touchscreens | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| open | Whether the dropdown menu is currently open | boolean | - | |
|
||||
| onOpenChange | Called when the open state is changed. Not trigger when hidden by click item | (open: boolean) => void | - | |
|
||||
| open | Whether the dropdown menu is currently open | boolean | - | 4.23.0 |
|
||||
| onOpenChange | Called when the open state is changed. Not trigger when hidden by click item | (open: boolean) => void | - | 4.23.0 |
|
||||
|
||||
You should use [Menu](/components/menu/) as `overlay`. The menu items and dividers are also available by using `Menu.Item` and `Menu.Divider`.
|
||||
|
||||
@ -50,6 +50,6 @@ You should use [Menu](/components/menu/) as `overlay`. The menu items and divide
|
||||
| size | Size of the button, the same as [Button](/components/button/#API) | string | `default` | |
|
||||
| trigger | The trigger mode which executes the dropdown action | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| type | Type of the button, the same as [Button](/components/button/#API) | string | `default` | |
|
||||
| open | Whether the dropdown menu is currently open | boolean | - | |
|
||||
| open | Whether the dropdown menu is currently open | boolean | - | 4.23.0 |
|
||||
| onClick | The same as [Button](/components/button/#API): called when you click the button on the left | (event) => void | - | |
|
||||
| onOpenChange | Called when the open state is changed | (open: boolean) => void | - | |
|
||||
| onOpenChange | Called when the open state is changed | (open: boolean) => void | - | 4.23.0 |
|
||||
|
@ -31,8 +31,8 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
|
||||
| overlayStyle | 下拉根元素的样式 | CSSProperties | - | |
|
||||
| placement | 菜单弹出位置:`bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
|
||||
| trigger | 触发下拉的行为, 移动端不支持 hover | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| open | 菜单是否显示 | boolean | - | |
|
||||
| onOpenChange | 菜单显示状态改变时调用,参数为 `open`。点击菜单按钮导致的消失不会触发 | (open: boolean) => void | - | |
|
||||
| open | 菜单是否显示 | boolean | - | 4.23.0 |
|
||||
| onOpenChange | 菜单显示状态改变时调用,参数为 `visible`。点击菜单按钮导致的消失不会触发 | (open: boolean) => void | - | 4.23.0 |
|
||||
|
||||
`overlay` 菜单使用 [Menu](/components/menu/),还包括菜单项 `Menu.Item`,分割线 `Menu.Divider`。
|
||||
|
||||
@ -54,6 +54,6 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
|
||||
| size | 按钮大小,和 [Button](/components/button/#API) 一致 | string | `default` | |
|
||||
| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| type | 按钮类型,和 [Button](/components/button/#API) 一致 | string | `default` | |
|
||||
| open | 菜单是否显示 | boolean | - | |
|
||||
| open | 菜单是否显示 | boolean | - | 4.23.0 |
|
||||
| onClick | 点击左侧按钮的回调,和 [Button](/components/button/#API) 一致 | (event) => void | - | |
|
||||
| onOpenChange | 菜单显示状态改变时调用,参数为 `open` | (open: boolean) => void | - | |
|
||||
| onOpenChange | 菜单显示状态改变时调用,参数为 `visible` | (open: boolean) => void | - | 4.23.0 |
|
||||
|
@ -11,7 +11,7 @@ describe('Empty', () => {
|
||||
|
||||
it('image size should change', () => {
|
||||
const { container } = render(<Empty imageStyle={{ height: 20 }} />);
|
||||
expect(container.querySelector('.ant-empty-image').style.height).toBe('20px');
|
||||
expect(container.querySelector<HTMLDivElement>('.ant-empty-image')?.style.height).toBe('20px');
|
||||
});
|
||||
|
||||
it('description can be false', () => {
|
@ -30,6 +30,7 @@ function toErrorEntity(
|
||||
}
|
||||
|
||||
export interface ErrorListProps {
|
||||
fieldId?: string;
|
||||
help?: React.ReactNode;
|
||||
helpStatus?: ValidateStatus;
|
||||
errors?: React.ReactNode[];
|
||||
@ -44,6 +45,7 @@ export default function ErrorList({
|
||||
errors = EMPTY_LIST,
|
||||
warnings = EMPTY_LIST,
|
||||
className: rootClassName,
|
||||
fieldId,
|
||||
onVisibleChanged,
|
||||
}: ErrorListProps) {
|
||||
const { prefixCls } = React.useContext(FormItemPrefixContext);
|
||||
@ -72,6 +74,12 @@ export default function ErrorList({
|
||||
];
|
||||
}, [help, helpStatus, debounceErrors, debounceWarnings]);
|
||||
|
||||
const helpProps: { id?: string } = {};
|
||||
|
||||
if (fieldId) {
|
||||
helpProps.id = `${fieldId}_help`;
|
||||
}
|
||||
|
||||
return (
|
||||
<CSSMotion
|
||||
{...collapseMotion}
|
||||
@ -85,8 +93,10 @@ export default function ErrorList({
|
||||
|
||||
return (
|
||||
<div
|
||||
{...helpProps}
|
||||
className={classNames(baseClassName, holderClassName, rootClassName)}
|
||||
style={holderStyle}
|
||||
role="alert"
|
||||
>
|
||||
<CSSMotionList
|
||||
keys={fullKeyList}
|
||||
@ -106,7 +116,6 @@ export default function ErrorList({
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
role="alert"
|
||||
className={classNames(itemClassName, {
|
||||
[`${baseClassName}-${errorStatus}`]: errorStatus,
|
||||
})}
|
||||
|
@ -40,11 +40,16 @@ interface MemoInputProps {
|
||||
value: any;
|
||||
update: any;
|
||||
children: React.ReactNode;
|
||||
childProps: any[];
|
||||
}
|
||||
|
||||
const MemoInput = React.memo(
|
||||
({ children }: MemoInputProps) => children as JSX.Element,
|
||||
(prev, next) => prev.value === next.value && prev.update === next.update,
|
||||
(prev, next) =>
|
||||
prev.value === next.value &&
|
||||
prev.update === next.update &&
|
||||
prev.childProps.length === next.childProps.length &&
|
||||
prev.childProps.every((value, index) => value === next.childProps[index]),
|
||||
);
|
||||
|
||||
export interface FormItemProps<Values = any>
|
||||
@ -313,6 +318,25 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
|
||||
childProps.id = fieldId;
|
||||
}
|
||||
|
||||
if (props.help || mergedErrors.length > 0 || mergedWarnings.length > 0 || props.extra) {
|
||||
const describedbyArr = [];
|
||||
if (props.help || mergedErrors.length > 0) {
|
||||
describedbyArr.push(`${fieldId}_help`);
|
||||
}
|
||||
if (props.extra) {
|
||||
describedbyArr.push(`${fieldId}_extra`);
|
||||
}
|
||||
childProps['aria-describedby'] = describedbyArr.join(' ');
|
||||
}
|
||||
|
||||
if (mergedErrors.length > 0) {
|
||||
childProps['aria-invalid'] = 'true';
|
||||
}
|
||||
|
||||
if (isRequired) {
|
||||
childProps['aria-required'] = 'true';
|
||||
}
|
||||
|
||||
if (supportRef(children)) {
|
||||
childProps.ref = getItemRef(mergedName, children);
|
||||
}
|
||||
@ -330,8 +354,19 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
|
||||
};
|
||||
});
|
||||
|
||||
// List of props that need to be watched for changes -> if changes are detected in MemoInput -> rerender
|
||||
const watchingChildProps = [
|
||||
childProps['aria-required'],
|
||||
childProps['aria-invalid'],
|
||||
childProps['aria-describedby'],
|
||||
];
|
||||
|
||||
childNode = (
|
||||
<MemoInput value={mergedControl[props.valuePropName || 'value']} update={children}>
|
||||
<MemoInput
|
||||
value={mergedControl[props.valuePropName || 'value']}
|
||||
update={children}
|
||||
childProps={watchingChildProps}
|
||||
>
|
||||
{cloneElement(children, childProps)}
|
||||
</MemoInput>
|
||||
);
|
||||
|
@ -32,6 +32,7 @@ export interface FormItemInputProps {
|
||||
extra?: React.ReactNode;
|
||||
status?: ValidateStatus;
|
||||
help?: React.ReactNode;
|
||||
fieldId?: string;
|
||||
}
|
||||
|
||||
const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = props => {
|
||||
@ -45,6 +46,7 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
|
||||
_internalItemRender: formItemRender,
|
||||
extra,
|
||||
help,
|
||||
fieldId,
|
||||
marginBottom,
|
||||
onErrorVisibleChanged,
|
||||
} = props;
|
||||
@ -72,6 +74,7 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
|
||||
<div style={{ display: 'flex', flexWrap: 'nowrap' }}>
|
||||
<FormItemPrefixContext.Provider value={formItemContext}>
|
||||
<ErrorList
|
||||
fieldId={fieldId}
|
||||
errors={errors}
|
||||
warnings={warnings}
|
||||
help={help}
|
||||
@ -84,9 +87,19 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const extraProps: { id?: string } = {};
|
||||
|
||||
if (fieldId) {
|
||||
extraProps.id = `${fieldId}_extra`;
|
||||
}
|
||||
|
||||
// If extra = 0, && will goes wrong
|
||||
// 0&&error -> 0
|
||||
const extraDom = extra ? <div className={`${baseClassName}-extra`}>{extra}</div> : null;
|
||||
const extraDom = extra ? (
|
||||
<div {...extraProps} className={`${baseClassName}-extra`}>
|
||||
{extra}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const dom =
|
||||
formItemRender && formItemRender.mark === 'pro_table_render' && formItemRender.render ? (
|
||||
|
@ -41,6 +41,7 @@ exports[`renders ./components/form/demo/advanced-search.md extend context correc
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="advanced_search_field-0"
|
||||
placeholder="placeholder"
|
||||
@ -84,6 +85,7 @@ exports[`renders ./components/form/demo/advanced-search.md extend context correc
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -98,6 +100,7 @@ exports[`renders ./components/form/demo/advanced-search.md extend context correc
|
||||
aria-controls="advanced_search_field-1_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="advanced_search_field-1_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="advanced_search_field-1"
|
||||
@ -262,6 +265,7 @@ exports[`renders ./components/form/demo/advanced-search.md extend context correc
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="advanced_search_field-2"
|
||||
placeholder="placeholder"
|
||||
@ -305,6 +309,7 @@ exports[`renders ./components/form/demo/advanced-search.md extend context correc
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="advanced_search_field-3"
|
||||
placeholder="placeholder"
|
||||
@ -348,6 +353,7 @@ exports[`renders ./components/form/demo/advanced-search.md extend context correc
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -362,6 +368,7 @@ exports[`renders ./components/form/demo/advanced-search.md extend context correc
|
||||
aria-controls="advanced_search_field-4_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="advanced_search_field-4_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="advanced_search_field-4"
|
||||
@ -526,6 +533,7 @@ exports[`renders ./components/form/demo/advanced-search.md extend context correc
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="advanced_search_field-5"
|
||||
placeholder="placeholder"
|
||||
@ -631,6 +639,7 @@ exports[`renders ./components/form/demo/basic.md extend context correctly 1`] =
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="basic_username"
|
||||
type="text"
|
||||
@ -672,6 +681,7 @@ exports[`renders ./components/form/demo/basic.md extend context correctly 1`] =
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="basic_password"
|
||||
type="password"
|
||||
@ -815,6 +825,7 @@ Array [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="basic_username"
|
||||
type="text"
|
||||
@ -856,6 +867,7 @@ Array [
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="basic_password"
|
||||
type="password"
|
||||
@ -1184,6 +1196,7 @@ Array [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="responsive_username"
|
||||
type="text"
|
||||
@ -1225,6 +1238,7 @@ Array [
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="responsive_password"
|
||||
type="password"
|
||||
@ -1601,6 +1615,7 @@ exports[`renders ./components/form/demo/control-hooks.md extend context correctl
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="control-hooks_note"
|
||||
type="text"
|
||||
@ -1638,6 +1653,7 @@ exports[`renders ./components/form/demo/control-hooks.md extend context correctl
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -1652,6 +1668,7 @@ exports[`renders ./components/form/demo/control-hooks.md extend context correctl
|
||||
aria-controls="control-hooks_gender_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="control-hooks_gender_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="control-hooks_gender"
|
||||
@ -1879,6 +1896,7 @@ exports[`renders ./components/form/demo/control-ref.md extend context correctly
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="control-ref_note"
|
||||
type="text"
|
||||
@ -1916,6 +1934,7 @@ exports[`renders ./components/form/demo/control-ref.md extend context correctly
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -1930,6 +1949,7 @@ exports[`renders ./components/form/demo/control-ref.md extend context correctly
|
||||
aria-controls="control-ref_gender_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="control-ref_gender_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="control-ref_gender"
|
||||
@ -5972,6 +5992,7 @@ exports[`renders ./components/form/demo/dynamic-form-items-complex.md extend con
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -5986,6 +6007,7 @@ exports[`renders ./components/form/demo/dynamic-form-items-complex.md extend con
|
||||
aria-controls="dynamic_form_nest_item_area_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="dynamic_form_nest_item_area_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="dynamic_form_nest_item_area"
|
||||
@ -6347,6 +6369,7 @@ exports[`renders ./components/form/demo/dynamic-rule.md extend context correctly
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="dynamic_rule_username"
|
||||
placeholder="Please input your name"
|
||||
@ -6497,6 +6520,7 @@ exports[`renders ./components/form/demo/form-context.md extend context correctly
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="basicForm_group"
|
||||
type="text"
|
||||
@ -6648,6 +6672,7 @@ Array [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="global_state_username"
|
||||
type="text"
|
||||
@ -6721,6 +6746,7 @@ exports[`renders ./components/form/demo/inline-login.md extend context correctly
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="horizontal_login_username"
|
||||
placeholder="Username"
|
||||
@ -6775,6 +6801,7 @@ exports[`renders ./components/form/demo/inline-login.md extend context correctly
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="horizontal_login_password"
|
||||
placeholder="Password"
|
||||
@ -7209,6 +7236,7 @@ exports[`renders ./components/form/demo/layout-can-wrap.md extend context correc
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="wrap_username"
|
||||
type="text"
|
||||
@ -7248,6 +7276,7 @@ exports[`renders ./components/form/demo/layout-can-wrap.md extend context correc
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="wrap_password"
|
||||
type="text"
|
||||
@ -7333,6 +7362,7 @@ exports[`renders ./components/form/demo/nest-messages.md extend context correctl
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="nest-messages_user_name"
|
||||
type="text"
|
||||
@ -7635,6 +7665,7 @@ exports[`renders ./components/form/demo/normal-login.md extend context correctly
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="normal_login_username"
|
||||
placeholder="Username"
|
||||
@ -7689,6 +7720,7 @@ exports[`renders ./components/form/demo/normal-login.md extend context correctly
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="normal_login_password"
|
||||
placeholder="Password"
|
||||
@ -7902,6 +7934,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_email"
|
||||
type="text"
|
||||
@ -7943,6 +7976,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_password"
|
||||
type="password"
|
||||
@ -8012,6 +8046,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_confirm"
|
||||
type="password"
|
||||
@ -8124,6 +8159,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_nickname"
|
||||
type="text"
|
||||
@ -8161,6 +8197,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-cascader ant-select-in-form-item ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -8174,6 +8211,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
aria-controls="register_residence_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="register_residence_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="register_residence"
|
||||
@ -8521,6 +8559,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
</div>
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_phone"
|
||||
type="text"
|
||||
@ -8631,6 +8670,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
id="register_donation"
|
||||
@ -8818,6 +8858,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
>
|
||||
<div
|
||||
@ -8832,6 +8873,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
aria-controls="register_website_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="register_website_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-input ant-select-selection-search-input"
|
||||
id="register_website"
|
||||
@ -8897,6 +8939,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
data-count="0 / 100"
|
||||
>
|
||||
<textarea
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_intro"
|
||||
/>
|
||||
@ -8933,6 +8976,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -8947,6 +8991,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
aria-controls="register_gender_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="register_gender_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="register_gender"
|
||||
@ -9129,6 +9174,7 @@ exports[`renders ./components/form/demo/register.md extend context correctly 1`]
|
||||
style="padding-left:4px;padding-right:4px"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_captcha"
|
||||
type="text"
|
||||
@ -10986,6 +11032,7 @@ exports[`renders ./components/form/demo/time-related-controls.md extend context
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
id="time_related_controls_date-picker"
|
||||
placeholder="Select date"
|
||||
@ -11611,6 +11658,7 @@ exports[`renders ./components/form/demo/time-related-controls.md extend context
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
id="time_related_controls_date-time-picker"
|
||||
placeholder="Select date"
|
||||
@ -13589,6 +13637,7 @@ exports[`renders ./components/form/demo/time-related-controls.md extend context
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
id="time_related_controls_month-picker"
|
||||
placeholder="Select month"
|
||||
@ -13844,6 +13893,7 @@ exports[`renders ./components/form/demo/time-related-controls.md extend context
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-picker ant-picker-range"
|
||||
>
|
||||
<div
|
||||
@ -15054,6 +15104,7 @@ exports[`renders ./components/form/demo/time-related-controls.md extend context
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-picker ant-picker-range"
|
||||
>
|
||||
<div
|
||||
@ -17085,6 +17136,7 @@ exports[`renders ./components/form/demo/time-related-controls.md extend context
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
id="time_related_controls_time-picker"
|
||||
placeholder="Select time"
|
||||
@ -18748,6 +18800,7 @@ exports[`renders ./components/form/demo/validate-other.md extend context correct
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-has-feedback ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -18762,6 +18815,7 @@ exports[`renders ./components/form/demo/validate-other.md extend context correct
|
||||
aria-controls="validate_other_select_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="validate_other_select_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="validate_other_select"
|
||||
@ -18920,6 +18974,7 @@ exports[`renders ./components/form/demo/validate-other.md extend context correct
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-multiple ant-select-show-search"
|
||||
>
|
||||
<div
|
||||
@ -18942,6 +18997,7 @@ exports[`renders ./components/form/demo/validate-other.md extend context correct
|
||||
aria-controls="validate_other_select-multiple_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="validate_other_select-multiple_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="validate_other_select-multiple"
|
||||
@ -19476,6 +19532,7 @@ exports[`renders ./components/form/demo/validate-other.md extend context correct
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
id="validate_other_radio-button"
|
||||
>
|
||||
@ -20095,6 +20152,7 @@ exports[`renders ./components/form/demo/validate-other.md extend context correct
|
||||
>
|
||||
<input
|
||||
accept=""
|
||||
aria-describedby="validate_other_upload_extra"
|
||||
id="validate_other_upload"
|
||||
style="display:none"
|
||||
type="file"
|
||||
@ -20136,6 +20194,7 @@ exports[`renders ./components/form/demo/validate-other.md extend context correct
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item-extra"
|
||||
id="validate_other_upload_extra"
|
||||
>
|
||||
longgggggggggggggggggggggggggggggggggg
|
||||
</div>
|
||||
@ -26409,6 +26468,7 @@ exports[`renders ./components/form/demo/warning-only.md extend context correctly
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="url"
|
||||
placeholder="input placeholder"
|
||||
|
@ -41,6 +41,7 @@ exports[`renders ./components/form/demo/advanced-search.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="advanced_search_field-0"
|
||||
placeholder="placeholder"
|
||||
@ -84,6 +85,7 @@ exports[`renders ./components/form/demo/advanced-search.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -98,6 +100,7 @@ exports[`renders ./components/form/demo/advanced-search.md correctly 1`] = `
|
||||
aria-controls="advanced_search_field-1_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="advanced_search_field-1_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="advanced_search_field-1"
|
||||
@ -180,6 +183,7 @@ exports[`renders ./components/form/demo/advanced-search.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="advanced_search_field-2"
|
||||
placeholder="placeholder"
|
||||
@ -223,6 +227,7 @@ exports[`renders ./components/form/demo/advanced-search.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="advanced_search_field-3"
|
||||
placeholder="placeholder"
|
||||
@ -266,6 +271,7 @@ exports[`renders ./components/form/demo/advanced-search.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -280,6 +286,7 @@ exports[`renders ./components/form/demo/advanced-search.md correctly 1`] = `
|
||||
aria-controls="advanced_search_field-4_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="advanced_search_field-4_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="advanced_search_field-4"
|
||||
@ -362,6 +369,7 @@ exports[`renders ./components/form/demo/advanced-search.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="advanced_search_field-5"
|
||||
placeholder="placeholder"
|
||||
@ -467,6 +475,7 @@ exports[`renders ./components/form/demo/basic.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="basic_username"
|
||||
type="text"
|
||||
@ -508,6 +517,7 @@ exports[`renders ./components/form/demo/basic.md correctly 1`] = `
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="basic_password"
|
||||
type="password"
|
||||
@ -651,6 +661,7 @@ Array [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="basic_username"
|
||||
type="text"
|
||||
@ -692,6 +703,7 @@ Array [
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="basic_password"
|
||||
type="password"
|
||||
@ -913,6 +925,7 @@ Array [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="responsive_username"
|
||||
type="text"
|
||||
@ -954,6 +967,7 @@ Array [
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="responsive_password"
|
||||
type="password"
|
||||
@ -1223,6 +1237,7 @@ exports[`renders ./components/form/demo/control-hooks.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="control-hooks_note"
|
||||
type="text"
|
||||
@ -1260,6 +1275,7 @@ exports[`renders ./components/form/demo/control-hooks.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -1274,6 +1290,7 @@ exports[`renders ./components/form/demo/control-hooks.md correctly 1`] = `
|
||||
aria-controls="control-hooks_gender_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="control-hooks_gender_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="control-hooks_gender"
|
||||
@ -1402,6 +1419,7 @@ exports[`renders ./components/form/demo/control-ref.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="control-ref_note"
|
||||
type="text"
|
||||
@ -1439,6 +1457,7 @@ exports[`renders ./components/form/demo/control-ref.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -1453,6 +1472,7 @@ exports[`renders ./components/form/demo/control-ref.md correctly 1`] = `
|
||||
aria-controls="control-ref_gender_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="control-ref_gender_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="control-ref_gender"
|
||||
@ -3455,6 +3475,7 @@ exports[`renders ./components/form/demo/dynamic-form-items-complex.md correctly
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -3469,6 +3490,7 @@ exports[`renders ./components/form/demo/dynamic-form-items-complex.md correctly
|
||||
aria-controls="dynamic_form_nest_item_area_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="dynamic_form_nest_item_area_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="dynamic_form_nest_item_area"
|
||||
@ -3748,6 +3770,7 @@ exports[`renders ./components/form/demo/dynamic-rule.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="dynamic_rule_username"
|
||||
placeholder="Please input your name"
|
||||
@ -3898,6 +3921,7 @@ exports[`renders ./components/form/demo/form-context.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="basicForm_group"
|
||||
type="text"
|
||||
@ -4049,6 +4073,7 @@ Array [
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="global_state_username"
|
||||
type="text"
|
||||
@ -4122,6 +4147,7 @@ exports[`renders ./components/form/demo/inline-login.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="horizontal_login_username"
|
||||
placeholder="Username"
|
||||
@ -4176,6 +4202,7 @@ exports[`renders ./components/form/demo/inline-login.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="horizontal_login_password"
|
||||
placeholder="Password"
|
||||
@ -4610,6 +4637,7 @@ exports[`renders ./components/form/demo/layout-can-wrap.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="wrap_username"
|
||||
type="text"
|
||||
@ -4649,6 +4677,7 @@ exports[`renders ./components/form/demo/layout-can-wrap.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="wrap_password"
|
||||
type="text"
|
||||
@ -4734,6 +4763,7 @@ exports[`renders ./components/form/demo/nest-messages.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="nest-messages_user_name"
|
||||
type="text"
|
||||
@ -5036,6 +5066,7 @@ exports[`renders ./components/form/demo/normal-login.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="normal_login_username"
|
||||
placeholder="Username"
|
||||
@ -5090,6 +5121,7 @@ exports[`renders ./components/form/demo/normal-login.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="normal_login_password"
|
||||
placeholder="Password"
|
||||
@ -5303,6 +5335,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_email"
|
||||
type="text"
|
||||
@ -5344,6 +5377,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_password"
|
||||
type="password"
|
||||
@ -5413,6 +5447,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_confirm"
|
||||
type="password"
|
||||
@ -5501,6 +5536,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_nickname"
|
||||
type="text"
|
||||
@ -5538,6 +5574,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-cascader ant-select-in-form-item ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -5551,6 +5588,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
aria-controls="register_residence_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="register_residence_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="register_residence"
|
||||
@ -5726,6 +5764,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
</div>
|
||||
</span>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_phone"
|
||||
type="text"
|
||||
@ -5836,6 +5875,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
id="register_donation"
|
||||
@ -5941,6 +5981,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
>
|
||||
<div
|
||||
@ -5955,6 +5996,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
aria-controls="register_website_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="register_website_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-input ant-select-selection-search-input"
|
||||
id="register_website"
|
||||
@ -6006,6 +6048,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
data-count="0 / 100"
|
||||
>
|
||||
<textarea
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_intro"
|
||||
/>
|
||||
@ -6042,6 +6085,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -6056,6 +6100,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
aria-controls="register_gender_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="register_gender_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="register_gender"
|
||||
@ -6139,6 +6184,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
style="padding-left:4px;padding-right:4px"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="register_captcha"
|
||||
type="text"
|
||||
@ -7186,6 +7232,7 @@ exports[`renders ./components/form/demo/time-related-controls.md correctly 1`] =
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
id="time_related_controls_date-picker"
|
||||
placeholder="Select date"
|
||||
@ -7257,6 +7304,7 @@ exports[`renders ./components/form/demo/time-related-controls.md correctly 1`] =
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
id="time_related_controls_date-time-picker"
|
||||
placeholder="Select date"
|
||||
@ -7328,6 +7376,7 @@ exports[`renders ./components/form/demo/time-related-controls.md correctly 1`] =
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
id="time_related_controls_month-picker"
|
||||
placeholder="Select month"
|
||||
@ -7393,6 +7442,7 @@ exports[`renders ./components/form/demo/time-related-controls.md correctly 1`] =
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-picker ant-picker-range"
|
||||
>
|
||||
<div
|
||||
@ -7506,6 +7556,7 @@ exports[`renders ./components/form/demo/time-related-controls.md correctly 1`] =
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-picker ant-picker-range"
|
||||
>
|
||||
<div
|
||||
@ -7625,6 +7676,7 @@ exports[`renders ./components/form/demo/time-related-controls.md correctly 1`] =
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
id="time_related_controls_time-picker"
|
||||
placeholder="Select time"
|
||||
@ -7923,6 +7975,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-has-feedback ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
@ -7937,6 +7990,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
aria-controls="validate_other_select_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="validate_other_select_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="validate_other_select"
|
||||
@ -8013,6 +8067,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-multiple ant-select-show-search"
|
||||
>
|
||||
<div
|
||||
@ -8035,6 +8090,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
aria-controls="validate_other_select-multiple_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="validate_other_select-multiple_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="validate_other_select-multiple"
|
||||
@ -8464,6 +8520,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
id="validate_other_radio-button"
|
||||
>
|
||||
@ -9083,6 +9140,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
>
|
||||
<input
|
||||
accept=""
|
||||
aria-describedby="validate_other_upload_extra"
|
||||
id="validate_other_upload"
|
||||
style="display:none"
|
||||
type="file"
|
||||
@ -9124,6 +9182,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item-extra"
|
||||
id="validate_other_upload_extra"
|
||||
>
|
||||
longgggggggggggggggggggggggggggggggggg
|
||||
</div>
|
||||
@ -11066,6 +11125,7 @@ exports[`renders ./components/form/demo/warning-only.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="url"
|
||||
placeholder="input placeholder"
|
@ -203,6 +203,158 @@ describe('Form', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('input element should have the prop aria-describedby pointing to the help id when there is a help message', () => {
|
||||
const wrapper = mount(
|
||||
<Form>
|
||||
<Form.Item name="test" help="This is a help">
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
const input = wrapper.find('input');
|
||||
expect(input.prop('aria-describedby')).toBe('test_help');
|
||||
const help = wrapper.find('.ant-form-item-explain');
|
||||
expect(help.prop('id')).toBe('test_help');
|
||||
});
|
||||
|
||||
it('input element should not have the prop aria-describedby pointing to the help id when there is a help message and name is not defined', () => {
|
||||
const wrapper = mount(
|
||||
<Form>
|
||||
<Form.Item help="This is a help">
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
const input = wrapper.find('input');
|
||||
expect(input.prop('aria-describedby')).toBeUndefined();
|
||||
const help = wrapper.find('.ant-form-item-explain');
|
||||
expect(help.prop('id')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('input element should have the prop aria-describedby concatenated with the form name pointing to the help id when there is a help message', () => {
|
||||
const wrapper = mount(
|
||||
<Form name="form">
|
||||
<Form.Item name="test" help="This is a help">
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
const input = wrapper.find('input');
|
||||
expect(input.prop('aria-describedby')).toBe('form_test_help');
|
||||
const help = wrapper.find('.ant-form-item-explain');
|
||||
expect(help.prop('id')).toBe('form_test_help');
|
||||
});
|
||||
|
||||
it('input element should have the prop aria-describedby pointing to the help id when there are errors', async () => {
|
||||
const wrapper = mount(
|
||||
<Form>
|
||||
<Form.Item name="test" rules={[{ len: 3 }, { type: 'number' }]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
const input = wrapper.find('input');
|
||||
input.simulate('change', { target: { value: 'Invalid number' } });
|
||||
await sleep(800);
|
||||
wrapper.update();
|
||||
|
||||
const inputChanged = wrapper.find('input');
|
||||
expect(inputChanged.prop('aria-describedby')).toBe('test_help');
|
||||
const help = wrapper.find('.ant-form-item-explain');
|
||||
expect(help.prop('id')).toBe('test_help');
|
||||
});
|
||||
|
||||
it('input element should have the prop aria-invalid when there are errors', async () => {
|
||||
const wrapper = mount(
|
||||
<Form>
|
||||
<Form.Item name="test" rules={[{ len: 3 }, { type: 'number' }]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
const input = wrapper.find('input');
|
||||
input.simulate('change', { target: { value: 'Invalid number' } });
|
||||
await sleep(800);
|
||||
wrapper.update();
|
||||
|
||||
const inputChanged = wrapper.find('input');
|
||||
expect(inputChanged.prop('aria-invalid')).toBe('true');
|
||||
});
|
||||
|
||||
it('input element should have the prop aria-required when the prop `required` is true', async () => {
|
||||
const wrapper = mount(
|
||||
<Form>
|
||||
<Form.Item name="test" required>
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
const input = wrapper.find('input');
|
||||
expect(input.prop('aria-required')).toBe('true');
|
||||
});
|
||||
|
||||
it('input element should have the prop aria-required when there is a rule with required', async () => {
|
||||
const wrapper = mount(
|
||||
<Form>
|
||||
<Form.Item name="test" rules={[{ required: true }]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
const input = wrapper.find('input');
|
||||
expect(input.prop('aria-required')).toBe('true');
|
||||
});
|
||||
|
||||
it('input element should have the prop aria-describedby pointing to the extra id when there is a extra message', () => {
|
||||
const wrapper = mount(
|
||||
<Form>
|
||||
<Form.Item name="test" extra="This is a extra message">
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
const input = wrapper.find('input');
|
||||
expect(input.prop('aria-describedby')).toBe('test_extra');
|
||||
const extra = wrapper.find('.ant-form-item-extra');
|
||||
expect(extra.prop('id')).toBe('test_extra');
|
||||
});
|
||||
|
||||
it('input element should not have the prop aria-describedby pointing to the extra id when there is a extra message and name is not defined', () => {
|
||||
const wrapper = mount(
|
||||
<Form>
|
||||
<Form.Item extra="This is a extra message">
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
const input = wrapper.find('input');
|
||||
expect(input.prop('aria-describedby')).toBeUndefined();
|
||||
const extra = wrapper.find('.ant-form-item-extra');
|
||||
expect(extra.prop('id')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('input element should have the prop aria-describedby pointing to the help and extra id when there is a help and extra message', () => {
|
||||
const wrapper = mount(
|
||||
<Form>
|
||||
<Form.Item name="test" help="This is a help" extra="This is a extra message">
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
const input = wrapper.find('input');
|
||||
expect(input.prop('aria-describedby')).toBe('test_help test_extra');
|
||||
});
|
||||
|
||||
describe('scrollToField', () => {
|
||||
function test(name, genForm) {
|
||||
it(name, () => {
|
||||
@ -710,9 +862,7 @@ describe('Form', () => {
|
||||
await sleep(100);
|
||||
wrapper.update();
|
||||
await sleep(100);
|
||||
expect(wrapper.find('.ant-form-item-explain div').getDOMNode().getAttribute('role')).toBe(
|
||||
'alert',
|
||||
);
|
||||
expect(wrapper.find('.ant-form-item-explain').getDOMNode().getAttribute('role')).toBe('alert');
|
||||
});
|
||||
|
||||
it('return same form instance', () => {
|
||||
|
17
components/icon/__tests__/index.test.tsx
Normal file
17
components/icon/__tests__/index.test.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import Icon from '..';
|
||||
import { render } from '../../../tests/utils';
|
||||
|
||||
// v3 兼容性测试
|
||||
describe('Icon', () => {
|
||||
it('should render Icon', () => {
|
||||
const { container } = render(<Icon />);
|
||||
expect(container.firstChild).toBe(null);
|
||||
});
|
||||
|
||||
it('should throw Error', () => {
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<Icon />);
|
||||
expect(errSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
import warning from '../_util/warning';
|
||||
|
||||
const Icon = () => {
|
||||
const Icon: React.FC = () => {
|
||||
warning(false, 'Icon', 'Empty Icon');
|
||||
return null;
|
||||
};
|
||||
|
@ -11,60 +11,58 @@ describe('Image', () => {
|
||||
mountTest(Image);
|
||||
rtlTest(Image);
|
||||
it('Image preview props set false', () => {
|
||||
const { container: wrapper } = render(<Image src={src} preview={false} />);
|
||||
const { container } = render(<Image src={src} preview={false} />);
|
||||
|
||||
fireEvent.click(wrapper.querySelector('.ant-image'));
|
||||
expect(wrapper.querySelector('.ant-image-preview-root')).toBe(null);
|
||||
fireEvent.click(container.querySelector('.ant-image')!);
|
||||
expect(container.querySelector('.ant-image-preview-root')).toBe(null);
|
||||
});
|
||||
it('Group preview props set false', () => {
|
||||
const { container: wrapper } = render(
|
||||
const { container } = render(
|
||||
<Image.PreviewGroup preview={false}>
|
||||
<Image src={src} />
|
||||
</Image.PreviewGroup>,
|
||||
);
|
||||
|
||||
fireEvent.click(wrapper.querySelector('.ant-image'));
|
||||
fireEvent.click(container.querySelector('.ant-image')!);
|
||||
|
||||
expect(wrapper.querySelector('.ant-image-preview-root')).toBe(null);
|
||||
expect(container.querySelector('.ant-image-preview-root')).toBe(null);
|
||||
});
|
||||
|
||||
it('Default preview props', () => {
|
||||
const { container: wrapper, baseElement } = render(
|
||||
<Image src={src} preview={{ visible: true }} />,
|
||||
);
|
||||
const { container, baseElement } = render(<Image src={src} preview={{ visible: true }} />);
|
||||
|
||||
fireEvent.click(wrapper.querySelector('.ant-image'));
|
||||
fireEvent.click(container.querySelector('.ant-image')!);
|
||||
|
||||
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('ant-fade');
|
||||
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('ant-zoom');
|
||||
});
|
||||
it('Default Group preview props', () => {
|
||||
const { container: wrapper, baseElement } = render(
|
||||
const { container, baseElement } = render(
|
||||
<Image.PreviewGroup preview={{ visible: true }}>
|
||||
<Image src={src} />
|
||||
</Image.PreviewGroup>,
|
||||
);
|
||||
|
||||
fireEvent.click(wrapper.querySelector('.ant-image'));
|
||||
fireEvent.click(container.querySelector('.ant-image')!);
|
||||
|
||||
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('ant-fade');
|
||||
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('ant-zoom');
|
||||
});
|
||||
it('Customize preview props', () => {
|
||||
const { container: wrapper, baseElement } = render(
|
||||
const { container, baseElement } = render(
|
||||
<Image
|
||||
src={src}
|
||||
preview={{ visible: true, transitionName: 'abc', maskTransitionName: 'def' }}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(wrapper.querySelector('.ant-image'));
|
||||
fireEvent.click(container.querySelector('.ant-image')!);
|
||||
|
||||
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('abc');
|
||||
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('def');
|
||||
});
|
||||
it('Customize Group preview props', () => {
|
||||
const { container: wrapper, baseElement } = render(
|
||||
const { container, baseElement } = render(
|
||||
<Image.PreviewGroup
|
||||
preview={{ visible: true, transitionName: 'abc', maskTransitionName: 'def' }}
|
||||
>
|
||||
@ -72,22 +70,21 @@ describe('Image', () => {
|
||||
</Image.PreviewGroup>,
|
||||
);
|
||||
|
||||
fireEvent.click(wrapper.querySelector('.ant-image'));
|
||||
fireEvent.click(container.querySelector('.ant-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(
|
||||
const { container, baseElement } = render(
|
||||
<>
|
||||
<div className="container" />
|
||||
<ConfigProvider getPopupContainer={() => document.querySelector('.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);
|
||||
fireEvent.click(container.querySelector('.ant-image')!);
|
||||
expect(baseElement.querySelector('.container')?.children.length).not.toBe(0);
|
||||
});
|
||||
});
|
@ -15,18 +15,18 @@ describe('InputNumber', () => {
|
||||
it('should return null when blur a empty input number', () => {
|
||||
const onChange = jest.fn();
|
||||
const { container } = render(<InputNumber defaultValue="1" onChange={onChange} />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: '' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: '' } });
|
||||
expect(onChange).toHaveBeenLastCalledWith(null);
|
||||
});
|
||||
|
||||
it('should call onStep when press up or down button', () => {
|
||||
const onStep = jest.fn();
|
||||
const { container } = render(<InputNumber defaultValue={1} onStep={onStep} />);
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-up'));
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-up')!);
|
||||
expect(onStep).toHaveBeenCalledTimes(1);
|
||||
expect(onStep).toHaveBeenLastCalledWith(2, { offset: 1, type: 'up' });
|
||||
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-down'));
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-down')!);
|
||||
expect(onStep).toHaveBeenCalledTimes(2);
|
||||
expect(onStep).toHaveBeenLastCalledWith(1, { offset: 1, type: 'down' });
|
||||
});
|
@ -10,15 +10,15 @@ describe('prefix', () => {
|
||||
);
|
||||
it('should support className when has prefix', () => {
|
||||
const { container } = render(<InputNumber prefix="suffix" className="my-class-name" />);
|
||||
expect(container.firstChild?.className.includes('my-class-name')).toBe(true);
|
||||
expect((container.firstChild as HTMLElement)?.className.includes('my-class-name')).toBe(true);
|
||||
expect(container.querySelector('input')?.className.includes('my-class-name')).toBe(false);
|
||||
});
|
||||
|
||||
it('should trigger focus when prefix is clicked', () => {
|
||||
const { container } = render(<InputNumber prefix={<i>123</i>} />);
|
||||
|
||||
const mockFocus = jest.spyOn(container.querySelector('input'), 'focus');
|
||||
fireEvent.mouseUp(container.querySelector('i'));
|
||||
const mockFocus = jest.spyOn(container.querySelector('input')!, 'focus');
|
||||
fireEvent.mouseUp(container.querySelector('i')!);
|
||||
expect(mockFocus).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import type { InputRef } from '..';
|
||||
import Input from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
@ -13,12 +13,12 @@ describe('Input.Password', () => {
|
||||
rtlTest(Input.Password);
|
||||
|
||||
it('should get input element from ref', () => {
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<InputRef>();
|
||||
const onSelect = jest.fn();
|
||||
|
||||
const { container } = render(<Input.Password onSelect={onSelect} ref={ref} />);
|
||||
expect(ref.current.input instanceof HTMLInputElement).toBe(true);
|
||||
fireEvent.select(container.querySelector('input'));
|
||||
expect(ref.current?.input instanceof HTMLInputElement).toBe(true);
|
||||
fireEvent.select(container.querySelector('input')!);
|
||||
expect(onSelect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -30,13 +30,13 @@ describe('Input.Password', () => {
|
||||
|
||||
it('should change type when click', () => {
|
||||
const { asFragment, container } = render(<Input.Password />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: '111' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: '111' } });
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon')!);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon')!);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -50,7 +50,7 @@ describe('Input.Password', () => {
|
||||
it('should not toggle visibility when disabled prop is true', () => {
|
||||
const { container } = render(<Input.Password disabled />);
|
||||
expect(container.querySelectorAll('.anticon-eye-invisible').length).toBe(1);
|
||||
fireEvent.click(container.querySelector('.anticon-eye-invisible'));
|
||||
fireEvent.click(container.querySelector('.anticon-eye-invisible')!);
|
||||
expect(container.querySelectorAll('.anticon-eye').length).toBe(0);
|
||||
});
|
||||
|
||||
@ -59,43 +59,43 @@ describe('Input.Password', () => {
|
||||
container: document.body,
|
||||
});
|
||||
expect(document.activeElement).toBe(container.querySelector('input'));
|
||||
document.activeElement.setSelectionRange(2, 2);
|
||||
expect(document.activeElement.selectionStart).toBe(2);
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-password-icon'));
|
||||
fireEvent.mouseUp(container.querySelector('.ant-input-password-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon'));
|
||||
(document?.activeElement as any)?.setSelectionRange(2, 2);
|
||||
expect((document?.activeElement as any)?.selectionStart).toBe(2);
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-password-icon')!);
|
||||
fireEvent.mouseUp(container.querySelector('.ant-input-password-icon')!);
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon')!);
|
||||
expect(document.activeElement).toBe(container.querySelector('input'));
|
||||
expect(document.activeElement.selectionStart).toBe(2);
|
||||
expect((document?.activeElement as any).selectionStart).toBe(2);
|
||||
unmount();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/20541
|
||||
it('should not show value attribute in input element', async () => {
|
||||
const { container } = render(<Input.Password />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'value' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'value' } });
|
||||
await sleep();
|
||||
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/24526
|
||||
it('should not show value attribute in input element after blur it', async () => {
|
||||
const { container } = render(<Input.Password />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'value' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'value' } });
|
||||
await sleep();
|
||||
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
|
||||
fireEvent.blur(container.querySelector('input'));
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
|
||||
fireEvent.blur(container.querySelector('input')!);
|
||||
await sleep();
|
||||
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
|
||||
fireEvent.focus(container.querySelector('input'));
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
|
||||
fireEvent.focus(container.querySelector('input')!);
|
||||
await sleep();
|
||||
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/20541
|
||||
it('could be unmount without errors', () => {
|
||||
expect(() => {
|
||||
const { container, unmount } = render(<Input.Password />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'value' } });
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'value' } });
|
||||
unmount();
|
||||
}).not.toThrow();
|
||||
});
|
||||
@ -104,6 +104,6 @@ describe('Input.Password', () => {
|
||||
it('should not contain value attribute in input element with defaultValue', async () => {
|
||||
const { container } = render(<Input.Password defaultValue="value" />);
|
||||
await sleep();
|
||||
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
|
||||
});
|
||||
});
|
@ -4,6 +4,7 @@ import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import Button from '../../button';
|
||||
import type { InputRef } from '../Input';
|
||||
import Search from '../Search';
|
||||
|
||||
describe('Input.Search', () => {
|
||||
@ -42,24 +43,16 @@ describe('Input.Search', () => {
|
||||
const { container } = render(
|
||||
<Search defaultValue="search text" onSearch={onSearch} disabled />,
|
||||
);
|
||||
fireEvent.click(container.querySelector('button'));
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
expect(onSearch).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should trigger onSearch when click search icon', () => {
|
||||
const onSearch = jest.fn();
|
||||
const { container } = render(<Search defaultValue="search text" onSearch={onSearch} />);
|
||||
fireEvent.click(container.querySelector('button'));
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith(
|
||||
'search text',
|
||||
expect.anything(),
|
||||
// FIXME: should use following code
|
||||
// expect.objectContaining({
|
||||
// type: 'click',
|
||||
// preventDefault: expect.any(Function),
|
||||
// }),
|
||||
);
|
||||
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
|
||||
});
|
||||
|
||||
it('should trigger onSearch when click search button', () => {
|
||||
@ -67,17 +60,9 @@ describe('Input.Search', () => {
|
||||
const { container } = render(
|
||||
<Search defaultValue="search text" enterButton onSearch={onSearch} />,
|
||||
);
|
||||
fireEvent.click(container.querySelector('button'));
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith(
|
||||
'search text',
|
||||
expect.anything(),
|
||||
// FIXME: should use following code
|
||||
// expect.objectContaining({
|
||||
// type: 'click',
|
||||
// preventDefault: expect.any(Function),
|
||||
// }),
|
||||
);
|
||||
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
|
||||
});
|
||||
|
||||
it('should trigger onSearch when click search button with text', () => {
|
||||
@ -85,17 +70,9 @@ describe('Input.Search', () => {
|
||||
const { container } = render(
|
||||
<Search defaultValue="search text" enterButton="button text" onSearch={onSearch} />,
|
||||
);
|
||||
fireEvent.click(container.querySelector('button'));
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith(
|
||||
'search text',
|
||||
expect.anything(),
|
||||
// FIXME: should use following code
|
||||
// expect.objectContaining({
|
||||
// type: 'click',
|
||||
// preventDefault: expect.any(Function),
|
||||
// }),
|
||||
);
|
||||
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
|
||||
});
|
||||
|
||||
it('should trigger onSearch when click search button with customize button', () => {
|
||||
@ -107,17 +84,9 @@ describe('Input.Search', () => {
|
||||
onSearch={onSearch}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(container.querySelector('button'));
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith(
|
||||
'search text',
|
||||
expect.anything(),
|
||||
// FIXME: should use following code
|
||||
// expect.objectContaining({
|
||||
// type: 'click',
|
||||
// preventDefault: expect.any(Function),
|
||||
// }),
|
||||
);
|
||||
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
|
||||
});
|
||||
|
||||
it('should trigger onSearch when click search button of native', () => {
|
||||
@ -134,56 +103,32 @@ describe('Input.Search', () => {
|
||||
onSearch={onSearch}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(container.querySelector('button'));
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith(
|
||||
'search text',
|
||||
expect.anything(),
|
||||
// FIXME: should use following code
|
||||
// expect.objectContaining({
|
||||
// type: 'click',
|
||||
// preventDefault: expect.any(Function),
|
||||
// }),
|
||||
);
|
||||
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
|
||||
expect(onButtonClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should trigger onSearch when press enter', () => {
|
||||
const onSearch = jest.fn();
|
||||
const { container } = render(<Search defaultValue="search text" onSearch={onSearch} />);
|
||||
fireEvent.keyDown(container.querySelector('input'), { key: 'Enter', keyCode: 13 });
|
||||
fireEvent.keyDown(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith(
|
||||
'search text',
|
||||
expect.anything(),
|
||||
// FIXME: should use following code
|
||||
// expect.objectContaining({
|
||||
// type: 'keydown',
|
||||
// preventDefault: expect.any(Function),
|
||||
// }),
|
||||
);
|
||||
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/34844
|
||||
it('should not trigger onSearch when press enter using chinese inputting method', () => {
|
||||
const onSearch = jest.fn();
|
||||
const { container } = render(<Search defaultValue="search text" onSearch={onSearch} />);
|
||||
fireEvent.compositionStart(container.querySelector('input'));
|
||||
fireEvent.keyDown(container.querySelector('input'), { key: 'Enter', keyCode: 13 });
|
||||
fireEvent.compositionStart(container.querySelector('input')!);
|
||||
fireEvent.keyDown(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
|
||||
expect(onSearch).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.compositionEnd(container.querySelector('input'));
|
||||
fireEvent.keyDown(container.querySelector('input'), { key: 'Enter', keyCode: 13 });
|
||||
fireEvent.compositionEnd(container.querySelector('input')!);
|
||||
fireEvent.keyDown(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith(
|
||||
'search text',
|
||||
expect.anything(),
|
||||
// FIXME: should use following code
|
||||
// expect.objectContaining({
|
||||
// type: 'keydown',
|
||||
// preventDefault: expect.any(Function),
|
||||
// }),
|
||||
);
|
||||
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/14785
|
||||
@ -204,7 +149,7 @@ describe('Input.Search', () => {
|
||||
const { container } = render(
|
||||
<Search allowClear defaultValue="value" onSearch={onSearch} onChange={onChange} />,
|
||||
);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(onSearch).toHaveBeenLastCalledWith('', expect.anything());
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
});
|
||||
@ -236,13 +181,13 @@ describe('Input.Search', () => {
|
||||
});
|
||||
|
||||
it('should prevent search button mousedown event', () => {
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<InputRef>();
|
||||
const { container } = render(<Search ref={ref} enterButton="button text" />, {
|
||||
container: document.body,
|
||||
});
|
||||
ref.current.focus();
|
||||
ref.current?.focus();
|
||||
expect(document.activeElement).toBe(container.querySelector('input'));
|
||||
fireEvent.mouseDown(container.querySelector('button'));
|
||||
fireEvent.mouseDown(container.querySelector('button')!);
|
||||
expect(document.activeElement).toBe(container.querySelector('input'));
|
||||
});
|
||||
|
||||
@ -250,7 +195,7 @@ describe('Input.Search', () => {
|
||||
const ref = jest.fn();
|
||||
const { container } = render(<Search ref={ref} enterButton />);
|
||||
expect(() => {
|
||||
fireEvent.mouseDown(container.querySelector('button'));
|
||||
fireEvent.mouseDown(container.querySelector('button')!);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
@ -258,10 +203,10 @@ describe('Input.Search', () => {
|
||||
it('Search with allowClear should have one className only', () => {
|
||||
const { container } = render(<Search allowClear className="className" />);
|
||||
expect(
|
||||
container.querySelector('.ant-input-group-wrapper').classList.contains('className'),
|
||||
container.querySelector('.ant-input-group-wrapper')?.classList.contains('className'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
container.querySelector('.ant-input-affix-wrapper').classList.contains('className'),
|
||||
container.querySelector('.ant-input-affix-wrapper')?.classList.contains('className'),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
@ -1,8 +1,11 @@
|
||||
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
|
||||
import type { ChangeEventHandler, TextareaHTMLAttributes } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import Input from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import type { RenderOptions } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, triggerResize } from '../../../tests/utils';
|
||||
import type { TextAreaRef } from '../TextArea';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
@ -12,27 +15,23 @@ describe('TextArea', () => {
|
||||
const originalGetComputedStyle = window.getComputedStyle;
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(window, 'getComputedStyle', {
|
||||
value: node => ({
|
||||
getPropertyValue: prop => {
|
||||
if (prop === 'box-sizing') {
|
||||
return originalGetComputedStyle(node)[prop] || 'border-box';
|
||||
}
|
||||
return originalGetComputedStyle(node)[prop];
|
||||
},
|
||||
value: (node: Element) => ({
|
||||
getPropertyValue: (prop: PropertyKey) =>
|
||||
prop === 'box-sizing'
|
||||
? originalGetComputedStyle(node)[prop as unknown as number] || 'border-box'
|
||||
: originalGetComputedStyle(node)[prop as unknown as number],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Object.defineProperty(window, 'getComputedStyle', {
|
||||
value: originalGetComputedStyle,
|
||||
});
|
||||
Object.defineProperty(window, 'getComputedStyle', { value: originalGetComputedStyle });
|
||||
});
|
||||
|
||||
it('should auto calculate height according to content length', async () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<TextAreaRef>();
|
||||
|
||||
const genTextArea = (props = {}) => (
|
||||
<TextArea
|
||||
@ -47,7 +46,7 @@ describe('TextArea', () => {
|
||||
|
||||
const { container, rerender } = render(genTextArea());
|
||||
|
||||
const mockFunc = jest.spyOn(ref.current.resizableTextArea, 'resizeTextarea');
|
||||
const mockFunc = jest.spyOn(ref.current?.resizableTextArea!, 'resizeTextarea');
|
||||
|
||||
rerender(genTextArea({ value: '1111\n2222\n3333' }));
|
||||
// wrapper.setProps({ value: '1111\n2222\n3333' });
|
||||
@ -59,7 +58,7 @@ describe('TextArea', () => {
|
||||
await sleep(0);
|
||||
expect(mockFunc).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(container.querySelector('textarea').style.overflow).toBeFalsy();
|
||||
expect(container.querySelector('textarea')?.style.overflow).toBeFalsy();
|
||||
|
||||
expect(errorSpy).not.toHaveBeenCalled();
|
||||
errorSpy.mockRestore();
|
||||
@ -72,12 +71,12 @@ describe('TextArea', () => {
|
||||
<TextArea onKeyDown={fakeHandleKeyDown} onPressEnter={fakeHandlePressEnter} />,
|
||||
);
|
||||
/** KeyCode 65 is A */
|
||||
fireEvent.keyDown(container.querySelector('textarea'), { keyCode: 65 });
|
||||
fireEvent.keyDown(container.querySelector('textarea')!, { keyCode: 65 });
|
||||
expect(fakeHandleKeyDown).toHaveBeenCalledTimes(1);
|
||||
expect(fakeHandlePressEnter).toHaveBeenCalledTimes(0);
|
||||
|
||||
/** KeyCode 13 is Enter */
|
||||
fireEvent.keyDown(container.querySelector('textarea'), { keyCode: 13 });
|
||||
fireEvent.keyDown(container.querySelector('textarea')!, { keyCode: 13 });
|
||||
expect(fakeHandleKeyDown).toHaveBeenCalledTimes(2);
|
||||
expect(fakeHandlePressEnter).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
@ -95,7 +94,7 @@ describe('TextArea', () => {
|
||||
|
||||
it('maxLength should not block control', () => {
|
||||
const { container } = render(<TextArea maxLength={1} value="light" />);
|
||||
expect(container.querySelector('textarea').value).toEqual('light');
|
||||
expect(container.querySelector('textarea')?.value).toEqual('light');
|
||||
});
|
||||
|
||||
it('should limit correctly when in control', () => {
|
||||
@ -105,21 +104,21 @@ describe('TextArea', () => {
|
||||
};
|
||||
|
||||
const { container } = render(<Demo />);
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'light' } });
|
||||
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'light' } });
|
||||
|
||||
expect(container.querySelector('textarea').value).toEqual('l');
|
||||
expect(container.querySelector('textarea')?.value).toEqual('l');
|
||||
});
|
||||
|
||||
it('should exceed maxLength when use IME', () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const { container } = render(<TextArea maxLength={1} onChange={onChange} />);
|
||||
fireEvent.compositionStart(container.querySelector('textarea'));
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'zhu' } });
|
||||
fireEvent.compositionEnd(container.querySelector('textarea'), {
|
||||
fireEvent.compositionStart(container.querySelector('textarea')!);
|
||||
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'zhu' } });
|
||||
fireEvent.compositionEnd(container.querySelector('textarea')!, {
|
||||
currentTarget: { value: '竹' },
|
||||
});
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: '竹' } });
|
||||
fireEvent.change(container.querySelector('textarea')!, { target: { value: '竹' } });
|
||||
|
||||
expect(onChange).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ target: expect.objectContaining({ value: '竹' }) }),
|
||||
@ -132,13 +131,13 @@ describe('TextArea', () => {
|
||||
const { container } = render(
|
||||
<TextArea maxLength={6} defaultValue="123456" onChange={onChange} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
fireEvent.change(container.querySelector('textarea')!, {
|
||||
target: { selectionStart: 1, value: 'w123456' },
|
||||
});
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
fireEvent.change(container.querySelector('textarea')!, {
|
||||
target: { selectionStart: 3, value: 'w123456' },
|
||||
});
|
||||
expect(container.querySelector('textarea').value).toBe('123456');
|
||||
expect(container.querySelector('textarea')?.value).toBe('123456');
|
||||
});
|
||||
|
||||
// 拼音输入
|
||||
@ -148,21 +147,21 @@ describe('TextArea', () => {
|
||||
const { container } = render(
|
||||
<TextArea maxLength={6} defaultValue="1234" onChange={onChange} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
fireEvent.change(container.querySelector('textarea')!, {
|
||||
target: { selectionStart: 4, value: '1234' },
|
||||
});
|
||||
fireEvent.compositionStart(container.querySelector('textarea'));
|
||||
fireEvent.compositionStart(container.querySelector('textarea')!);
|
||||
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
fireEvent.change(container.querySelector('textarea')!, {
|
||||
target: { selectionStart: 9, value: '1234z z z' },
|
||||
});
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
fireEvent.change(container.querySelector('textarea')!, {
|
||||
target: { selectionStart: 7, value: '1234组织者' },
|
||||
});
|
||||
|
||||
fireEvent.compositionEnd(container.querySelector('textarea'));
|
||||
fireEvent.compositionEnd(container.querySelector('textarea')!);
|
||||
|
||||
expect(container.querySelector('textarea').value).toBe('1234组织');
|
||||
expect(container.querySelector('textarea')?.value).toBe('1234组织');
|
||||
});
|
||||
|
||||
// 2. 光标位于中间或开头,且当前字符数未达到6个,若选中的字符 + 原字符的长度超过6个,则显示原有字符
|
||||
@ -171,29 +170,29 @@ describe('TextArea', () => {
|
||||
const { container } = render(
|
||||
<TextArea maxLength={6} defaultValue="1234" onChange={onChange} />,
|
||||
);
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
fireEvent.change(container.querySelector('textarea')!, {
|
||||
target: { selectionStart: 2, value: '1234' },
|
||||
});
|
||||
fireEvent.compositionStart(container.querySelector('textarea'));
|
||||
fireEvent.compositionStart(container.querySelector('textarea')!);
|
||||
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
fireEvent.change(container.querySelector('textarea')!, {
|
||||
target: { selectionStart: 2, value: '12z z z34' },
|
||||
});
|
||||
fireEvent.change(container.querySelector('textarea'), {
|
||||
fireEvent.change(container.querySelector('textarea')!, {
|
||||
target: { selectionStart: 5, value: '12组织者34' },
|
||||
});
|
||||
|
||||
fireEvent.compositionEnd(container.querySelector('textarea'));
|
||||
fireEvent.compositionEnd(container.querySelector('textarea')!);
|
||||
|
||||
expect(container.querySelector('textarea').value).toBe('1234');
|
||||
expect(container.querySelector('textarea')?.value).toBe('1234');
|
||||
});
|
||||
});
|
||||
|
||||
it('when prop value not in this.props, resizeTextarea should be called', async () => {
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<TextAreaRef>();
|
||||
const { container } = render(<TextArea aria-label="textarea" ref={ref} />);
|
||||
const resizeTextarea = jest.spyOn(ref.current.resizableTextArea, 'resizeTextarea');
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'test' } });
|
||||
const resizeTextarea = jest.spyOn(ref.current?.resizableTextArea!, 'resizeTextarea');
|
||||
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'test' } });
|
||||
expect(resizeTextarea).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -203,7 +202,7 @@ describe('TextArea', () => {
|
||||
const { container } = render(
|
||||
<TextArea onPressEnter={onPressEnter} onKeyDown={onKeyDown} aria-label="textarea" />,
|
||||
);
|
||||
fireEvent.keyDown(container.querySelector('textarea'), { keyCode: 13 });
|
||||
fireEvent.keyDown(container.querySelector('textarea')!, { keyCode: 13 });
|
||||
|
||||
expect(onPressEnter).toHaveBeenCalled();
|
||||
expect(onKeyDown).toHaveBeenCalled();
|
||||
@ -211,18 +210,15 @@ describe('TextArea', () => {
|
||||
|
||||
it('should trigger onResize', async () => {
|
||||
const onResize = jest.fn();
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<TextAreaRef>();
|
||||
render(<TextArea ref={ref} onResize={onResize} autoSize />);
|
||||
await sleep(100);
|
||||
const target = ref.current.resizableTextArea.textArea;
|
||||
const target = ref.current?.resizableTextArea?.textArea!;
|
||||
triggerResize(target);
|
||||
await Promise.resolve();
|
||||
|
||||
expect(onResize).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
width: expect.any(Number),
|
||||
height: expect.any(Number),
|
||||
}),
|
||||
expect.objectContaining({ width: expect.any(Number), height: expect.any(Number) }),
|
||||
);
|
||||
});
|
||||
|
||||
@ -233,24 +229,24 @@ describe('TextArea', () => {
|
||||
);
|
||||
inputRerender(<Input value={undefined} />);
|
||||
textareaRerender(<TextArea value={undefined} />);
|
||||
expect(textareaContainer.querySelector('textarea').value).toBe(
|
||||
inputContainer.querySelector('input').value,
|
||||
expect(textareaContainer.querySelector('textarea')?.value).toBe(
|
||||
inputContainer.querySelector('input')?.value,
|
||||
);
|
||||
});
|
||||
|
||||
describe('should support showCount', () => {
|
||||
it('maxLength', () => {
|
||||
const { container } = render(<TextArea maxLength={5} showCount value="12345" />);
|
||||
expect(container.querySelector('textarea').value).toBe('12345');
|
||||
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
expect(container.querySelector('textarea')?.value).toBe('12345');
|
||||
expect(container.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
|
||||
'5 / 5',
|
||||
);
|
||||
});
|
||||
|
||||
it('control exceed maxLength', () => {
|
||||
const { container } = render(<TextArea maxLength={5} showCount value="12345678" />);
|
||||
expect(container.querySelector('textarea').value).toBe('12345678');
|
||||
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
expect(container.querySelector('textarea')?.value).toBe('12345678');
|
||||
expect(container.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
|
||||
'8 / 5',
|
||||
);
|
||||
});
|
||||
@ -258,29 +254,29 @@ describe('TextArea', () => {
|
||||
describe('emoji', () => {
|
||||
it('should minimize value between emoji length and maxLength', () => {
|
||||
const { container } = render(<TextArea maxLength={1} showCount value="👀" />);
|
||||
expect(container.querySelector('textarea').value).toBe('👀');
|
||||
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
expect(container.querySelector('textarea')?.value).toBe('👀');
|
||||
expect(container.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
|
||||
'1 / 1',
|
||||
);
|
||||
|
||||
// fix: 当 maxLength 长度为 2 的时候,输入 emoji 之后 showCount 会显示 1/2,但是不能再输入了
|
||||
// zombieJ: 逻辑统一了,emoji 现在也可以正确计数了
|
||||
const { container: container1 } = render(<TextArea maxLength={2} showCount value="👀" />);
|
||||
expect(container1.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
expect(container1.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
|
||||
'1 / 2',
|
||||
);
|
||||
});
|
||||
|
||||
it('defaultValue should slice', () => {
|
||||
const { container } = render(<TextArea maxLength={1} defaultValue="🧐cut" />);
|
||||
expect(container.querySelector('textarea').value).toBe('🧐');
|
||||
expect(container.querySelector('textarea')?.value).toBe('🧐');
|
||||
});
|
||||
|
||||
// 修改TextArea value截取规则后新增单测
|
||||
it('slice emoji', () => {
|
||||
const { container } = render(<TextArea maxLength={5} showCount value="1234😂" />);
|
||||
expect(container.querySelector('textarea').value).toBe('1234😂');
|
||||
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
expect(container.querySelector('textarea')?.value).toBe('1234😂');
|
||||
expect(container.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
|
||||
'5 / 5',
|
||||
);
|
||||
});
|
||||
@ -292,12 +288,12 @@ describe('TextArea', () => {
|
||||
);
|
||||
|
||||
// Outer
|
||||
expect(container.querySelector('div').classList.contains('bamboo')).toBeTruthy();
|
||||
expect(container.querySelector('div').style.background).toEqual('red');
|
||||
expect(container.querySelector('div')?.classList.contains('bamboo')).toBeTruthy();
|
||||
expect(container.querySelector('div')?.style.background).toEqual('red');
|
||||
|
||||
// Inner
|
||||
expect(container.querySelector('.ant-input').classList.contains('bamboo')).toBeFalsy();
|
||||
expect(container.querySelector('.ant-input').style.background).toBeFalsy();
|
||||
expect(container.querySelector('.ant-input')?.classList.contains('bamboo')).toBeFalsy();
|
||||
expect(container.querySelector<HTMLDivElement>('.ant-input')?.style.background).toBeFalsy();
|
||||
});
|
||||
|
||||
it('count formatter', () => {
|
||||
@ -310,8 +306,8 @@ describe('TextArea', () => {
|
||||
value="12345"
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelector('textarea').value).toBe('12345');
|
||||
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
|
||||
expect(container.querySelector('textarea')?.value).toBe('12345');
|
||||
expect(container.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
|
||||
'12345, 5, 5',
|
||||
);
|
||||
});
|
||||
@ -319,36 +315,40 @@ describe('TextArea', () => {
|
||||
|
||||
it('should support size', async () => {
|
||||
const { asFragment, container } = render(<TextArea size="large" />);
|
||||
expect(container.querySelector('textarea').classList.contains('ant-input-lg')).toBe(true);
|
||||
expect(container.querySelector('textarea')?.classList.contains('ant-input-lg')).toBe(true);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('set mouse cursor position', () => {
|
||||
const defaultValue = '11111';
|
||||
const valLength = defaultValue.length;
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<TextAreaRef>();
|
||||
render(<TextArea autoFocus ref={ref} defaultValue={defaultValue} />);
|
||||
ref.current.resizableTextArea.textArea.setSelectionRange(valLength, valLength);
|
||||
expect(ref.current.resizableTextArea.textArea.selectionStart).toEqual(5);
|
||||
expect(ref.current.resizableTextArea.textArea.selectionEnd).toEqual(5);
|
||||
ref.current?.resizableTextArea?.textArea.setSelectionRange(valLength, valLength);
|
||||
expect(ref.current?.resizableTextArea?.textArea.selectionStart).toEqual(5);
|
||||
expect(ref.current?.resizableTextArea?.textArea.selectionEnd).toEqual(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TextArea allowClear', () => {
|
||||
it('should change type when click', () => {
|
||||
const { asFragment, container } = render(<TextArea allowClear />);
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: '111' } });
|
||||
expect(container.querySelector('textarea').value).toEqual('111');
|
||||
fireEvent.change(container.querySelector('textarea')!, { target: { value: '111' } });
|
||||
expect(container.querySelector('textarea')?.value).toEqual('111');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(container.querySelector('textarea').value).toEqual('');
|
||||
expect(container.querySelector('textarea')?.value).toEqual('');
|
||||
});
|
||||
|
||||
it('should not show icon if value is undefined, null or empty string', () => {
|
||||
const wrappers = [null, undefined, ''].map(val => render(<TextArea allowClear value={val} />));
|
||||
const wrappers = [null, undefined, ''].map(val =>
|
||||
render(
|
||||
<TextArea allowClear value={val as TextareaHTMLAttributes<HTMLTextAreaElement>['value']} />,
|
||||
),
|
||||
);
|
||||
wrappers.forEach(({ asFragment, container }) => {
|
||||
expect(container.querySelector('textarea').value).toEqual('');
|
||||
expect(container.querySelector('textarea')?.value).toEqual('');
|
||||
expect(container.querySelector('.ant-input-clear-icon-hidden')).toBeTruthy();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
@ -356,10 +356,15 @@ describe('TextArea allowClear', () => {
|
||||
|
||||
it('should not show icon if defaultValue is undefined, null or empty string', () => {
|
||||
const wrappers = [null, undefined, ''].map(val =>
|
||||
render(<TextArea allowClear defaultValue={val} />),
|
||||
render(
|
||||
<TextArea
|
||||
allowClear
|
||||
defaultValue={val as TextareaHTMLAttributes<HTMLTextAreaElement>['value']}
|
||||
/>,
|
||||
),
|
||||
);
|
||||
wrappers.forEach(({ asFragment, container }) => {
|
||||
expect(container.querySelector('textarea').value).toEqual('');
|
||||
expect(container.querySelector('textarea')?.value).toEqual('');
|
||||
expect(container.querySelector('.ant-input-clear-icon-hidden')).toBeTruthy();
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
@ -368,36 +373,36 @@ describe('TextArea allowClear', () => {
|
||||
it('should trigger event correctly', () => {
|
||||
let argumentEventObjectType;
|
||||
let argumentEventObjectValue;
|
||||
const onChange = e => {
|
||||
const onChange: ChangeEventHandler<HTMLTextAreaElement> = e => {
|
||||
argumentEventObjectType = e.type;
|
||||
argumentEventObjectValue = e.target.value;
|
||||
};
|
||||
const { container } = render(<TextArea allowClear defaultValue="111" onChange={onChange} />);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(argumentEventObjectType).toBe('click');
|
||||
expect(argumentEventObjectValue).toBe('');
|
||||
expect(container.querySelector('textarea').value).toBe('');
|
||||
expect(container.querySelector('textarea')?.value).toBe('');
|
||||
});
|
||||
|
||||
it('should trigger event correctly on controlled mode', () => {
|
||||
let argumentEventObjectType;
|
||||
let argumentEventObjectValue;
|
||||
const onChange = e => {
|
||||
const onChange: ChangeEventHandler<HTMLTextAreaElement> = e => {
|
||||
argumentEventObjectType = e.type;
|
||||
argumentEventObjectValue = e.target.value;
|
||||
};
|
||||
const { container } = render(<TextArea allowClear value="111" onChange={onChange} />);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(argumentEventObjectType).toBe('click');
|
||||
expect(argumentEventObjectValue).toBe('');
|
||||
expect(container.querySelector('textarea').value).toBe('111');
|
||||
expect(container.querySelector('textarea')?.value).toBe('111');
|
||||
});
|
||||
|
||||
it('should focus textarea after clear', () => {
|
||||
const { container, unmount } = render(<TextArea allowClear defaultValue="111" />, {
|
||||
container: document.body,
|
||||
});
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(document.activeElement).toBe(container.querySelector('textarea'));
|
||||
unmount();
|
||||
});
|
||||
@ -409,30 +414,30 @@ describe('TextArea allowClear', () => {
|
||||
|
||||
it('not block input when `value` is undefined', () => {
|
||||
const { container, rerender } = render(<Input value={undefined} />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'Bamboo' } });
|
||||
expect(container.querySelector('input').value).toEqual('Bamboo');
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'Bamboo' } });
|
||||
expect(container.querySelector('input')?.value).toEqual('Bamboo');
|
||||
|
||||
// Controlled
|
||||
rerender(<Input value="Light" />);
|
||||
fireEvent.change(container.querySelector('input'), { target: { value: 'Bamboo' } });
|
||||
expect(container.querySelector('input').value).toEqual('Light');
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'Bamboo' } });
|
||||
expect(container.querySelector('input')?.value).toEqual('Light');
|
||||
});
|
||||
|
||||
it('scroll to bottom when autoSize', async () => {
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<TextAreaRef>();
|
||||
const { container, unmount } = render(<Input.TextArea ref={ref} autoSize />, {
|
||||
container: document.body,
|
||||
legacyRoot: true,
|
||||
});
|
||||
fireEvent.focus(container.querySelector('textarea'));
|
||||
container.querySelector('textarea').focus();
|
||||
} as RenderOptions);
|
||||
fireEvent.focus(container.querySelector('textarea')!);
|
||||
container.querySelector('textarea')?.focus();
|
||||
|
||||
const setSelectionRangeFn = jest.spyOn(
|
||||
container.querySelector('textarea'),
|
||||
container.querySelector('textarea')!,
|
||||
'setSelectionRange',
|
||||
);
|
||||
fireEvent.input(container.querySelector('textarea'), { target: { value: '\n1' } });
|
||||
const target = ref.current.resizableTextArea.textArea;
|
||||
fireEvent.input(container.querySelector('textarea')!, { target: { value: '\n1' } });
|
||||
const target = ref.current?.resizableTextArea?.textArea!;
|
||||
triggerResize(target);
|
||||
await sleep(100);
|
||||
expect(setSelectionRangeFn).toHaveBeenCalled();
|
||||
@ -442,7 +447,7 @@ describe('TextArea allowClear', () => {
|
||||
// https://github.com/ant-design/ant-design/issues/26308
|
||||
it('should display defaultValue when value is undefined', () => {
|
||||
const { container } = render(<Input.TextArea defaultValue="Light" value={undefined} />);
|
||||
expect(container.querySelector('textarea').value).toBe('Light');
|
||||
expect(container.querySelector('textarea')?.value).toBe('Light');
|
||||
});
|
||||
|
||||
it('onChange event should return HTMLTextAreaElement', () => {
|
||||
@ -451,25 +456,23 @@ describe('TextArea allowClear', () => {
|
||||
|
||||
function isNativeElement() {
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
target: expect.any(HTMLTextAreaElement),
|
||||
}),
|
||||
expect.objectContaining({ target: expect.any(HTMLTextAreaElement) }),
|
||||
);
|
||||
|
||||
onChange.mockReset();
|
||||
}
|
||||
|
||||
// Change
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'bamboo' } });
|
||||
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'bamboo' } });
|
||||
isNativeElement();
|
||||
|
||||
// Composition End
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: 'light' } });
|
||||
fireEvent.compositionEnd(container.querySelector('textarea'));
|
||||
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'light' } });
|
||||
fireEvent.compositionEnd(container.querySelector('textarea')!);
|
||||
isNativeElement();
|
||||
|
||||
// Reset
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
isNativeElement();
|
||||
});
|
||||
|
||||
@ -489,12 +492,12 @@ describe('TextArea allowClear', () => {
|
||||
};
|
||||
|
||||
const { container, unmount } = render(<App />);
|
||||
container.querySelector('textarea').focus();
|
||||
fireEvent.change(container.querySelector('textarea'), { target: { value: '111' } });
|
||||
expect(container.querySelector('textarea').value).toEqual('111');
|
||||
container.querySelector('textarea')?.focus();
|
||||
fireEvent.change(container.querySelector('textarea')!, { target: { value: '111' } });
|
||||
expect(container.querySelector('textarea')?.value).toEqual('111');
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
expect(container.querySelector('textarea').value).toEqual('');
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(container.querySelector('textarea')?.value).toEqual('');
|
||||
|
||||
unmount();
|
||||
});
|
||||
@ -508,12 +511,12 @@ describe('TextArea allowClear', () => {
|
||||
container: document.body,
|
||||
},
|
||||
);
|
||||
container.querySelector('textarea').focus();
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.mouseUp(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.focus(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
container.querySelector('textarea')?.focus();
|
||||
fireEvent.mouseDown(container.querySelector('.ant-input-clear-icon')!);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
fireEvent.mouseUp(container.querySelector('.ant-input-clear-icon')!);
|
||||
fireEvent.focus(container.querySelector('.ant-input-clear-icon')!);
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(onBlur).not.toHaveBeenCalled();
|
||||
unmount();
|
||||
});
|
||||
@ -522,16 +525,20 @@ describe('TextArea allowClear', () => {
|
||||
const { container, unmount } = render(<TextArea allowClear defaultValue="111" />, {
|
||||
container: document.body,
|
||||
});
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(document.activeElement).toBe(container.querySelector('textarea'));
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should display boolean value as string', () => {
|
||||
const { container, rerender } = render(<TextArea value />);
|
||||
expect(container.querySelector('textarea').value).toBe('true');
|
||||
rerender(<TextArea value={false} />);
|
||||
expect(container.querySelector('textarea').value).toBe('false');
|
||||
const { container, rerender } = render(
|
||||
<TextArea value={true as unknown as TextareaHTMLAttributes<HTMLTextAreaElement>['value']} />,
|
||||
);
|
||||
expect(container.querySelector('textarea')?.value).toBe('true');
|
||||
rerender(
|
||||
<TextArea value={false as unknown as TextareaHTMLAttributes<HTMLTextAreaElement>['value']} />,
|
||||
);
|
||||
expect(container.querySelector('textarea')?.value).toBe('false');
|
||||
});
|
||||
|
||||
it('should focus when clearBtn is clicked in controlled case', () => {
|
||||
@ -541,18 +548,17 @@ describe('TextArea allowClear', () => {
|
||||
focus: handleFocus,
|
||||
});
|
||||
|
||||
const Demo = () => {
|
||||
const Demo: React.FC = () => {
|
||||
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' } });
|
||||
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'));
|
||||
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
|
||||
expect(handleFocus).toHaveBeenCalledTimes(1);
|
||||
|
||||
textareaSpy.mockRestore();
|
@ -1,9 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { UserOutlined } from '@ant-design/icons';
|
||||
import Layout from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import Icon from '../../icon';
|
||||
import Menu from '../../menu';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
|
||||
@ -31,14 +31,14 @@ describe('Layout', () => {
|
||||
<Content>Content</Content>
|
||||
</Layout>,
|
||||
);
|
||||
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
|
||||
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
|
||||
true,
|
||||
);
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('umount from multiple siders', async () => {
|
||||
const App = () => {
|
||||
const App: React.FC = () => {
|
||||
const [hide1, setHide1] = useState(false);
|
||||
const [hide2, setHide2] = useState(false);
|
||||
return (
|
||||
@ -57,15 +57,15 @@ describe('Layout', () => {
|
||||
);
|
||||
};
|
||||
const { container } = render(<App />);
|
||||
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
|
||||
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
|
||||
true,
|
||||
);
|
||||
fireEvent.click(container.querySelectorAll('button')[0]);
|
||||
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
|
||||
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
|
||||
true,
|
||||
);
|
||||
fireEvent.click(container.querySelectorAll('button')[1]);
|
||||
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
|
||||
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
@ -79,7 +79,7 @@ describe('Layout', () => {
|
||||
<Content>Content</Content>
|
||||
</Layout>,
|
||||
);
|
||||
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
|
||||
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
@ -96,7 +96,7 @@ describe('Layout', () => {
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-layout-sider')
|
||||
.className.includes('ant-layout-sider-has-trigger'),
|
||||
?.className.includes('ant-layout-sider-has-trigger'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
@ -109,8 +109,8 @@ describe('Layout', () => {
|
||||
<Content>Content</Content>
|
||||
</Layout>,
|
||||
);
|
||||
expect(container.querySelector('.ant-layout-sider').style.width).toBe('50%');
|
||||
expect(container.querySelector('.ant-layout-sider').style.flex).toBe('0 0 50%');
|
||||
expect(container.querySelector<HTMLElement>('.ant-layout-sider')?.style.width).toBe('50%');
|
||||
expect(container.querySelector<HTMLElement>('.ant-layout-sider')?.style.flex).toBe('0 0 50%');
|
||||
});
|
||||
|
||||
describe('zeroWidth', () => {
|
||||
@ -126,7 +126,7 @@ describe('Layout', () => {
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-layout-sider')
|
||||
.className.includes('ant-layout-sider-zero-width'),
|
||||
?.className.includes('ant-layout-sider-zero-width'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
@ -144,12 +144,12 @@ describe('Layout', () => {
|
||||
);
|
||||
|
||||
onCollapse.mockReset();
|
||||
fireEvent.click(container.querySelector('.ant-layout-sider-zero-width-trigger'));
|
||||
fireEvent.click(container.querySelector('.ant-layout-sider-zero-width-trigger')!);
|
||||
expect(onCollapse).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('controlled', () => {
|
||||
const Demo = () => {
|
||||
const Demo: React.FC = () => {
|
||||
const [collapsed, setCollapsed] = React.useState(true);
|
||||
|
||||
return (
|
||||
@ -170,7 +170,7 @@ describe('Layout', () => {
|
||||
|
||||
const { container } = render(<Demo />);
|
||||
expect(container.querySelector('.ant-layout-sider-collapsed')).toBeTruthy();
|
||||
fireEvent.click(container.querySelector('.ant-layout-sider-zero-width-trigger'));
|
||||
fireEvent.click(container.querySelector('.ant-layout-sider-zero-width-trigger')!);
|
||||
expect(container.querySelector('.ant-layout-sider-collapsed')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@ -179,14 +179,14 @@ describe('Layout', () => {
|
||||
it('detect ant-layout-sider-dark as default theme', async () => {
|
||||
const { container } = render(<Sider>Sider</Sider>);
|
||||
expect(
|
||||
container.querySelector('.ant-layout-sider').className.includes('ant-layout-sider-dark'),
|
||||
container.querySelector('.ant-layout-sider')?.className.includes('ant-layout-sider-dark'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('detect ant-layout-sider-light when set light theme', async () => {
|
||||
const { container } = render(<Sider theme="light">Sider</Sider>);
|
||||
expect(
|
||||
container.querySelector('.ant-layout-sider').className.includes('ant-layout-sider-light'),
|
||||
container.querySelector('.ant-layout-sider')?.className.includes('ant-layout-sider-light'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
@ -208,7 +208,7 @@ describe('Layout', () => {
|
||||
<Sider>Sider</Sider>
|
||||
</Layout>,
|
||||
);
|
||||
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
|
||||
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
@ -219,30 +219,29 @@ describe('Layout', () => {
|
||||
<Sider collapsible collapsed={false}>
|
||||
<Menu mode="inline">
|
||||
<Menu.Item key="1">
|
||||
<Icon type="user" />
|
||||
<UserOutlined />
|
||||
<span>Light</span>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Sider>,
|
||||
);
|
||||
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-menu-item'));
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-menu-item')!);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(container.querySelectorAll('.ant-tooltip-inner').length).toBeFalsy();
|
||||
|
||||
rerender(
|
||||
<Sider collapsible collapsed>
|
||||
<Menu mode="inline">
|
||||
<Menu.Item key="1">
|
||||
<Icon type="user" />
|
||||
<UserOutlined />
|
||||
<span>Light</span>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Sider>,
|
||||
);
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-menu-item'));
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-menu-item')!);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
@ -290,14 +289,15 @@ describe('Sider', () => {
|
||||
<Sider collapsedWidth={0} collapsible zeroWidthTriggerStyle={{ background: '#F96' }}>
|
||||
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
|
||||
<Menu.Item key="1">
|
||||
<Icon type="user" />
|
||||
<UserOutlined />
|
||||
<span>nav 1</span>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Sider>,
|
||||
);
|
||||
expect(
|
||||
container.querySelector('.ant-layout-sider-zero-width-trigger').style.background,
|
||||
container.querySelector<HTMLDivElement>('.ant-layout-sider-zero-width-trigger')?.style
|
||||
.background,
|
||||
).toEqual('rgb(255, 153, 102)');
|
||||
});
|
||||
|
||||
@ -306,24 +306,23 @@ describe('Sider', () => {
|
||||
<Sider collapsedWidth={0} collapsible trigger={<span className="my-trigger" />}>
|
||||
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
|
||||
<Menu.Item key="1">
|
||||
<Icon type="user" />
|
||||
<UserOutlined />
|
||||
<span>nav 1</span>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Sider>,
|
||||
);
|
||||
expect(
|
||||
container.querySelector('.ant-layout-sider-zero-width-trigger').querySelector('.my-trigger'),
|
||||
container.querySelector('.ant-layout-sider-zero-width-trigger')?.querySelector('.my-trigger'),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
['Layout', 'Header', 'Footer', 'Sider'].forEach(tag => {
|
||||
const ComponentMap = { Layout, Header, Footer, Sider };
|
||||
it(`should get ${tag} element from ref`, () => {
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<any>();
|
||||
const onSelect = jest.fn();
|
||||
const Component = ComponentMap[tag];
|
||||
|
||||
const Component = ComponentMap[tag as keyof typeof ComponentMap];
|
||||
render(
|
||||
<Component onSelect={onSelect} ref={ref}>
|
||||
{tag}
|
@ -49,7 +49,7 @@ describe('List Item Layout', () => {
|
||||
/>,
|
||||
);
|
||||
expect(
|
||||
wrapper.querySelector('.ant-list-item').classList.contains('ant-list-item-no-flex'),
|
||||
wrapper.querySelector('.ant-list-item')?.classList.contains('ant-list-item-no-flex'),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
@ -188,17 +188,17 @@ describe('List Item Layout', () => {
|
||||
});
|
||||
|
||||
it('should ref', () => {
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<HTMLElement>();
|
||||
|
||||
render(<List.Item ref={ref}>Item</List.Item>);
|
||||
expect(ref.current).toHaveClass('ant-list-item');
|
||||
});
|
||||
|
||||
it('should grid ref', () => {
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<HTMLElement>();
|
||||
|
||||
render(
|
||||
<List grid>
|
||||
<List grid={{}}>
|
||||
<List.Item ref={ref}>Item</List.Item>,
|
||||
</List>,
|
||||
);
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import type { ListProps } from '..';
|
||||
import List from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
@ -13,12 +14,12 @@ describe('List', () => {
|
||||
|
||||
it('locale not passed to internal div', async () => {
|
||||
const locale = { emptyText: 'Custom text' };
|
||||
const renderItem = item => <List.Item>{item}</List.Item>;
|
||||
const dataSource = [];
|
||||
const renderItem: ListProps<any>['renderItem'] = item => <List.Item>{item}</List.Item>;
|
||||
const dataSource: ListProps<any>['dataSource'] = [];
|
||||
|
||||
const { container } = render(
|
||||
<List renderItem={renderItem} dataSource={dataSource} locale={locale} />,
|
||||
);
|
||||
expect(container.querySelector('div.ant-list').getAttribute('locale')).toBe(null);
|
||||
expect(container.querySelector('div.ant-list')?.getAttribute('locale')).toBe(null);
|
||||
});
|
||||
});
|
@ -6,9 +6,7 @@ import List from '..';
|
||||
|
||||
describe('List', () => {
|
||||
it('renders empty loading', () => {
|
||||
const loading = {
|
||||
spinning: true,
|
||||
};
|
||||
const loading = { spinning: true };
|
||||
const { container: wrapper } = render(
|
||||
<List loading={loading} dataSource={[]} renderItem={() => <List.Item />} />,
|
||||
);
|
@ -1,10 +1,16 @@
|
||||
import React from 'react';
|
||||
import type { ListProps } from '..';
|
||||
import List from '..';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import { noop } from '../../_util/warning';
|
||||
|
||||
interface DataSourceItem {
|
||||
name: string;
|
||||
key: React.Key;
|
||||
}
|
||||
|
||||
describe('List.pagination', () => {
|
||||
const data = [
|
||||
const data: ListProps<DataSourceItem>['dataSource'] = [
|
||||
{ key: 0, name: 'Jack' },
|
||||
{ key: 1, name: 'Lucy' },
|
||||
{ key: 2, name: 'Tom' },
|
||||
@ -13,7 +19,7 @@ describe('List.pagination', () => {
|
||||
|
||||
const pagination = { className: 'my-page', pageSize: 2 };
|
||||
|
||||
function createList(props) {
|
||||
function createList(props?: ListProps<DataSourceItem>) {
|
||||
return (
|
||||
<List
|
||||
itemLayout="vertical"
|
||||
@ -25,10 +31,10 @@ describe('List.pagination', () => {
|
||||
);
|
||||
}
|
||||
|
||||
function renderedNames(wrapper) {
|
||||
function renderedNames(container: ReturnType<typeof render>['container']) {
|
||||
return Array.prototype.map.call(
|
||||
wrapper.querySelectorAll('.ant-list-item'),
|
||||
row => row.textContent,
|
||||
container.querySelectorAll('.ant-list-item'),
|
||||
(row: HTMLDivElement) => row.textContent,
|
||||
);
|
||||
}
|
||||
|
||||
@ -39,12 +45,7 @@ describe('List.pagination', () => {
|
||||
|
||||
it('should not show pager if pagination.hideOnSinglePage is true and only 1 page', () => {
|
||||
const { container: wrapper, rerender } = render(
|
||||
createList({
|
||||
pagination: {
|
||||
pageSize: 3,
|
||||
hideOnSinglePage: true,
|
||||
},
|
||||
}),
|
||||
createList({ pagination: { pageSize: 3, hideOnSinglePage: true } }),
|
||||
);
|
||||
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
rerender(createList({ pagination: { pageSize: 3, hideOnSinglePage: false } }));
|
||||
@ -107,13 +108,13 @@ describe('List.pagination', () => {
|
||||
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
expect(wrapper.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
|
||||
|
||||
fireEvent.click(wrapper.querySelector('.ant-pagination-item-2'));
|
||||
fireEvent.click(wrapper.querySelector('.ant-pagination-item-2')!);
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Jerry']);
|
||||
|
||||
rerender(createList({ pagination: false }));
|
||||
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(0);
|
||||
|
||||
rerender(createList({ pagination: true }));
|
||||
rerender(createList({ pagination: true as ListProps<DataSourceItem>['pagination'] }));
|
||||
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
// Legacy code will make pageSize ping with 10, here we fixed to keep sync by current one
|
||||
expect(wrapper.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
|
||||
@ -123,7 +124,7 @@ describe('List.pagination', () => {
|
||||
// https://github.com/ant-design/ant-design/issues/5259
|
||||
it('change to correct page when data source changes', () => {
|
||||
const { container: wrapper, rerender } = render(createList({ pagination: { pageSize: 1 } }));
|
||||
fireEvent.click(wrapper.querySelector('.ant-pagination-item-3'));
|
||||
fireEvent.click(wrapper.querySelector('.ant-pagination-item-3')!);
|
||||
rerender(createList({ dataSource: [data[0]] }));
|
||||
expect(wrapper.querySelector('.ant-pagination-item-1')).toHaveClass(
|
||||
'ant-pagination-item-active',
|
||||
@ -134,20 +135,20 @@ describe('List.pagination', () => {
|
||||
const { container: wrapper, rerender } = render(
|
||||
createList({ pagination: { position: 'top' } }),
|
||||
);
|
||||
expect(wrapper.querySelector('.ant-list').querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
expect(wrapper.querySelector('.ant-list')?.querySelectorAll('.ant-pagination')).toHaveLength(1);
|
||||
|
||||
rerender(createList({ pagination: { position: 'bottom' } }));
|
||||
expect(
|
||||
wrapper.querySelector('.ant-list').lastElementChild.querySelectorAll('.ant-pagination'),
|
||||
wrapper.querySelector('.ant-list')?.lastElementChild?.querySelectorAll('.ant-pagination'),
|
||||
).toHaveLength(1);
|
||||
|
||||
rerender(createList({ pagination: { position: 'both' } }));
|
||||
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(2);
|
||||
expect(
|
||||
wrapper.querySelector('.ant-list').firstElementChild.querySelectorAll('.ant-pagination'),
|
||||
wrapper.querySelector('.ant-list')?.firstElementChild?.querySelectorAll('.ant-pagination'),
|
||||
).toHaveLength(1);
|
||||
expect(
|
||||
wrapper.querySelector('.ant-list').lastElementChild.querySelectorAll('.ant-pagination'),
|
||||
wrapper.querySelector('.ant-list')?.lastElementChild?.querySelectorAll('.ant-pagination'),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
@ -155,10 +156,10 @@ describe('List.pagination', () => {
|
||||
const { container: wrapper } = render(createList({ pagination: { showSizeChanger: true } }));
|
||||
expect(wrapper.querySelector('.ant-pagination')).toMatchSnapshot();
|
||||
|
||||
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector'));
|
||||
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector')!);
|
||||
fireEvent.click(wrapper.querySelectorAll('.ant-select-item-option')[2]);
|
||||
|
||||
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector'));
|
||||
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector')!);
|
||||
expect(wrapper.querySelector('.ant-pagination')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -178,7 +179,7 @@ describe('List.pagination', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector'));
|
||||
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector')!);
|
||||
fireEvent.click(wrapper.querySelectorAll('.ant-select-item-option')[1]);
|
||||
expect(handlePaginationChange).toHaveBeenCalledWith(1, 10);
|
||||
});
|
||||
@ -199,10 +200,6 @@ describe('List.pagination', () => {
|
||||
});
|
||||
|
||||
it('should not crash when pagination is null', () => {
|
||||
render(
|
||||
createList({
|
||||
pagination: null,
|
||||
}),
|
||||
);
|
||||
render(createList({ pagination: null as unknown as ListProps<DataSourceItem>['pagination'] }));
|
||||
});
|
||||
});
|
@ -35871,7 +35871,7 @@ exports[`Locale Provider should display the text as cs 1`] = `
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Storno
|
||||
Zrušit
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
@ -37444,7 +37444,7 @@ exports[`Locale Provider should display the text as cs 1`] = `
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Storno
|
||||
Zrušit
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
|
@ -1,9 +1,12 @@
|
||||
/* eslint-disable no-template-curly-in-string */
|
||||
import Pagination from 'rc-pagination/lib/locale/cs_CZ';
|
||||
import Calendar from '../calendar/locale/cs_CZ';
|
||||
import DatePicker from '../date-picker/locale/cs_CZ';
|
||||
import type { Locale } from '../locale-provider';
|
||||
import TimePicker from '../time-picker/locale/cs_CZ';
|
||||
|
||||
const typeTemplate = '${label} není platný ${type}';
|
||||
|
||||
const localeValues: Locale = {
|
||||
locale: 'cs',
|
||||
Pagination,
|
||||
@ -18,8 +21,12 @@ const localeValues: Locale = {
|
||||
filterConfirm: 'Potvrdit',
|
||||
filterReset: 'Obnovit',
|
||||
filterEmptyText: 'Žádné filtry',
|
||||
filterCheckall: 'Vybrat všechny položky',
|
||||
filterSearchPlaceholder: 'Vyhledat ve filtrech',
|
||||
emptyText: 'Žádná data',
|
||||
selectAll: 'Vybrat všechny řádky na současné stránce',
|
||||
selectInvert: 'Invertovat výběr na současné stránce',
|
||||
selectNone: 'Odznačit vše',
|
||||
selectionAll: 'Vybrat všechny řádky',
|
||||
sortTitle: 'Řadit',
|
||||
expand: 'Rozbalit řádek',
|
||||
@ -30,17 +37,24 @@ const localeValues: Locale = {
|
||||
},
|
||||
Modal: {
|
||||
okText: 'OK',
|
||||
cancelText: 'Storno',
|
||||
cancelText: 'Zrušit',
|
||||
justOkText: 'OK',
|
||||
},
|
||||
Popconfirm: {
|
||||
okText: 'OK',
|
||||
cancelText: 'Storno',
|
||||
cancelText: 'Zrušit',
|
||||
},
|
||||
Transfer: {
|
||||
titles: ['', ''],
|
||||
searchPlaceholder: 'Vyhledávání',
|
||||
itemUnit: 'položka',
|
||||
itemsUnit: 'položek',
|
||||
remove: 'Odstranit',
|
||||
selectCurrent: 'Vybrat aktuální stranu',
|
||||
removeCurrent: 'Smazat aktuální stranu',
|
||||
selectAll: 'Označit vše',
|
||||
removeAll: 'Odznačit vše',
|
||||
selectInvert: 'Opačný výběr',
|
||||
},
|
||||
Upload: {
|
||||
uploading: 'Nahrávání...',
|
||||
@ -52,6 +66,71 @@ const localeValues: Locale = {
|
||||
Empty: {
|
||||
description: 'Žádná data',
|
||||
},
|
||||
Icon: {
|
||||
icon: 'ikona',
|
||||
},
|
||||
Text: {
|
||||
edit: 'Upravit',
|
||||
copy: 'Kopírovat',
|
||||
copied: 'Zkopírované',
|
||||
expand: 'Zvětšit',
|
||||
},
|
||||
PageHeader: {
|
||||
back: 'Zpět',
|
||||
},
|
||||
Form: {
|
||||
optional: '(nepovinné)',
|
||||
defaultValidateMessages: {
|
||||
default: 'Validační chyba pole pro ${label}',
|
||||
required: 'Prosím vložte ${label}',
|
||||
enum: '${label} musí být jeden z [${enum}]',
|
||||
whitespace: '${label} nemůže být prázdný znak',
|
||||
date: {
|
||||
format: '${label} formát datumu je neplatný',
|
||||
parse: '${label} není možné konvertovat na datum',
|
||||
invalid: '${label} je neplatné datum',
|
||||
},
|
||||
types: {
|
||||
string: typeTemplate,
|
||||
method: typeTemplate,
|
||||
array: typeTemplate,
|
||||
object: typeTemplate,
|
||||
number: typeTemplate,
|
||||
date: typeTemplate,
|
||||
boolean: typeTemplate,
|
||||
integer: typeTemplate,
|
||||
float: typeTemplate,
|
||||
regexp: typeTemplate,
|
||||
email: typeTemplate,
|
||||
url: typeTemplate,
|
||||
hex: typeTemplate,
|
||||
},
|
||||
string: {
|
||||
len: '${label} musí být ${len} znaků',
|
||||
min: '${label} musí být alespoň ${min} znaků',
|
||||
max: '${label} musí být do ${max} znaků',
|
||||
range: '${label} musí být mezi ${min}-${max} znaky',
|
||||
},
|
||||
number: {
|
||||
len: '${label} musí být stejný jako ${len}',
|
||||
min: '${label} musí být minimálně ${min}',
|
||||
max: '${label} musí být maximálně ${max}',
|
||||
range: '${label} musí být mezi ${min}-${max}',
|
||||
},
|
||||
array: {
|
||||
len: 'Musí být ${len} ${label}',
|
||||
min: 'Alespoň ${min} ${label}',
|
||||
max: 'Nejvíc ${max} ${label}',
|
||||
range: 'Počet ${label} musí být mezi ${min}-${max}',
|
||||
},
|
||||
pattern: {
|
||||
mismatch: '${label} neodpovídá vzoru ${pattern}',
|
||||
},
|
||||
},
|
||||
},
|
||||
Image: {
|
||||
preview: 'Náhled',
|
||||
},
|
||||
};
|
||||
|
||||
export default localeValues;
|
||||
|
@ -22,6 +22,7 @@ const localeValues: Locale = {
|
||||
filterConfirm: 'OK',
|
||||
filterReset: 'Сбросить',
|
||||
filterEmptyText: 'Без фильтров',
|
||||
filterCheckall: 'Выбрать все элементы',
|
||||
emptyText: 'Нет данных',
|
||||
selectAll: 'Выбрать всё',
|
||||
selectInvert: 'Инвертировать выбор',
|
||||
|
@ -112,6 +112,7 @@ exports[`renders ./components/mentions/demo/form.md extend context correctly 1`]
|
||||
class="ant-mentions"
|
||||
>
|
||||
<textarea
|
||||
aria-required="true"
|
||||
class="rc-textarea"
|
||||
id="bio"
|
||||
placeholder="You can use @ to ref user here"
|
||||
|
@ -112,6 +112,7 @@ exports[`renders ./components/mentions/demo/form.md correctly 1`] = `
|
||||
class="ant-mentions"
|
||||
>
|
||||
<textarea
|
||||
aria-required="true"
|
||||
class="rc-textarea"
|
||||
id="bio"
|
||||
placeholder="You can use @ to ref user here"
|
||||
|
@ -124,7 +124,7 @@ const getThemeStyle = (token: MenuToken): CSSInterpolation => {
|
||||
},
|
||||
|
||||
[`${componentCls}-item, ${componentCls}-submenu-title`]: {
|
||||
'&:focus-visible': {
|
||||
[`&:not(${componentCls}-item-disabled):focus-visible`]: {
|
||||
...accessibilityFocus(token),
|
||||
},
|
||||
},
|
||||
|
@ -5,12 +5,14 @@ import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import { resetWarned } from 'rc-util/lib/warning';
|
||||
import * as React from 'react';
|
||||
import TestUtils from 'react-dom/test-utils';
|
||||
import type { ModalFuncProps } from '..';
|
||||
import Modal from '..';
|
||||
import { sleep, act } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import type { ModalFunc } from '../confirm';
|
||||
import destroyFns from '../destroyFns';
|
||||
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
||||
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
const { confirm } = Modal;
|
||||
|
||||
@ -20,7 +22,7 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
// Inject CSSMotion to replace with No transition support
|
||||
const MockCSSMotion = genCSSMotion(false);
|
||||
Object.keys(MockCSSMotion).forEach(key => {
|
||||
CSSMotion[key] = MockCSSMotion[key];
|
||||
(CSSMotion as any)[key] = (MockCSSMotion as any)[key];
|
||||
});
|
||||
|
||||
// // Mock for rc-util raf
|
||||
@ -67,11 +69,11 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
|
||||
function $$(className) {
|
||||
return document.body.querySelectorAll(className);
|
||||
function $$(className: string) {
|
||||
return document.body.querySelectorAll<HTMLElement>(className);
|
||||
}
|
||||
|
||||
function open(args) {
|
||||
function open(args?: ModalFuncProps) {
|
||||
jest.useFakeTimers();
|
||||
confirm({
|
||||
title: 'Want to delete these items?',
|
||||
@ -242,23 +244,22 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
});
|
||||
|
||||
it('allows extra props on buttons', async () => {
|
||||
open({ okButtonProps: { disabled: true }, cancelButtonProps: { 'data-test': 'baz' } });
|
||||
open({
|
||||
okButtonProps: { disabled: true },
|
||||
cancelButtonProps: { 'data-test': 'baz' } as ModalFuncProps['cancelButtonProps'],
|
||||
});
|
||||
|
||||
await sleep();
|
||||
expect($$('.ant-btn')).toHaveLength(2);
|
||||
expect($$('.ant-btn')[0].attributes['data-test'].value).toBe('baz');
|
||||
expect($$('.ant-btn')[1].disabled).toBe(true);
|
||||
expect(($$('.ant-btn')[0].attributes as any)['data-test'].value).toBe('baz');
|
||||
expect(($$('.ant-btn')[1] as HTMLButtonElement).disabled).toBe(true);
|
||||
});
|
||||
|
||||
describe('should close modals when click confirm button', () => {
|
||||
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
|
||||
it(type, async () => {
|
||||
jest.useFakeTimers();
|
||||
Modal[type]({
|
||||
title: 'title',
|
||||
content: 'content',
|
||||
});
|
||||
|
||||
Modal[type]?.({ title: 'title', content: 'content' });
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
await sleep();
|
||||
@ -325,13 +326,13 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
});
|
||||
|
||||
describe('should not close modals when click confirm button when onOk has argument', () => {
|
||||
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
|
||||
(['confirm', 'info', 'success', 'warning', 'error'] as const).forEach(type => {
|
||||
it(type, async () => {
|
||||
jest.useFakeTimers();
|
||||
Modal[type]({
|
||||
Modal[type]?.({
|
||||
title: 'title',
|
||||
content: 'content',
|
||||
onOk: close => null, // eslint-disable-line no-unused-vars
|
||||
onOk: _ => null, // eslint-disable-line no-unused-vars
|
||||
});
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
@ -351,10 +352,10 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
});
|
||||
|
||||
describe('could be update by new config', () => {
|
||||
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
|
||||
it(type, async () => {
|
||||
jest.useFakeTimers();
|
||||
const instance = Modal[type]({
|
||||
const instance = Modal[type]?.({
|
||||
title: 'title',
|
||||
content: 'content',
|
||||
});
|
||||
@ -387,17 +388,12 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
});
|
||||
|
||||
describe('could be update by call function', () => {
|
||||
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
|
||||
it(type, () => {
|
||||
jest.useFakeTimers();
|
||||
const instance = Modal[type]({
|
||||
const instance = Modal[type]?.({
|
||||
title: 'title',
|
||||
okButtonProps: {
|
||||
loading: true,
|
||||
style: {
|
||||
color: 'red',
|
||||
},
|
||||
},
|
||||
okButtonProps: { loading: true, style: { color: 'red' } },
|
||||
});
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
@ -434,10 +430,10 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
});
|
||||
|
||||
describe('could be destroy', () => {
|
||||
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
|
||||
jest.useFakeTimers();
|
||||
it(type, async () => {
|
||||
const instance = Modal[type]({
|
||||
const instance = Modal[type]?.({
|
||||
title: 'title',
|
||||
content: 'content',
|
||||
});
|
||||
@ -462,8 +458,8 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
// Show
|
||||
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||
Modal[type]({
|
||||
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
|
||||
Modal[type]?.({
|
||||
title: 'title',
|
||||
content: 'content',
|
||||
});
|
||||
@ -515,9 +511,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
const instances = [];
|
||||
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||
const instance = Modal[type]({
|
||||
const instances: ReturnType<ModalFunc>[] = [];
|
||||
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
|
||||
const instance = Modal[type]?.({
|
||||
title: 'title',
|
||||
content: 'content',
|
||||
});
|
||||
@ -588,8 +584,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
it('ok button should trigger onOk multiple times when onOk has close argument', async () => {
|
||||
const onOk = jest.fn();
|
||||
open({
|
||||
onOk: close => {
|
||||
onOk(close?: any) {
|
||||
onOk();
|
||||
// @ts-ignore
|
||||
(() => {})(close); // do nothing
|
||||
},
|
||||
});
|
||||
@ -615,7 +612,7 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
expect(document.querySelectorAll('.my-btn').length).toBe(2);
|
||||
expect(document.querySelectorAll('.bamboo-smile').length).toBe(1);
|
||||
expect(document.querySelectorAll('.my-modal-confirm').length).toBe(1);
|
||||
ConfigProvider.config({ prefixCls: 'ant', iconPrefixCls: null });
|
||||
ConfigProvider.config({ prefixCls: 'ant', iconPrefixCls: undefined });
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
@ -702,12 +699,12 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
});
|
||||
|
||||
describe('the callback close should be a method when onCancel has a close parameter', () => {
|
||||
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
|
||||
(['confirm', 'info', 'success', 'warning', 'error'] as const).forEach(type => {
|
||||
it(`click the close icon to trigger ${type} onCancel`, async () => {
|
||||
jest.useFakeTimers();
|
||||
const mock = jest.fn();
|
||||
|
||||
Modal[type]({
|
||||
Modal[type]?.({
|
||||
closable: true,
|
||||
onCancel: close => mock(close),
|
||||
});
|
||||
@ -732,12 +729,12 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
});
|
||||
});
|
||||
|
||||
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
|
||||
(['confirm', 'info', 'success', 'warning', 'error'] as const).forEach(type => {
|
||||
it(`press ESC to trigger ${type} onCancel`, async () => {
|
||||
jest.useFakeTimers();
|
||||
const mock = jest.fn();
|
||||
|
||||
Modal[type]({
|
||||
Modal[type]?.({
|
||||
keyboard: true,
|
||||
onCancel: close => mock(close),
|
||||
});
|
||||
@ -770,12 +767,12 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
});
|
||||
});
|
||||
|
||||
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
|
||||
(['confirm', 'info', 'success', 'warning', 'error'] as const).forEach(type => {
|
||||
it(`click the mask to trigger ${type} onCancel`, async () => {
|
||||
jest.useFakeTimers();
|
||||
const mock = jest.fn();
|
||||
|
||||
Modal[type]({
|
||||
Modal[type]?.({
|
||||
maskClosable: true,
|
||||
onCancel: close => mock(close),
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user