chore: auto merge branches (#36683)

chore: Feature merge master
This commit is contained in:
github-actions[bot] 2022-07-25 10:02:34 +00:00 committed by GitHub
commit 6386431c49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2118 additions and 228 deletions

View File

@ -28,7 +28,6 @@ jobs:
branch: 'master'
dingding-token: ${{ secrets.DINGDING_BOT_TOKEN }}
dingding-msg: 'CHANGELOG.zh-CN.md'
dingding-delay-minute: 10
msg-title: '# Ant Design {{v}} 发布日志'
msg-poster: 'https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*zx7LTI_ECSAAAAAAAAAAAABkARQnAQ'
msg-footer: '💬 前往 [**Ant Design Releases**]({{url}}) 查看更新日志'
@ -44,6 +43,7 @@ jobs:
dingding-token: ${{ secrets.DINGDING_BOT_BIGFISH_TOKEN }}
dingding-msg: 'CHANGELOG.zh-CN.md'
dingding-delay-minute: 10
release: false
antd-conch-msg: '🐟 当前 Bigfish 内嵌 antd 版本:'
msg-title: '# Ant Design {{v}} 发布日志'
msg-poster: 'https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*zx7LTI_ECSAAAAAAAAAAAABkARQnAQ'

View File

@ -1,7 +1,6 @@
import { mount } from 'enzyme';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { render, sleep } from '../../../tests/utils';
import { sleep, render, fireEvent } from '../../../tests/utils';
import { resetWarned } from '../../_util/warning';
describe('Collapse', () => {
@ -10,6 +9,15 @@ describe('Collapse', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
// fix React concurrent
function triggerAllTimer() {
for (let i = 0; i < 10; i += 1) {
act(() => {
jest.runAllTimers();
});
}
}
beforeEach(() => {
resetWarned();
});
@ -23,16 +31,16 @@ describe('Collapse', () => {
});
it('should support remove expandIcon', () => {
const wrapper = mount(
const { asFragment } = render(
<Collapse expandIcon={() => null}>
<Collapse.Panel header="header" />
</Collapse>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(asFragment().firstChild).toMatchSnapshot();
});
it('should keep the className of the expandIcon', () => {
const wrapper = mount(
const { container } = render(
<Collapse
expandIcon={() => (
<button type="button" className="custom-expandicon-classname">
@ -44,49 +52,51 @@ describe('Collapse', () => {
</Collapse>,
);
expect(wrapper.find('.custom-expandicon-classname').exists()).toBe(true);
expect(container.querySelectorAll('.custom-expandicon-classname').length).toBe(1);
});
it('should render extra node of panel', () => {
const wrapper = mount(
const { asFragment } = render(
<Collapse>
<Collapse.Panel header="header" extra={<button type="button">action</button>} />
<Collapse.Panel header="header" extra={<button type="button">action</button>} />
</Collapse>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(asFragment().firstChild).toMatchSnapshot();
});
it('could be expand and collapse', async () => {
const wrapper = mount(
const { container } = render(
<Collapse>
<Collapse.Panel header="This is panel header 1" key="1">
content
</Collapse.Panel>
</Collapse>,
);
expect(wrapper.find('.ant-collapse-item').hasClass('ant-collapse-item-active')).toBe(false);
wrapper.find('.ant-collapse-header').at(0).simulate('click');
wrapper.update();
expect(
container.querySelector('.ant-collapse-item')?.classList.contains('ant-collapse-item-active'),
).toBe(false);
fireEvent.click(container.querySelector('.ant-collapse-header')!);
await sleep(400);
wrapper.update();
expect(wrapper.find('.ant-collapse-item').hasClass('ant-collapse-item-active')).toBe(true);
expect(
container.querySelector('.ant-collapse-item')?.classList.contains('ant-collapse-item-active'),
).toBe(true);
});
it('could override default openMotion', () => {
const wrapper = mount(
const { container, asFragment } = render(
<Collapse openMotion={{}}>
<Collapse.Panel header="This is panel header 1" key="1">
content
</Collapse.Panel>
</Collapse>,
);
wrapper.find('.ant-collapse-header').at(0).simulate('click');
expect(wrapper.render()).toMatchSnapshot();
fireEvent.click(container.querySelector('.ant-collapse-header')!);
expect(asFragment().firstChild).toMatchSnapshot();
});
it('should trigger warning and keep compatibility when using disabled in Panel', () => {
const wrapper = mount(
const { container } = render(
<Collapse>
<Collapse.Panel disabled header="This is panel header 1" key="1">
content
@ -98,19 +108,19 @@ describe('Collapse', () => {
'Warning: [antd: Collapse.Panel] `disabled` is deprecated. Please use `collapsible="disabled"` instead.',
);
expect(wrapper.find('.ant-collapse-item-disabled').length).toBe(1);
expect(container.querySelectorAll('.ant-collapse-item-disabled').length).toBe(1);
wrapper.find('.ant-collapse-header').simulate('click');
expect(wrapper.find('.ant-collapse-item-active').length).toBe(0);
fireEvent.click(container.querySelector('.ant-collapse-header')!);
expect(container.querySelectorAll('.ant-collapse-item-active').length).toBe(0);
});
it('should end motion when set activeKey while hiding', async () => {
jest.useFakeTimers();
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => {
setTimeout(cb, 16.66);
});
const spiedRAF = jest
.spyOn(window, 'requestAnimationFrame')
.mockImplementation(cb => setTimeout(cb, 16.66));
let setActiveKeyOuter;
let setActiveKeyOuter: React.Dispatch<React.SetStateAction<React.Key | undefined>>;
const Test = () => {
const [activeKey, setActiveKey] = React.useState();
setActiveKeyOuter = setActiveKey;
@ -125,17 +135,18 @@ describe('Collapse', () => {
);
};
const wrapper = mount(<Test />);
const { container } = render(<Test />);
await act(async () => {
setActiveKeyOuter('1');
await Promise.resolve();
jest.runAllTimers();
});
expect(wrapper.render().find('.ant-motion-collapse').length).toBe(0);
triggerAllTimer();
window.requestAnimationFrame.mockRestore();
expect(container.querySelectorAll('.ant-motion-collapse').length).toBe(0);
spiedRAF.mockRestore();
jest.useRealTimers();
});

View File

@ -1,10 +1,10 @@
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
import { mount } from 'enzyme';
import React from 'react';
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
import InputNumber from '..';
import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render } from '../../../tests/utils';
describe('InputNumber', () => {
focusTest(InputNumber, { refFocus: true });
@ -14,32 +14,35 @@ describe('InputNumber', () => {
// https://github.com/ant-design/ant-design/issues/13896
it('should return null when blur a empty input number', () => {
const onChange = jest.fn();
const wrapper = mount(<InputNumber defaultValue="1" onChange={onChange} />);
wrapper.find('input').simulate('change', { target: { value: '' } });
const { container } = render(<InputNumber defaultValue="1" onChange={onChange} />);
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 wrapper = mount(<InputNumber defaultValue={1} onStep={onStep} />);
wrapper.find('.ant-input-number-handler-up').simulate('mousedown');
const { container } = render(<InputNumber defaultValue={1} onStep={onStep} />);
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-up'));
expect(onStep).toBeCalledTimes(1);
expect(onStep).toHaveBeenLastCalledWith(2, { offset: 1, type: 'up' });
wrapper.find('.ant-input-number-handler-down').simulate('mousedown');
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-down'));
expect(onStep).toBeCalledTimes(2);
expect(onStep).toHaveBeenLastCalledWith(1, { offset: 1, type: 'down' });
});
it('renders correctly when controls is boolean', () => {
expect(mount(<InputNumber controls={false} />).render()).toMatchSnapshot();
const { asFragment } = render(<InputNumber controls={false} />);
expect(asFragment().firstChild).toMatchSnapshot();
});
it('renders correctly when controls is {}', () => {
expect(mount(<InputNumber controls={{}} />).render()).toMatchSnapshot();
const { asFragment } = render(<InputNumber controls={{}} />);
expect(asFragment().firstChild).toMatchSnapshot();
});
it('renders correctly when controls has custom upIcon and downIcon', () => {
const wrapper = mount(
const { asFragment } = render(
<InputNumber
controls={{
upIcon: <ArrowUpOutlined />,
@ -47,11 +50,11 @@ describe('InputNumber', () => {
}}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(asFragment().firstChild).toMatchSnapshot();
});
it('should support className', () => {
const wrapper = mount(
const { container } = render(
<InputNumber
controls={{
upIcon: <ArrowUpOutlined className="my-class-name" />,
@ -59,11 +62,11 @@ describe('InputNumber', () => {
}}
/>,
);
expect(wrapper.find('.anticon-arrow-up').getDOMNode().className.includes('my-class-name')).toBe(
expect(container.querySelector('.anticon-arrow-up')?.className.includes('my-class-name')).toBe(
true,
);
expect(
wrapper.find('.anticon-arrow-down').getDOMNode().className.includes('my-class-name'),
container.querySelector('.anticon-arrow-down')?.className.includes('my-class-name'),
).toBe(true);
});
});

View File

@ -1,25 +1,24 @@
import { render } from '@testing-library/react';
import { mount } from 'enzyme';
import React from 'react';
import React, { forwardRef } from 'react';
import InputNumber from '..';
import focusTest from '../../../tests/shared/focusTest';
import { fireEvent, render } from '../../../tests/utils';
describe('prefix', () => {
focusTest(
React.forwardRef((props, ref) => <InputNumber {...props} prefix="A" ref={ref} />),
forwardRef((props, ref) => <InputNumber {...props} prefix="A" ref={ref} />),
{ refFocus: true },
);
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?.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 wrapper = mount(<InputNumber prefix={<i>123</i>} />);
const { container } = render(<InputNumber prefix={<i>123</i>} />);
const mockFocus = jest.spyOn(wrapper.find('input').getDOMNode(), 'focus');
wrapper.find('i').simulate('mouseUp');
const mockFocus = jest.spyOn(container.querySelector('input'), 'focus');
fireEvent.mouseUp(container.querySelector('i'));
expect(mockFocus).toBeCalled();
});
});

View File

@ -39,7 +39,7 @@ export function resolveOnChange<E extends HTMLInputElement | HTMLTextAreaElement
if (!onChange) {
return;
}
let event = e;
let event = e as React.ChangeEvent<E>;
if (e.type === 'click') {
// Clone a new target for event.
@ -63,7 +63,7 @@ export function resolveOnChange<E extends HTMLInputElement | HTMLTextAreaElement
});
currentTarget.value = '';
onChange(event as React.ChangeEvent<E>);
onChange(event);
return;
}
@ -75,10 +75,10 @@ export function resolveOnChange<E extends HTMLInputElement | HTMLTextAreaElement
});
target.value = targetValue;
onChange(event as React.ChangeEvent<E>);
onChange(event);
return;
}
onChange(event as React.ChangeEvent<E>);
onChange(event);
}
export function triggerFocus(

View File

@ -119,7 +119,7 @@ The legacy demo code for version `<4.20.0` could be found at [https://github.com
#### SubMenuType
| Param | Description | Type | Default value | Version |
| --- | --- | --- | --- | --- |
| --- | --- | --- | --- | --- | --- |
| children | Sub-menus or sub-menu items | [ItemType\[\]](#ItemType) | - | |
| disabled | Whether sub-menu is disabled | boolean | false | |
| icon | Icon of sub menu | ReactNode | - | |
@ -166,3 +166,14 @@ const dividerItem = {
### Why will Menu's children be rendered twice?
Menu collects structure info with [twice-render](https://github.com/react-component/menu/blob/f4684514096d6b7123339cbe72e7b0f68db0bce2/src/Menu.tsx#L543) to support HOC usage. Merging into one render may cause the logic to become much more complex. Contributions to help improve the collection logic are welcomed.
### Why Menu do not responsive collapse in Flex layout?
Menu will render fully item in flex layout and then collapse it. You need tell flex not consider Menu width to enable responsive ([online demo](https://codesandbox.io/s/ding-bu-dao-hang-antd-4-21-7-forked-5e3imy?file=/demo.js)):
```jsx
<div style={{ flex }}>
<div style={{ ... }}>Some Content</div>
<Menu style={{ minWidth: 0, flex: "auto" }} />
</div>
```

View File

@ -120,7 +120,7 @@ return <Menu items={items} />;
#### SubMenuType
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| --- | --- | --- | --- | --- | --- |
| children | 子菜单的菜单项 | [ItemType\[\]](#ItemType) | - | |
| disabled | 是否禁用 | boolean | false | |
| icon | 菜单图标 | ReactNode | - | |
@ -167,3 +167,14 @@ const dividerItem = {
### 为何 Menu 的子元素会渲染两次?
Menu 通过[二次渲染](https://github.com/react-component/menu/blob/f4684514096d6b7123339cbe72e7b0f68db0bce2/src/Menu.tsx#L543)收集嵌套结构信息以支持 HOC 的结构。合并成一个推导结构会使得逻辑变得十分复杂,欢迎 PR 以协助改进该设计。
### 在 Flex 布局中Menu 没有按照预期响应式省略菜单?
Menu 初始化时会先全部渲染,然后根据宽度裁剪内容。当处于 Flex 布局中,你需要告知其预期宽度为响应式宽度([在线 Demo](https://codesandbox.io/s/ding-bu-dao-hang-antd-4-21-7-forked-5e3imy?file=/demo.js)
```jsx
<div style={{ flex }}>
<div style={{ ... }}>Some Content</div>
<Menu style={{ minWidth: 0, flex: "auto" }} />
</div>
```

View File

@ -305,7 +305,7 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});
describe('should not close modals when click confirm button when onOk has argument', () => {
['info', 'success', 'warning', 'error'].forEach(type => {
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
it(type, async () => {
jest.useFakeTimers();
Modal[type]({
@ -318,7 +318,7 @@ describe('Modal.confirm triggers callbacks correctly', () => {
await sleep();
});
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
$$('.ant-btn')[0].click();
$$('.ant-btn-primary')[0].click();
await act(async () => {
jest.runAllTimers();
@ -674,4 +674,149 @@ describe('Modal.confirm triggers callbacks correctly', () => {
const { width } = $$('.ant-modal-body')[0].style;
expect(width).toBe('500px');
});
describe('the callback close should be a method when onCancel has a close parameter', () => {
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
it(`click the close icon to trigger ${type} onCancel`, async () => {
jest.useFakeTimers();
const mock = jest.fn();
Modal[type]({
closable: true,
onCancel: close => mock(close),
});
await act(async () => {
jest.runAllTimers();
await sleep();
});
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
$$('.ant-modal-close')[0].click();
await act(async () => {
jest.runAllTimers();
await sleep();
});
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0);
expect(mock).toBeCalledWith(expect.any(Function));
jest.useRealTimers();
});
});
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
it(`press ESC to trigger ${type} onCancel`, async () => {
jest.useFakeTimers();
const mock = jest.fn();
Modal[type]({
keyboard: true,
onCancel: close => mock(close),
});
jest.runAllTimers();
await sleep();
jest.runAllTimers();
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
TestUtils.Simulate.keyDown($$('.ant-modal')[0], {
keyCode: KeyCode.ESC,
});
jest.runAllTimers();
await sleep(0);
jest.runAllTimers();
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0);
expect(mock).toBeCalledWith(expect.any(Function));
jest.useRealTimers();
});
});
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
it(`click the mask to trigger ${type} onCancel`, async () => {
jest.useFakeTimers();
const mock = jest.fn();
Modal[type]({
maskClosable: true,
onCancel: close => mock(close),
});
await act(async () => {
jest.runAllTimers();
await sleep();
});
expect($$('.ant-modal-mask')).toHaveLength(1);
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
$$('.ant-modal-wrap')[0].click();
await act(async () => {
jest.runAllTimers();
await sleep();
});
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0);
expect(mock).toBeCalledWith(expect.any(Function));
jest.useRealTimers();
});
});
});
it('confirm modal click Cancel button close callback is a function', async () => {
jest.useFakeTimers();
const mock = jest.fn();
Modal.confirm({
onCancel: close => mock(close),
});
await act(async () => {
jest.runAllTimers();
await sleep();
});
$$('.ant-modal-confirm-btns > .ant-btn')[0].click();
await act(async () => {
jest.runAllTimers();
await sleep();
});
expect(mock).toBeCalledWith(expect.any(Function));
jest.useRealTimers();
});
it('close can close modal when onCancel has a close parameter', async () => {
jest.useFakeTimers();
Modal.confirm({
onCancel: close => close(),
});
await act(async () => {
jest.runAllTimers();
await sleep();
});
expect($$('.ant-modal-confirm-confirm')).toHaveLength(1);
$$('.ant-modal-confirm-btns > .ant-btn')[0].click();
await act(async () => {
jest.runAllTimers();
await sleep();
});
expect($$('.ant-modal-confirm-confirm')).toHaveLength(0);
jest.useRealTimers();
});
});

View File

@ -1,9 +1,11 @@
import CSSMotion from 'rc-motion';
import { genCSSMotion } from 'rc-motion/lib/CSSMotion';
import KeyCode from 'rc-util/lib/KeyCode';
import React from 'react';
import { act } from 'react-dom/test-utils';
import TestUtils, { act } from 'react-dom/test-utils';
import Modal from '..';
import { fireEvent, render } from '../../../tests/utils';
import { fireEvent, render, sleep } from '../../../tests/utils';
import Button from '../../button';
import ConfigProvider from '../../config-provider';
import Input from '../../input';
@ -193,4 +195,110 @@ describe('Modal.hook', () => {
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
expect(document.body.classList.contains('ant-modal-confirm-title')).toBeFalsy();
});
it('the callback close should be a method when onCancel has a close parameter', async () => {
jest.useFakeTimers();
const clear = async function clear() {
await act(async () => {
jest.runAllTimers();
await sleep();
});
};
const mockFn = jest.fn();
const Demo = () => {
const [modal, contextHolder] = Modal.useModal();
const openBrokenModal = React.useCallback(() => {
modal.confirm({
closable: true,
keyboard: true,
maskClosable: true,
onCancel: close => mockFn(close),
});
}, [modal]);
return (
<div className="App">
{contextHolder}
<div className="open-hook-modal-btn" onClick={openBrokenModal}>
Test hook modal
</div>
</div>
);
};
const { container } = render(<Demo />);
await clear();
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(0);
// First open
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
await clear();
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(1);
// Click mask to close
fireEvent.click(document.body.querySelectorAll('.ant-modal-wrap')[0]);
await clear();
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(0);
// Second open
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
await clear();
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(1);
// Press ESC to turn off
TestUtils.Simulate.keyDown(document.body.querySelectorAll('.ant-modal')[0], {
keyCode: KeyCode.ESC,
});
await clear();
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(0);
// Third open
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
await clear();
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(1);
// Click the close icon to close
fireEvent.click(document.body.querySelectorAll('.ant-modal-close')[0]);
await clear();
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(0);
// Last open
fireEvent.click(container.querySelectorAll('.open-hook-modal-btn')[0]);
await clear();
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(1);
// Click the Cancel button to close (invalid)
fireEvent.click(document.body.querySelectorAll('.ant-modal-confirm-btns > .ant-btn')[0]);
await clear();
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(1);
mockFn.mockImplementation(close => close());
// Click the Cancel button to close (valid)
fireEvent.click(document.body.querySelectorAll('.ant-modal-confirm-btns > .ant-btn')[0]);
await clear();
expect(document.body.querySelectorAll('.ant-modal-confirm-confirm')).toHaveLength(0);
// Close called 5 times
expect(mockFn).toHaveBeenCalledTimes(5);
expect(mockFn.mock.calls).toEqual(Array.from({ length: 5 }, () => [expect.any(Function)]));
jest.useRealTimers();
});
});

View File

@ -34,7 +34,7 @@ export default function confirm(config: ModalFuncProps) {
function destroy(...args: any[]) {
const triggerCancel = args.some(param => param && param.triggerCancel);
if (config.onCancel && triggerCancel) {
config.onCancel(...args);
config.onCancel(() => {}, ...args.slice(1));
}
for (let i = 0; i < destroyFns.length; i++) {
const fn = destroyFns[i];

View File

@ -36,7 +36,7 @@ const HookModal: React.ForwardRefRenderFunction<HookModalRef, HookModalProps> =
setVisible(false);
const triggerCancel = args.some(param => param && param.triggerCancel);
if (innerConfig.onCancel && triggerCancel) {
innerConfig.onCancel();
innerConfig.onCancel(() => {}, ...args.slice(1));
}
};

View File

@ -25,7 +25,7 @@ exports[`Radio Group passes prefixCls down to radio 1`] = `
</label>
<label
class="my-radio-wrapper"
style="font-size:12px"
style="font-size: 12px;"
>
<span
class="my-radio"

View File

@ -29,6 +29,7 @@ exports[`Radio Button should render correctly 1`] = `
<input
class="ant-radio-button-input"
type="radio"
value=""
/>
<span
class="ant-radio-button-inner"
@ -65,7 +66,7 @@ exports[`Radio Group passes prefixCls down to radio 1`] = `
</label>
<label
class="my-radio-wrapper"
style="font-size:12px"
style="font-size: 12px;"
>
<span
class="my-radio"

View File

@ -54,6 +54,7 @@ exports[`Radio should render correctly 1`] = `
<input
class="ant-radio-input"
type="radio"
value=""
/>
<span
class="ant-radio-inner"

View File

@ -1,8 +1,8 @@
import React from 'react';
import { mount, render } from 'enzyme';
import { fireEvent, render as testLibRender } from '@testing-library/react';
import Radio from '..';
import { render, fireEvent } from '../../../tests/utils';
describe('Radio Group', () => {
function createRadioGroup(props) {
return (
@ -28,32 +28,37 @@ describe('Radio Group', () => {
const onMouseEnter = jest.fn();
const onMouseLeave = jest.fn();
const wrapper = mount(
const { container } = render(
<Radio.Group onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
<Radio />
</Radio.Group>,
);
wrapper.find('div').at(0).simulate('mouseenter');
fireEvent.mouseEnter(container.querySelector('div'));
expect(onMouseEnter).toHaveBeenCalled();
wrapper.find('div').at(0).simulate('mouseleave');
fireEvent.mouseLeave(container.querySelector('div'));
expect(onMouseLeave).toHaveBeenCalled();
});
it('fire change events when value changes', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container, rerender } = render(
createRadioGroup({
onChange,
}),
);
const radios = wrapper.find('input');
const radios = container.querySelectorAll('input');
// controlled component
wrapper.setProps({ value: 'A' });
radios.at(1).simulate('change');
rerender(
createRadioGroup({
onChange,
value: 'A',
}),
);
fireEvent.click(radios[1]);
expect(onChange.mock.calls.length).toBe(1);
});
@ -61,83 +66,95 @@ describe('Radio Group', () => {
const onChange = jest.fn();
const onChangeRadioGroup = jest.fn();
const wrapper = mount(
<Radio.Group onChange={onChangeRadioGroup}>
<Radio value="A" onChange={onChange}>
const RadioGroup = props => (
<Radio.Group onChange={props.onChangeRadioGroup}>
<Radio value="A" onChange={props.onChange}>
A
</Radio>
<Radio value="B" onChange={onChange}>
<Radio value="B" onChange={props.onChange}>
B
</Radio>
<Radio value="C" onChange={onChange}>
<Radio value="C" onChange={props.onChange}>
C
</Radio>
</Radio.Group>,
</Radio.Group>
);
const radios = wrapper.find('input');
const { container, rerender } = render(
<RadioGroup onChangeRadioGroup={onChangeRadioGroup} onChange={onChange} />,
);
const radios = container.querySelectorAll('input');
// controlled component
wrapper.setProps({ value: 'A' });
radios.at(1).simulate('change');
rerender(<RadioGroup value="A" onChangeRadioGroup={onChangeRadioGroup} onChange={onChange} />);
fireEvent.click(radios[1]);
expect(onChange.mock.calls.length).toBe(1);
expect(onChangeRadioGroup.mock.calls.length).toBe(1);
});
it('Trigger onChange when both of radioButton and radioGroup exists', () => {
const onChange = jest.fn();
const wrapper = mount(
<Radio.Group onChange={onChange}>
const RadioGroup = props => (
<Radio.Group {...props}>
<Radio.Button value="A">A</Radio.Button>
<Radio.Button value="B">B</Radio.Button>
<Radio.Button value="C">C</Radio.Button>
</Radio.Group>,
</Radio.Group>
);
const radios = wrapper.find('input');
const { container, rerender } = render(<RadioGroup onChange={onChange} />);
const radios = container.querySelectorAll('input');
// controlled component
wrapper.setProps({ value: 'A' });
radios.at(1).simulate('change');
rerender(<RadioGroup value="A" onChange={onChange} />);
fireEvent.click(radios[1]);
expect(onChange.mock.calls.length).toBe(1);
});
it('should only trigger once when in group with options', () => {
const onChange = jest.fn();
const options = [{ label: 'Bamboo', value: 'Bamboo' }];
const wrapper = mount(<Radio.Group options={options} onChange={onChange} />);
const { container } = render(<Radio.Group options={options} onChange={onChange} />);
wrapper.find('input').simulate('change');
fireEvent.click(container.querySelector('input'));
expect(onChange).toHaveBeenCalledTimes(1);
});
it("won't fire change events when value not changes", () => {
const onChange = jest.fn();
const wrapper = mount(
const { container, rerender } = render(
createRadioGroup({
onChange,
}),
);
const radios = wrapper.find('input');
const radios = container.querySelectorAll('input');
// controlled component
wrapper.setProps({ value: 'A' });
radios.at(0).simulate('change');
rerender(
createRadioGroup({
onChange,
value: 'A',
}),
);
fireEvent.click(radios[0]);
expect(onChange.mock.calls.length).toBe(0);
});
it('optional should correct render', () => {
const wrapper = mount(createRadioGroupByOption());
const radios = wrapper.find('input');
const { container } = render(createRadioGroupByOption());
const radios = container.querySelectorAll('input');
expect(radios.length).toBe(3);
});
it('all children should have a name property', () => {
const GROUP_NAME = 'radiogroup';
const wrapper = mount(createRadioGroup({ name: GROUP_NAME }));
const GROUP_NAME = 'GROUP_NAME';
const { container } = render(createRadioGroup({ name: GROUP_NAME }));
wrapper.find('input[type="radio"]').forEach(el => {
expect(el.props().name).toEqual(GROUP_NAME);
container.querySelectorAll('input[type="radio"]').forEach(el => {
expect(el.name).toEqual(GROUP_NAME);
});
});
@ -146,13 +163,13 @@ describe('Radio Group', () => {
{ label: 'Apple', value: 'Apple' },
{ label: 'Orange', value: 'Orange', style: { fontSize: 12 } },
];
const wrapper = render(<Radio.Group prefixCls="my-radio" options={options} />);
expect(wrapper).toMatchSnapshot();
const { container } = render(<Radio.Group prefixCls="my-radio" options={options} />);
expect(container.firstChild).toMatchSnapshot();
});
it('should forward ref', () => {
let radioGroupRef;
const { container } = testLibRender(
const { container } = render(
createRadioGroupByOption({
ref: ref => {
radioGroupRef = ref;
@ -164,7 +181,7 @@ describe('Radio Group', () => {
});
it('should support data-* or aria-* props', () => {
const { container } = testLibRender(
const { container } = render(
createRadioGroup({
'data-radio-group-id': 'radio-group-id',
'aria-label': 'radio-group',
@ -176,7 +193,7 @@ describe('Radio Group', () => {
it('Radio type should not be override', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container } = render(
<Radio.Group onChange={onChange}>
<Radio value={1} type="1">
A
@ -192,35 +209,28 @@ describe('Radio Group', () => {
</Radio>
</Radio.Group>,
);
const radios = wrapper.find('input');
radios.at(1).simulate('change');
const radios = container.querySelectorAll('input');
fireEvent.click(radios[0]);
expect(onChange).toHaveBeenCalled();
expect(radios.at(1).getDOMNode().type).toBe('radio');
expect(radios[1].type).toBe('radio');
});
describe('value is null or undefined', () => {
it('use `defaultValue` when `value` is undefined', () => {
const options = [{ label: 'Bamboo', value: 'bamboo' }];
const wrapper = mount(
const { container } = render(
<Radio.Group defaultValue="bamboo" value={undefined} options={options} />,
);
expect(wrapper.find('.ant-radio-wrapper').at(0).hasClass('ant-radio-wrapper-checked')).toBe(
true,
);
expect(container.querySelectorAll('.ant-radio-wrapper-checked').length).toBe(1);
});
[undefined, null].forEach(newValue => {
it(`should set value back when value change back to ${newValue}`, () => {
const options = [{ label: 'Bamboo', value: 'bamboo' }];
const wrapper = mount(<Radio.Group value="bamboo" options={options} />);
expect(wrapper.find('.ant-radio-wrapper').at(0).hasClass('ant-radio-wrapper-checked')).toBe(
true,
);
wrapper.setProps({ value: newValue });
wrapper.update();
expect(wrapper.find('.ant-radio-wrapper').at(0).hasClass('ant-radio-wrapper-checked')).toBe(
false,
);
const { container, rerender } = render(<Radio.Group value="bamboo" options={options} />);
expect(container.querySelectorAll('.ant-radio-wrapper-checked').length).toBe(1);
rerender(<Radio.Group value={newValue} options={options} />);
expect(container.querySelectorAll('.ant-radio-wrapper-checked').length).toBe(0);
});
});
});
@ -228,7 +238,7 @@ describe('Radio Group', () => {
it('onBlur & onFocus should work', () => {
const handleBlur = jest.fn();
const handleFocus = jest.fn();
const { container } = testLibRender(
const { container } = render(
<Radio.Group options={['1', '2', '3']} onBlur={handleBlur} onFocus={handleFocus} />,
);
fireEvent.focus(container.firstChild);

View File

@ -1,11 +1,11 @@
import { render as testLibRender } from '@testing-library/react';
import { mount, render } from 'enzyme';
import React from 'react';
import Radio, { Button } from '..';
import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { render, fireEvent } from '../../../tests/utils';
describe('Radio Button', () => {
focusTest(Button, { refFocus: true });
mountTest(Button);
@ -13,20 +13,22 @@ describe('Radio Button', () => {
rtlTest(Button);
it('should render correctly', () => {
const wrapper = render(<Button className="customized">Test</Button>);
expect(wrapper).toMatchSnapshot();
const { container } = render(<Button className="customized">Test</Button>);
expect(container.firstChild).toMatchSnapshot();
});
it('responses hover events', () => {
const onMouseEnter = jest.fn();
const onMouseLeave = jest.fn();
const wrapper = mount(<Button onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />);
const { container } = render(
<Button onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />,
);
wrapper.find('label').simulate('mouseenter');
fireEvent.mouseEnter(container.querySelector('label'));
expect(onMouseEnter).toHaveBeenCalled();
wrapper.find('label').simulate('mouseleave');
fireEvent.mouseLeave(container.querySelector('label'));
expect(onMouseLeave).toHaveBeenCalled();
});
});
@ -46,32 +48,29 @@ describe('Radio Group', () => {
const onMouseEnter = jest.fn();
const onMouseLeave = jest.fn();
const wrapper = mount(
const { container } = render(
<Radio.Group onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
<Radio />
</Radio.Group>,
);
wrapper.find('div').at(0).simulate('mouseenter');
fireEvent.mouseEnter(container.querySelectorAll('div')[0]);
expect(onMouseEnter).toHaveBeenCalled();
wrapper.find('div').at(0).simulate('mouseleave');
fireEvent.mouseLeave(container.querySelectorAll('div')[0]);
expect(onMouseLeave).toHaveBeenCalled();
});
it('fire change events when value changes', () => {
const onChange = jest.fn();
const wrapper = mount(
createRadioGroup({
onChange,
}),
);
const radios = wrapper.find('input');
const { container, rerender } = render(createRadioGroup({ onChange }));
const radios = container.querySelectorAll('input');
// controlled component
wrapper.setProps({ value: 'A' });
radios.at(1).simulate('change');
rerender(createRadioGroup({ onChange, value: 'A' }));
fireEvent.click(radios[1]);
expect(onChange.mock.calls.length).toBe(1);
});
@ -79,7 +78,7 @@ describe('Radio Group', () => {
const onChange = jest.fn();
const onChangeRadioGroup = jest.fn();
const wrapper = mount(
const { container } = render(
<Radio.Group onChange={onChangeRadioGroup}>
<Radio value="A" onChange={onChange}>
A
@ -92,63 +91,69 @@ describe('Radio Group', () => {
</Radio>
</Radio.Group>,
);
const radios = wrapper.find('input');
const radios = container.querySelectorAll('input');
// controlled component
wrapper.setProps({ value: 'A' });
radios.at(1).simulate('change');
fireEvent.click(radios[1]);
expect(onChange.mock.calls.length).toBe(1);
expect(onChangeRadioGroup.mock.calls.length).toBe(1);
});
it('Trigger onChange when both of Button and radioGroup exists', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container, rerender } = render(
<Radio.Group onChange={onChange}>
<Button value="A">A</Button>
<Button value="B">B</Button>
<Button value="C">C</Button>
</Radio.Group>,
);
const radios = wrapper.find('input');
const radios = container.querySelectorAll('input');
// controlled component
wrapper.setProps({ value: 'A' });
radios.at(1).simulate('change');
rerender(
<Radio.Group value="A" onChange={onChange}>
<Button value="A">A</Button>
<Button value="B">B</Button>
<Button value="C">C</Button>
</Radio.Group>,
);
fireEvent.click(radios[1]);
expect(onChange.mock.calls.length).toBe(1);
});
it('should only trigger once when in group with options', () => {
const onChange = jest.fn();
const options = [{ label: 'Bamboo', value: 'Bamboo' }];
const wrapper = mount(<Radio.Group options={options} onChange={onChange} />);
const { container } = render(<Radio.Group options={options} onChange={onChange} />);
wrapper.find('input').simulate('change');
fireEvent.click(container.querySelector('input'));
expect(onChange).toHaveBeenCalledTimes(1);
});
it("won't fire change events when value not changes", () => {
const onChange = jest.fn();
const wrapper = mount(
const { container, rerender } = render(
createRadioGroup({
onChange,
}),
);
const radios = wrapper.find('input');
const radios = container.querySelectorAll('input');
// controlled component
wrapper.setProps({ value: 'A' });
radios.at(0).simulate('change');
rerender(createRadioGroup({ onChange, value: 'A' }));
fireEvent.click(radios[0]);
expect(onChange.mock.calls.length).toBe(0);
});
it('all children should have a name property', () => {
const GROUP_NAME = 'radiogroup';
const wrapper = mount(createRadioGroup({ name: GROUP_NAME }));
const GROUP_NAME = 'GROUP_NAME';
const { container } = render(createRadioGroup({ name: GROUP_NAME }));
wrapper.find('input[type="radio"]').forEach(el => {
expect(el.props().name).toEqual(GROUP_NAME);
container.querySelectorAll('input[type="radio"]').forEach(el => {
expect(el.name).toEqual(GROUP_NAME);
});
});
@ -157,13 +162,13 @@ describe('Radio Group', () => {
{ label: 'Apple', value: 'Apple' },
{ label: 'Orange', value: 'Orange', style: { fontSize: 12 } },
];
const wrapper = render(<Radio.Group prefixCls="my-radio" options={options} />);
expect(wrapper).toMatchSnapshot();
const { container } = render(<Radio.Group prefixCls="my-radio" options={options} />);
expect(container.firstChild).toMatchSnapshot();
});
it('should forward ref', () => {
let radioGroupRef;
const { container } = testLibRender(
const { container } = render(
createRadioGroup({
ref: ref => {
radioGroupRef = ref;
@ -175,7 +180,7 @@ describe('Radio Group', () => {
});
it('should support data-* or aria-* props', () => {
const { container } = testLibRender(
const { container } = render(
createRadioGroup({
'data-radio-group-id': 'radio-group-id',
'aria-label': 'radio-group',
@ -187,7 +192,7 @@ describe('Radio Group', () => {
it('Radio type should not be override', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container } = render(
<Radio.Group onChange={onChange}>
<Radio value={1} type="1">
A
@ -203,48 +208,36 @@ describe('Radio Group', () => {
</Radio>
</Radio.Group>,
);
const radios = wrapper.find('input');
radios.at(1).simulate('change');
const radios = container.querySelectorAll('input');
fireEvent.click(radios[0]);
expect(onChange).toHaveBeenCalled();
expect(radios.at(1).getDOMNode().type).toBe('radio');
expect(radios[1].type).toBe('radio');
});
describe('value is null or undefined', () => {
it('use `defaultValue` when `value` is undefined', () => {
const wrapper = mount(
const { container } = render(
<Radio.Group defaultValue="bamboo" value={undefined}>
<Button value="bamboo">Bamboo</Button>
</Radio.Group>,
);
expect(
wrapper
.find('.ant-radio-button-wrapper')
.at(0)
.hasClass('ant-radio-button-wrapper-checked'),
).toBe(true);
expect(container.querySelectorAll('.ant-radio-button-wrapper-checked').length).toBe(1);
});
[undefined, null].forEach(newValue => {
it(`should set value back when value change back to ${newValue}`, () => {
const wrapper = mount(
const { container, rerender } = render(
<Radio.Group value="bamboo">
<Button value="bamboo">Bamboo</Button>
</Radio.Group>,
);
expect(
wrapper
.find('.ant-radio-button-wrapper')
.at(0)
.hasClass('ant-radio-button-wrapper-checked'),
).toBe(true);
wrapper.setProps({ value: newValue });
wrapper.update();
expect(
wrapper
.find('.ant-radio-button-wrapper')
.at(0)
.hasClass('ant-radio-button-wrapper-checked'),
).toBe(false);
expect(container.querySelectorAll('.ant-radio-button-wrapper-checked').length).toBe(1);
rerender(
<Radio.Group value={newValue}>
<Button value="bamboo">Bamboo</Button>
</Radio.Group>,
);
expect(container.querySelectorAll('.ant-radio-button-wrapper-checked').length).toBe(0);
});
});
});

View File

@ -1,10 +1,11 @@
import { mount, render } from 'enzyme';
import React from 'react';
import Radio, { Button, Group } from '..';
import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { render, fireEvent } from '../../../tests/utils';
describe('Radio', () => {
focusTest(Radio, { refFocus: true });
mountTest(Radio);
@ -16,20 +17,20 @@ describe('Radio', () => {
rtlTest(Button);
it('should render correctly', () => {
const wrapper = render(<Radio className="customized">Test</Radio>);
expect(wrapper).toMatchSnapshot();
const { container } = render(<Radio className="customized">Test</Radio>);
expect(container.firstChild).toMatchSnapshot();
});
it('responses hover events', () => {
const onMouseEnter = jest.fn();
const onMouseLeave = jest.fn();
const wrapper = mount(<Radio onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />);
const { container } = render(<Radio onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />);
wrapper.find('label').simulate('mouseenter');
fireEvent.mouseEnter(container.querySelector('label'));
expect(onMouseEnter).toHaveBeenCalled();
wrapper.find('label').simulate('mouseleave');
fireEvent.mouseLeave(container.querySelector('label'));
expect(onMouseLeave).toHaveBeenCalled();
});
});

View File

@ -1665,9 +1665,9 @@ exports[`renders ./components/select/demo/custom-dropdown-menu.md extend context
<div
class="ant-space-item"
>
<a
class="ant-typography"
style="white-space:nowrap"
<button
class="ant-btn ant-btn-text"
type="button"
>
<span
aria-label="plus"
@ -1692,8 +1692,10 @@ exports[`renders ./components/select/demo/custom-dropdown-menu.md extend context
/>
</svg>
</span>
Add item
</a>
<span>
Add item
</span>
</button>
</div>
</div>
</div>

View File

@ -7,16 +7,17 @@ title:
## zh-CN
使用 `dropdownRender` 对下拉菜单进行自由扩展。自定义内容点击时会关闭浮层,如果不喜欢关闭,可以添加 `onMouseDown={e => e.preventDefault()}` 进行阻止(更多详情见 [#13448](https://github.com/ant-design/ant-design/issues/13448))。
使用 `open` 对下拉菜单进行自由扩展。如果希望点击自定义内容后关闭浮层,你需要使用受控模式自行控制([codesandbox](https://codesandbox.io/s/ji-ben-shi-yong-antd-4-21-7-forked-gnp4cy?file=/demo.js))。
## en-US
Customize the dropdown menu via `dropdownRender`. Dropdown menu will be closed if click `dropdownRender` area, you can prevent it by wrapping `onMouseDown={e => e.preventDefault()}` (see more at [#13448](https://github.com/ant-design/ant-design/issues/13448)).
Customize the dropdown menu via `dropdownRender`. If you want to close the dropdown after clicking the custom content, you need to control `open` prop, here is an [codesandbox](https://codesandbox.io/s/ji-ben-shi-yong-antd-4-21-7-forked-gnp4cy?file=/demo.js).
```tsx
import { PlusOutlined } from '@ant-design/icons';
import { Divider, Input, Select, Space, Typography } from 'antd';
import React, { useState } from 'react';
import { Divider, Input, Select, Space, Button } from 'antd';
import type { InputRef } from 'antd';
import React, { useState, useRef } from 'react';
const { Option } = Select;
@ -25,6 +26,7 @@ let index = 0;
const App: React.FC = () => {
const [items, setItems] = useState(['jack', 'lucy']);
const [name, setName] = useState('');
const inputRef = useRef<InputRef>(null);
const onNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value);
@ -34,6 +36,9 @@ const App: React.FC = () => {
e.preventDefault();
setItems([...items, name || `New item ${index++}`]);
setName('');
setTimeout(() => {
inputRef.current?.focus();
}, 0);
};
return (
@ -44,11 +49,16 @@ const App: React.FC = () => {
<>
{menu}
<Divider style={{ margin: '8px 0' }} />
<Space align="center" style={{ padding: '0 8px 4px' }}>
<Input placeholder="Please enter item" value={name} onChange={onNameChange} />
<Typography.Link onClick={addItem} style={{ whiteSpace: 'nowrap' }}>
<PlusOutlined /> Add item
</Typography.Link>
<Space style={{ padding: '0 8px 4px' }}>
<Input
placeholder="Please enter item"
ref={inputRef}
value={name}
onChange={onNameChange}
/>
<Button type="text" icon={<PlusOutlined />} onClick={addItem}>
Add item
</Button>
</Space>
</>
)}

View File

@ -111,15 +111,15 @@ Select component to select value from options.
It's caused by option with different `label` and `value`. You can use `optionFilterProp="label"` to change filter logic instead.
### The dropdown is closed when click `dropdownRender` area?
### When I click elements in dropdownRender, the select dropdown will not be closed?
Dropdown menu will be closed if click `dropdownRender` area, you can prevent it by wrapping `onMouseDown={e => e.preventDefault()}` (see more at [#13448](https://github.com/ant-design/ant-design/issues/13448)).
You can control it by `open` prop: [codesandbox](https://codesandbox.io/s/ji-ben-shi-yong-antd-4-21-7-forked-gnp4cy?file=/demo.js).
### Why sometime customize Option cause scroll break?
Virtual scroll internal set item height as `32px`. You need to adjust `listItemHeight` when your option height is less and `listHeight` config list container height:
```tsx
```jsx
<Select listItemHeight={10} listHeight={250} />
```

View File

@ -112,9 +112,9 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
这一般是 `options` 中的 `label``value` 不同导致的,你可以通过 `optionFilterProp="label"` 将过滤设置为展示值以避免这种情况。
### 点击 `dropdownRender` 里的内容浮层关闭怎么办
### 点击 `dropdownRender` 里的元素,下拉菜单不会自动消失
自定义内容点击时会关闭浮层,如果不喜欢关闭,可以添加 `onMouseDown={e => e.preventDefault()}` 进行阻止(更多详情见 [#13448](https://github.com/ant-design/ant-design/issues/13448)
你可以使用受控模式,手动设置 `open` 属性:[codesandbox](https://codesandbox.io/s/ji-ben-shi-yong-antd-4-21-7-forked-gnp4cy?file=/demo.js)
### 自定义 Option 样式导致滚动异常怎么办?

View File

@ -26637,6 +26637,709 @@ Array [
]
`;
exports[`renders ./components/table/demo/tree-table-ellipsis.md extend context correctly 1`] = `
Array [
<div
class="ant-space ant-space-horizontal ant-space-align-center"
style="margin-bottom:16px"
>
<div
class="ant-space-item"
style="margin-right:8px"
>
Fixed first column:
</div>
<div
class="ant-space-item"
>
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
/>
</button>
</div>
</div>,
<div
class="ant-table-wrapper"
>
<div
class="ant-spin-nested-loading"
>
<div
class="ant-spin-container"
>
<div
class="ant-table ant-table-has-fix-left"
>
<div
class="ant-table-container"
>
<div
class="ant-table-content"
>
<table
style="table-layout:fixed"
>
<colgroup>
<col
class="ant-table-selection-col"
style="width:100px"
/>
<col
style="width:30%"
/>
<col
style="width:12%"
/>
</colgroup>
<thead
class="ant-table-thead"
>
<tr>
<th
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<div
class="ant-table-selection"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</div>
</th>
<th
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis"
style="position:sticky;left:0"
title="Name"
>
<span
class="ant-table-cell-content"
>
Name
</span>
</th>
<th
class="ant-table-cell"
>
Age
</th>
<th
class="ant-table-cell"
>
Address
</th>
</tr>
</thead>
<tbody
class="ant-table-tbody"
>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="1"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
type="button"
/>
<span
class="ant-table-cell-content"
>
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
</span>
</td>
<td
class="ant-table-cell"
>
60
</td>
<td
class="ant-table-cell"
>
New York No. 1 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-1"
data-row-key="11"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
>
<span
class="ant-table-row-indent indent-level-1"
style="padding-left:15px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
type="button"
/>
<span
class="ant-table-cell-content"
>
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
</span>
</td>
<td
class="ant-table-cell"
>
42
</td>
<td
class="ant-table-cell"
>
New York No. 2 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-1"
data-row-key="12"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
>
<span
class="ant-table-row-indent indent-level-1"
style="padding-left:15px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
type="button"
/>
<span
class="ant-table-cell-content"
>
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
</span>
</td>
<td
class="ant-table-cell"
>
30
</td>
<td
class="ant-table-cell"
>
New York No. 3 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-2"
data-row-key="121"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
>
<span
class="ant-table-row-indent indent-level-2"
style="padding-left:30px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
type="button"
/>
<span
class="ant-table-cell-content"
>
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
</span>
</td>
<td
class="ant-table-cell"
>
16
</td>
<td
class="ant-table-cell"
>
New York No. 3 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-1"
data-row-key="13"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="Jim Green sr. Jim Green sr. Jim Green sr. Jim Green sr."
>
<span
class="ant-table-row-indent indent-level-1"
style="padding-left:15px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
type="button"
/>
<span
class="ant-table-cell-content"
>
Jim Green sr. Jim Green sr. Jim Green sr. Jim Green sr.
</span>
</td>
<td
class="ant-table-cell"
>
72
</td>
<td
class="ant-table-cell"
>
London No. 1 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-2"
data-row-key="131"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="Jim Green. Jim Green. Jim Green. Jim Green. Jim Green. Jim Green."
>
<span
class="ant-table-row-indent indent-level-2"
style="padding-left:30px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
type="button"
/>
<span
class="ant-table-cell-content"
>
Jim Green. Jim Green. Jim Green. Jim Green. Jim Green. Jim Green.
</span>
</td>
<td
class="ant-table-cell"
>
42
</td>
<td
class="ant-table-cell"
>
London No. 2 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-3"
data-row-key="1311"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="Jim Green jr. Jim Green jr. Jim Green jr. Jim Green jr."
>
<span
class="ant-table-row-indent indent-level-3"
style="padding-left:45px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
type="button"
/>
<span
class="ant-table-cell-content"
>
Jim Green jr. Jim Green jr. Jim Green jr. Jim Green jr.
</span>
</td>
<td
class="ant-table-cell"
>
25
</td>
<td
class="ant-table-cell"
>
London No. 3 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-3"
data-row-key="1312"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="Jimmy Green sr. Jimmy Green sr. Jimmy Green sr."
>
<span
class="ant-table-row-indent indent-level-3"
style="padding-left:45px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
type="button"
/>
<span
class="ant-table-cell-content"
>
Jimmy Green sr. Jimmy Green sr. Jimmy Green sr.
</span>
</td>
<td
class="ant-table-cell"
>
18
</td>
<td
class="ant-table-cell"
>
London No. 4 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="2"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="Joe Black"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
type="button"
/>
<span
class="ant-table-cell-content"
>
Joe Black
</span>
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
Sidney No. 1 Lake Park
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<ul
class="ant-pagination ant-table-pagination ant-table-pagination-right"
unselectable="unselectable"
>
<li
aria-disabled="true"
class="ant-pagination-prev ant-pagination-disabled"
title="Previous Page"
>
<button
class="ant-pagination-item-link"
disabled=""
tabindex="-1"
type="button"
>
<span
aria-label="left"
class="anticon anticon-left"
role="img"
>
<svg
aria-hidden="true"
data-icon="left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
/>
</svg>
</span>
</button>
</li>
<li
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
tabindex="0"
title="1"
>
<a
rel="nofollow"
>
1
</a>
</li>
<li
aria-disabled="true"
class="ant-pagination-next ant-pagination-disabled"
title="Next Page"
>
<button
class="ant-pagination-item-link"
disabled=""
tabindex="-1"
type="button"
>
<span
aria-label="right"
class="anticon anticon-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
/>
</svg>
</span>
</button>
</li>
</ul>
</div>
</div>
</div>,
]
`;
exports[`renders ./components/table/demo/virtual-list.md extend context correctly 1`] = `
<div
class="ant-table-wrapper virtual-table"

View File

@ -20816,6 +20816,709 @@ Array [
]
`;
exports[`renders ./components/table/demo/tree-table-ellipsis.md correctly 1`] = `
Array [
<div
class="ant-space ant-space-horizontal ant-space-align-center"
style="margin-bottom:16px"
>
<div
class="ant-space-item"
style="margin-right:8px"
>
Fixed first column:
</div>
<div
class="ant-space-item"
>
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
/>
</button>
</div>
</div>,
<div
class="ant-table-wrapper"
>
<div
class="ant-spin-nested-loading"
>
<div
class="ant-spin-container"
>
<div
class="ant-table ant-table-has-fix-left"
>
<div
class="ant-table-container"
>
<div
class="ant-table-content"
>
<table
style="table-layout:fixed"
>
<colgroup>
<col
class="ant-table-selection-col"
style="width:100px"
/>
<col
style="width:30%"
/>
<col
style="width:12%"
/>
</colgroup>
<thead
class="ant-table-thead"
>
<tr>
<th
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<div
class="ant-table-selection"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</div>
</th>
<th
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis"
style="position:sticky;left:0"
title="Name"
>
<span
class="ant-table-cell-content"
>
Name
</span>
</th>
<th
class="ant-table-cell"
>
Age
</th>
<th
class="ant-table-cell"
>
Address
</th>
</tr>
</thead>
<tbody
class="ant-table-tbody"
>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="1"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
type="button"
/>
<span
class="ant-table-cell-content"
>
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
</span>
</td>
<td
class="ant-table-cell"
>
60
</td>
<td
class="ant-table-cell"
>
New York No. 1 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-1"
data-row-key="11"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
>
<span
class="ant-table-row-indent indent-level-1"
style="padding-left:15px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
type="button"
/>
<span
class="ant-table-cell-content"
>
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
</span>
</td>
<td
class="ant-table-cell"
>
42
</td>
<td
class="ant-table-cell"
>
New York No. 2 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-1"
data-row-key="12"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
>
<span
class="ant-table-row-indent indent-level-1"
style="padding-left:15px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
type="button"
/>
<span
class="ant-table-cell-content"
>
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
</span>
</td>
<td
class="ant-table-cell"
>
30
</td>
<td
class="ant-table-cell"
>
New York No. 3 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-2"
data-row-key="121"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr."
>
<span
class="ant-table-row-indent indent-level-2"
style="padding-left:30px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
type="button"
/>
<span
class="ant-table-cell-content"
>
John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.
</span>
</td>
<td
class="ant-table-cell"
>
16
</td>
<td
class="ant-table-cell"
>
New York No. 3 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-1"
data-row-key="13"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="Jim Green sr. Jim Green sr. Jim Green sr. Jim Green sr."
>
<span
class="ant-table-row-indent indent-level-1"
style="padding-left:15px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
type="button"
/>
<span
class="ant-table-cell-content"
>
Jim Green sr. Jim Green sr. Jim Green sr. Jim Green sr.
</span>
</td>
<td
class="ant-table-cell"
>
72
</td>
<td
class="ant-table-cell"
>
London No. 1 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-2"
data-row-key="131"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="Jim Green. Jim Green. Jim Green. Jim Green. Jim Green. Jim Green."
>
<span
class="ant-table-row-indent indent-level-2"
style="padding-left:30px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-expanded"
type="button"
/>
<span
class="ant-table-cell-content"
>
Jim Green. Jim Green. Jim Green. Jim Green. Jim Green. Jim Green.
</span>
</td>
<td
class="ant-table-cell"
>
42
</td>
<td
class="ant-table-cell"
>
London No. 2 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-3"
data-row-key="1311"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="Jim Green jr. Jim Green jr. Jim Green jr. Jim Green jr."
>
<span
class="ant-table-row-indent indent-level-3"
style="padding-left:45px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
type="button"
/>
<span
class="ant-table-cell-content"
>
Jim Green jr. Jim Green jr. Jim Green jr. Jim Green jr.
</span>
</td>
<td
class="ant-table-cell"
>
25
</td>
<td
class="ant-table-cell"
>
London No. 3 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-3"
data-row-key="1312"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="Jimmy Green sr. Jimmy Green sr. Jimmy Green sr."
>
<span
class="ant-table-row-indent indent-level-3"
style="padding-left:45px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
type="button"
/>
<span
class="ant-table-cell-content"
>
Jimmy Green sr. Jimmy Green sr. Jimmy Green sr.
</span>
</td>
<td
class="ant-table-cell"
>
18
</td>
<td
class="ant-table-cell"
>
London No. 4 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="2"
>
<td
class="ant-table-cell ant-table-selection-column ant-table-cell-fix-left"
style="position:sticky;left:0"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-ellipsis ant-table-cell-with-append"
style="position:sticky;left:0"
title="Joe Black"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
<button
aria-label="Collapse row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced"
type="button"
/>
<span
class="ant-table-cell-content"
>
Joe Black
</span>
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
Sidney No. 1 Lake Park
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<ul
class="ant-pagination ant-table-pagination ant-table-pagination-right"
unselectable="unselectable"
>
<li
aria-disabled="true"
class="ant-pagination-prev ant-pagination-disabled"
title="Previous Page"
>
<button
class="ant-pagination-item-link"
disabled=""
tabindex="-1"
type="button"
>
<span
aria-label="left"
class="anticon anticon-left"
role="img"
>
<svg
aria-hidden="true"
data-icon="left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
/>
</svg>
</span>
</button>
</li>
<li
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
tabindex="0"
title="1"
>
<a
rel="nofollow"
>
1
</a>
</li>
<li
aria-disabled="true"
class="ant-pagination-next ant-pagination-disabled"
title="Next Page"
>
<button
class="ant-pagination-item-link"
disabled=""
tabindex="-1"
type="button"
>
<span
aria-label="right"
class="anticon anticon-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
/>
</svg>
</span>
</button>
</li>
</ul>
</div>
</div>
</div>,
]
`;
exports[`renders ./components/table/demo/virtual-list.md correctly 1`] = `
<div
class="ant-table-wrapper virtual-table"

View File

@ -0,0 +1,136 @@
---
order: 17.1
title:
en-US: Tree data ellipsis debug demo
zh-CN: 树形数据省略情况测试
debug: true
---
## zh-CN
https://github.com/ant-design/ant-design/issues/36583
## en-US
https://github.com/ant-design/ant-design/issues/36583
```tsx
import { Space, Switch, Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import React, { useState } from 'react';
interface DataType {
key: React.ReactNode;
name: string;
age: number;
address: string;
children?: DataType[];
}
const data: DataType[] = [
{
key: 1,
name: 'John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.',
age: 60,
address: 'New York No. 1 Lake Park',
children: [
{
key: 11,
name: 'John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.',
age: 42,
address: 'New York No. 2 Lake Park',
},
{
key: 12,
name: 'John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.',
age: 30,
address: 'New York No. 3 Lake Park',
children: [
{
key: 121,
name: 'John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr. John Brown sr.',
age: 16,
address: 'New York No. 3 Lake Park',
},
],
},
{
key: 13,
name: 'Jim Green sr. Jim Green sr. Jim Green sr. Jim Green sr.',
age: 72,
address: 'London No. 1 Lake Park',
children: [
{
key: 131,
name: 'Jim Green. Jim Green. Jim Green. Jim Green. Jim Green. Jim Green.',
age: 42,
address: 'London No. 2 Lake Park',
children: [
{
key: 1311,
name: 'Jim Green jr. Jim Green jr. Jim Green jr. Jim Green jr.',
age: 25,
address: 'London No. 3 Lake Park',
},
{
key: 1312,
name: 'Jimmy Green sr. Jimmy Green sr. Jimmy Green sr.',
age: 18,
address: 'London No. 4 Lake Park',
},
],
},
],
},
],
},
{
key: 2,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
];
const App: React.FC = () => {
const [fixed, setFixed] = useState(true);
const columns: ColumnsType<DataType> = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: '30%',
ellipsis: true,
fixed,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '12%',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
];
return (
<>
<Space align="center" style={{ marginBottom: 16 }}>
Fixed first column: <Switch checked={fixed} onChange={setFixed} />
</Space>
<Table
columns={columns}
rowSelection={{ columnWidth: 100 }}
expandable={{ defaultExpandAllRows: true }}
dataSource={data}
/>
</>
);
};
export default App;
```

View File

@ -482,6 +482,12 @@
&-row-expand-icon-cell {
text-align: center;
.@{table-prefix-cls}-row-expand-icon {
display: inline-flex;
float: none;
vertical-align: sub;
}
}
&-row-indent {
@ -492,7 +498,7 @@
&-row-expand-icon {
.operation-unit();
position: relative;
display: inline-flex;
float: left;
box-sizing: border-box;
width: @expand-icon-size;
height: @expand-icon-size;
@ -500,7 +506,6 @@
color: inherit;
line-height: ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2)) * 2 + @border-width-base *
3;
vertical-align: -2.5px;
background: @table-expand-icon-bg;
border: @border-width-base @border-style-base @table-border-color;
border-radius: @border-radius-base;
@ -561,14 +566,10 @@
}
.@{table-prefix-cls}-row-indent + & {
margin-top: ((@font-size-base * @line-height-base - @border-width-base * 3) / 2) -
ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2));
margin-right: @padding-xs;
}
+ .@{table-prefix-cls}-cell-content {
display: inline-block !important;
width: calc(100% - (@expand-icon-size + @padding-xs));
vertical-align: top;
}
}
tr&-expanded-row {
@ -632,6 +633,10 @@
pointer-events: none;
}
&-cell-fix-left-all::after {
display: none;
}
&-cell-fix-right-first::after,
&-cell-fix-right-last::after {
position: absolute;

View File

@ -21,7 +21,7 @@ Ant Design has 3 types of Tabs for different situations.
### Tabs
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| --- | --- | --- | --- | --- | --- |
| activeKey | Current TabPane's key | string | - | |
| addIcon | Customize add icon | ReactNode | - | 4.4.0 |
| animated | Whether to change tabs with animation. Only works while `tabPosition="top"` | boolean \| { inkBar: boolean, tabPane: boolean } | { inkBar: true, tabPane: false } | |
@ -35,7 +35,7 @@ Ant Design has 3 types of Tabs for different situations.
| tabBarExtraContent | Extra content in tab bar | ReactNode \| {left?: ReactNode, right?: ReactNode} | - | object: 4.6.0 |
| tabBarGutter | The gap between tabs | number | - | |
| tabBarStyle | Tab bar style object | CSSProperties | - | |
| tabPosition | Position of tabs | `top` \| `right` \| `bottom` \| `left` | `top` | |
| tabPosition | Position of tabs | `top` \| `right` \| `bottom` \ | `left` | `top` | |
| destroyInactiveTabPane | Whether destroy inactive TabPane when change tab | boolean | false | |
| type | Basic style of tabs | `line` \| `card` \| `editable-card` | `line` | |
| onChange | Callback executed when active tab is changed | function(activeKey) {} | - | |
@ -50,6 +50,7 @@ More option at [rc-tabs option](https://github.com/react-component/tabs#tabs)
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| closeIcon | Customize close icon in TabPane's head. Only works while `type="editable-card"` | ReactNode | - |
| disabled | Set TabPane disabled | boolean | false |
| forceRender | Forced render of content in tabs, not lazy render after clicking on tabs | boolean | false |
| key | TabPane's key | string | - |
| tab | Show text in TabPane's head | ReactNode | - |

View File

@ -27,7 +27,7 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
| --- | --- | --- | --- | --- |
| activeKey | 当前激活 tab 面板的 key | string | - | |
| addIcon | 自定义添加按钮 | ReactNode | - | 4.4.0 |
| animated | 是否使用动画切换 Tabs, 仅生效于 `tabPosition="top"` | boolean \| { inkBar: boolean, tabPane: boolean } | { inkBar: true, tabPane: false } | |
| animated | 是否使用动画切换 Tabs, 仅生效于 `tabPosition="top"` | boolean\| { inkBar: boolean, tabPane: boolean } | { inkBar: true, tabPane: false } | |
| centered | 标签居中展示 | boolean | false | 4.4.0 |
| defaultActiveKey | 初始化选中面板的 key如果没有设置 activeKey | string | `第一个面板` | |
| hideAdd | 是否隐藏加号图标,在 `type="editable-card"` 时有效 | boolean | false | |
@ -46,11 +46,16 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
| onTabClick | tab 被点击的回调 | function(key: string, event: MouseEvent) | - | |
| onTabScroll | tab 滚动时触发 | function({ direction: `left` \| `right` \| `top` \| `bottom` }) | - | 4.3.0 |
> 更多属性查看 [rc-tabs tabs](https://github.com/react-component/tabs#tabs)
### Tabs.TabPane
| 参数 | 说明 | 类型 | 默认值 |
| ----------- | ----------------------------------------------- | --------- | ------ |
| closeIcon | 自定义关闭图标,`在 type="editable-card"`时有效 | ReactNode | - |
| disabled | 禁用某一项 | boolean | false |
| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false |
| key | 对应 activeKey | string | - |
| tab | 选项卡头显示文字 | ReactNode | - |
> 更多属性查看 [rc-tabs tabpane](https://github.com/react-component/tabs#tabpane)

View File

@ -95,7 +95,8 @@ export interface AntTreeNodeDropEvent {
// [Legacy] Compatible for v3
export type TreeNodeNormal = DataNode;
type DraggableFn = (node: AntTreeNode) => boolean;
type DraggableFn = (node: DataNode) => boolean;
interface DraggableConfig {
icon?: React.ReactNode | false;
nodeDraggable?: DraggableFn;

View File

@ -1,6 +1,7 @@
import type { BasicDataNode } from 'rc-tree';
import * as React from 'react';
import { render } from '../../../tests/utils';
import type { DataNode } from '../index';
import Tree from '../index';
const { DirectoryTree } = Tree;
@ -74,4 +75,25 @@ describe('Tree.TypeScript', () => {
expect(container).toBeTruthy();
});
it('draggable params type', () => {
const { container } = render(
<Tree
treeData={[
{
title: 'Bamboo',
key: 'bamboo',
children: [
{
title: 'Little',
key: 'little',
},
],
},
]}
draggable={(node: DataNode) => node.title === 'Little'}
/>,
);
expect(container).toBeTruthy();
});
});

View File

@ -21,6 +21,10 @@ But in antd, `undefined` is treated as uncontrolled, and `null` is used as an ex
Note: For `options` in `Select-like` components, it is **strongly recommended not** to use `undefined` and `null` as `value` in `option`. Please use `string | number` as a valid `value` in `option`.
## Can I use internal API which is not documented on the site?
NOT RECOMMEND. Internal API is not guaranteed to be compatible with future versions. It may be removed or changed in some versions. If you really need to use it, you should to make sure these API is still valid when upgrading to a new version or just lock version for usage.
## `Select Dropdown DatePicker TimePicker Popover Popconfirm` disappears when I click another popup component inside it. How do I resolve this?
This is an old bug that has been fixed since `v3.11.x`. If you're using an older version, you can use `<Select getPopupContainer={trigger => trigger.parentElement}>` to render a component inside Popover. (Or other `getXxxxContainer` props)

View File

@ -21,6 +21,10 @@ title: FAQ
注意:对于类 `Select` 组件的 `options`,我们**强烈不建议**使用 `undefined``null` 作为 `option` 中的 `value`,请使用 `string | number` 作为 `option``value`
## 官方文档中没有提供的隐藏 API 我可以使用吗?
不推荐。对内接口不保证兼容性,它很可能在某个版本中因重构而移除。如果你确实需要使用,需自行确保版本升级时隐藏接口仍旧可用,或者锁定版本。
## 当我点击 `Select Dropdown DatePicker TimePicker Popover Popconfirm` 内的另一个 popup 组件时它会消失,如何解决?
该问题在 `3.11.0` 后已经解决。如果你仍在使用旧版本,你可以通过 `<Select getPopupContainer={trigger => trigger.parentElement}>` 来在 Popover 中渲染组件,或者使用其他的 `getXxxxContainer` 参数。

View File

@ -132,7 +132,7 @@
"rc-field-form": "~1.27.0",
"rc-image": "~5.7.0",
"rc-input": "~0.0.1-alpha.5",
"rc-input-number": "~7.3.0",
"rc-input-number": "~7.3.5",
"rc-mentions": "~1.9.0",
"rc-menu": "~9.6.0",
"rc-motion": "^2.6.1",