mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 14:13:37 +08:00
Merge pull request #31974 from ant-design/master-merge-feature
chore: sync master to feature
This commit is contained in:
commit
a43e21e6bf
3
.github/workflows/release-helper.yml
vendored
3
.github/workflows/release-helper.yml
vendored
@ -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'
|
||||
|
@ -32,7 +32,7 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number
|
||||
return rect.top;
|
||||
}
|
||||
|
||||
const sharpMatcherRegx = /#(\S+)$/;
|
||||
const sharpMatcherRegx = /#([\S ]+)$/;
|
||||
|
||||
type Section = {
|
||||
link: string;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
|
@ -160,7 +160,8 @@
|
||||
height: 40%;
|
||||
}
|
||||
|
||||
&:hover &-handler-wrap {
|
||||
&:hover &-handler-wrap,
|
||||
&-focused &-handler-wrap {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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 = () => {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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}
|
||||
|
@ -112,6 +112,7 @@ Steps.Step = RcSteps.Step;
|
||||
|
||||
Steps.defaultProps = {
|
||||
current: 0,
|
||||
responsive: true,
|
||||
};
|
||||
|
||||
export default Steps;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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}
|
||||
|
@ -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">
|
||||
|
@ -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} />;
|
||||
};
|
||||
|
@ -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.',
|
||||
);
|
||||
|
@ -62,7 +62,7 @@ const DragableUploadListItem = ({ originNode, moveRow, file, fileList }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const DragSortingUpload: React.FC = () => {
|
||||
const DragSortingUpload = () => {
|
||||
const [fileList, setFileList] = useState([
|
||||
{
|
||||
uid: '-1',
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user