Merge pull request #31974 from ant-design/master-merge-feature

chore: sync master to feature
This commit is contained in:
二货机器人 2021-08-31 19:41:20 +08:00 committed by GitHub
commit a43e21e6bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 178 additions and 49 deletions

View File

@ -16,11 +16,12 @@ jobs:
steps:
- name: make release
if: github.event.ref_type == 'tag'
uses: actions-cool/release-helper@v1.4.0
uses: actions-cool/release-helper@v1.5.0
with:
triger: 'tag'
changelogs: 'CHANGELOG.en-US.md, CHANGELOG.zh-CN.md'
branch: 'master'
dingding-token: ${{ secrets.DINGDING_BOT_TOKEN }}
dingding-msg: 'CHANGELOG.zh-CN.md'
prettier: true
prerelease-filter: '-, a, b, A, B'

View File

@ -32,7 +32,7 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number
return rect.top;
}
const sharpMatcherRegx = /#(\S+)$/;
const sharpMatcherRegx = /#([\S ]+)$/;
type Section = {
link: string;

View File

@ -343,6 +343,51 @@ describe('Anchor Render', () => {
dateNowMock.mockRestore();
});
// https://github.com/ant-design/ant-design/issues/31941
it('Anchor targetOffset prop when contain spaces', async () => {
const hash = `${getHashUrl()} s p a c e s`;
let dateNowMock;
function dataNowMockFn() {
let start = 0;
const handler = () => {
start += 1000;
return start;
};
return jest.spyOn(Date, 'now').mockImplementation(handler);
}
dateNowMock = dataNowMockFn();
const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv();
mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
const wrapper = mount<Anchor>(
<Anchor>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
wrapper.instance().handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 });
wrapper.instance().handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 });
wrapper.instance().handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
dateNowMock.mockRestore();
});
it('Anchor onChange prop', async () => {
const hash1 = getHashUrl();
const hash2 = getHashUrl();

View File

@ -65,12 +65,8 @@ describe('DatePicker', () => {
});
it('disabled date', () => {
function disabledDate(current) {
return current && current < moment().endOf('day');
}
const disabledDate = current => current && current < moment().endOf('day');
const wrapper = mount(<DatePicker disabledDate={disabledDate} open />);
expect(wrapper.render()).toMatchSnapshot();
});

View File

@ -160,7 +160,8 @@
height: 40%;
}
&:hover &-handler-wrap {
&:hover &-handler-wrap,
&-focused &-handler-wrap {
opacity: 1;
}

View File

@ -75,18 +75,30 @@ export function resolveOnChange<E extends HTMLInputElement | HTMLTextAreaElement
return;
}
let event = e;
const originalInputValue = target.value;
if (e.type === 'click') {
// click clear icon
event = Object.create(e);
event.target = target;
event.currentTarget = target;
// change target ref value cause e.target.value should be '' when clear input
target.value = '';
// Clone a new target for event.
// Avoid the following usage, the setQuery method gets the original value.
//
// const [query, setQuery] = React.useState('');
// <Input
// allowClear
// value={query}
// onChange={(e)=> {
// setQuery((prevStatus) => e.target.value);
// }}
// />
const currentTarget = target.cloneNode(true) as E;
event.target = currentTarget;
event.currentTarget = currentTarget;
currentTarget.value = '';
onChange(event as React.ChangeEvent<E>);
// reset target ref value
target.value = originalInputValue;
return;
}

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { mount } from 'enzyme';
// eslint-disable-next-line import/no-unresolved
import Form from '../../form';
@ -228,4 +228,31 @@ describe('Input allowClear', () => {
expect(onBlur).not.toBeCalled();
wrapper.unmount();
});
// https://github.com/ant-design/ant-design/issues/31927
it('should correctly when useState', () => {
const App = () => {
const [query, setQuery] = useState('');
return (
<Input
allowClear
value={query}
onChange={e => {
setQuery(() => e.target.value);
}}
/>
);
};
const wrapper = mount(<App />);
wrapper.find('input').getDOMNode().focus();
wrapper.find('input').simulate('change', { target: { value: '111' } });
expect(wrapper.find('input').getDOMNode().value).toEqual('111');
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
expect(wrapper.find('input').getDOMNode().value).toEqual('');
wrapper.unmount();
});
});

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { mount } from 'enzyme';
import RcTextArea from 'rc-textarea';
import Input from '..';
@ -380,14 +380,14 @@ describe('TextArea allowClear', () => {
expect(wrapper.find('textarea').at(0).getDOMNode().value).toBe('Light');
});
it('onChange event should return HTMLInputElement', () => {
it('onChange event should return HTMLTextAreaElement', () => {
const onChange = jest.fn();
const wrapper = mount(<Input.TextArea onChange={onChange} allowClear />);
function isNativeElement() {
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
target: wrapper.find('textarea').instance(),
target: expect.any(HTMLTextAreaElement),
}),
);
@ -411,4 +411,31 @@ describe('TextArea allowClear', () => {
wrapper.find('.ant-input-clear-icon').first().simulate('click');
isNativeElement();
});
// https://github.com/ant-design/ant-design/issues/31927
it('should correctly when useState', () => {
const App = () => {
const [query, setQuery] = useState('');
return (
<TextArea
allowClear
value={query}
onChange={e => {
setQuery(() => e.target.value);
}}
/>
);
};
const wrapper = mount(<App />);
wrapper.find('textarea').getDOMNode().focus();
wrapper.find('textarea').simulate('change', { target: { value: '111' } });
expect(wrapper.find('textarea').getDOMNode().value).toEqual('111');
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
expect(wrapper.find('textarea').getDOMNode().value).toEqual('');
wrapper.unmount();
});
});

View File

@ -5,6 +5,8 @@ import { genCSSMotion } from 'rc-motion/lib/CSSMotion';
import { mount } from 'enzyme';
import Modal from '..';
import Button from '../../button';
import Input from '../../input';
import ConfigProvider from '../../config-provider';
jest.mock('rc-util/lib/Portal');
jest.mock('rc-motion');
@ -68,6 +70,34 @@ describe('Modal.hook', () => {
jest.useRealTimers();
});
it('context support config direction', () => {
jest.useFakeTimers();
const Demo = () => {
const [modal, contextHolder] = Modal.useModal();
return (
<>
<Button
onClick={() => {
modal.confirm({
content: <Input />,
});
}}
/>
{contextHolder}
</>
);
};
const wrapper = mount(
<ConfigProvider direction="rtl">
<Demo />
</ConfigProvider>,
);
wrapper.find('button').simulate('click');
expect(wrapper.find('.ant-input-rtl').length).toBeTruthy();
});
it('hooks modal should trigger onCancel', () => {
let cancelCount = 0;
const Demo = () => {

View File

@ -32,13 +32,13 @@ const HookModal: React.ForwardRefRenderFunction<HookModalRef, HookModalProps> =
const prefixCls = getPrefixCls('modal');
const rootPrefixCls = getPrefixCls();
function close(...args: any[]) {
const close = (...args: any[]) => {
setVisible(false);
const triggerCancel = args.some(param => param && param.triggerCancel);
if (innerConfig.onCancel && triggerCancel) {
innerConfig.onCancel();
}
}
};
React.useImperativeHandle(ref, () => ({
destroy: close,

View File

@ -19,7 +19,7 @@ describe('Pagination', () => {
});
it('should pass disabled to prev and next buttons', () => {
function itemRender(current, type, originalElement) {
const itemRender = (current, type, originalElement) => {
if (type === 'prev') {
return <button type="button">prev</button>;
}
@ -27,7 +27,7 @@ describe('Pagination', () => {
return <button type="button">next</button>;
}
return originalElement;
}
};
const wrapper = mount(<Pagination defaultCurrent={1} total={50} itemRender={itemRender} />);
expect(wrapper.find('button').at(0).props().disabled).toBe(true);
});

View File

@ -4,9 +4,7 @@ import Slider from '..';
describe('Slider.typescript', () => {
it('single value', () => {
const value = 0;
function onChange(v: number) {
return v;
}
const onChange = (v: number) => v;
const result = (
<Slider defaultValue={value} value={value} onChange={onChange} onAfterChange={onChange} />
);
@ -15,9 +13,7 @@ describe('Slider.typescript', () => {
it('range value', () => {
const value: [number, number] = [0, 1];
function onChange(v: [number, number]) {
return v;
}
const onChange = (v: [number, number]) => v;
const result = (
<Slider
range
@ -32,9 +28,7 @@ describe('Slider.typescript', () => {
it('step can be null value', () => {
const value = 0;
function onChange(v: number) {
return v;
}
const onChange = (v: number) => v;
const result = (
<Slider
defaultValue={value}

View File

@ -112,6 +112,7 @@ Steps.Step = RcSteps.Step;
Steps.defaultProps = {
current: 0,
responsive: true,
};
export default Steps;

View File

@ -13,9 +13,7 @@ import ConfigProvider from '../../config-provider';
const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => {} } };
describe('Table.filter', () => {
window.requestAnimationFrame = function (callback) {
return window.setTimeout(callback, 16);
};
window.requestAnimationFrame = callback => window.setTimeout(callback, 16);
window.cancelAnimationFrame = window.clearTimeout;
const filterFn = (value, record) => record.name.indexOf(value) !== -1;

View File

@ -8,7 +8,7 @@
@descriptions-prefix-cls: ~'@{ant-prefix}-descriptions';
@table-header-icon-color: #bfbfbf;
@table-header-icon-color-hover: darken(@table-header-icon-color, 10%);
@table-sticky-zindex: (@zindex-table-fixed + 1);
@table-sticky-zindex: calc(@zindex-table-fixed + 1);
@table-sticky-scroll-bar-active-bg: fade(@table-sticky-scroll-bar-bg, 80%);
.@{table-prefix-cls}-wrapper {

View File

@ -400,7 +400,7 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
footer={footer}
onScroll={this.handleLeftScroll}
disabled={disabled}
direction="left"
direction={direction === 'rtl' ? 'right' : 'left'}
showSelectAll={showSelectAll}
selectAllLabel={selectAllLabels[0]}
pagination={mergedPagination}
@ -437,7 +437,7 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
footer={footer}
onScroll={this.handleRightScroll}
disabled={disabled}
direction="right"
direction={direction === 'rtl' ? 'left' : 'right'}
showSelectAll={showSelectAll}
selectAllLabel={selectAllLabels[1]}
showRemove={oneWay}

View File

@ -56,13 +56,12 @@ describe('Tree', () => {
});
it('switcherIcon should be loading icon when loadData', () => {
function onLoadData() {
return new Promise(resolve => {
const onLoadData = () =>
new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
}
const wrapper = mount(
<Tree switcherIcon="switcherIcon" defaultExpandAll loadData={onLoadData}>
<TreeNode icon="icon">

View File

@ -52,8 +52,8 @@ function updateTreeData(list: DataNode[], key: React.Key, children: DataNode[]):
const Demo: React.FC<{}> = () => {
const [treeData, setTreeData] = useState(initTreeData);
function onLoadData({ key, children }: any) {
return new Promise<void>(resolve => {
const onLoadData = ({ key, children }: any) =>
new Promise<void>(resolve => {
if (children) {
resolve();
return;
@ -69,7 +69,6 @@ const Demo: React.FC<{}> = () => {
resolve();
}, 1000);
});
}
return <Tree loadData={onLoadData} treeData={treeData} />;
};

View File

@ -542,9 +542,8 @@ describe('Typography', () => {
});
it('warning if use setContentRef', () => {
function refFunc() {}
const refFunc = () => {};
mount(<Typography setContentRef={refFunc} />);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Typography] `setContentRef` is deprecated. Please use `ref` instead.',
);

View File

@ -62,7 +62,7 @@ const DragableUploadListItem = ({ originNode, moveRow, file, fileList }) => {
);
};
const DragSortingUpload: React.FC = () => {
const DragSortingUpload = () => {
const [fileList, setFileList] = useState([
{
uid: '-1',

View File

@ -247,7 +247,7 @@
"react-dom": "^17.0.1",
"react-draggable": "^4.4.3",
"react-github-button": "^0.1.11",
"react-helmet-async": "^1.0.4",
"react-helmet-async": "~1.1.2",
"react-highlight-words": "^0.17.0",
"react-infinite-scroller": "^1.2.4",
"react-intl": "^5.20.4",
@ -275,7 +275,7 @@
"stylelint-declaration-block-no-ignored-properties": "^2.1.0",
"stylelint-order": "^4.0.0",
"theme-switcher": "^1.0.2",
"typescript": "~4.3.2",
"typescript": "~4.4.2",
"webpack-bundle-analyzer": "^4.1.0",
"xhr-mock": "^2.4.1",
"xhr2": "^0.2.0",