chore: auto merge branches (#38173)

chore: Feature merge master
This commit is contained in:
github-actions[bot] 2022-10-22 15:15:43 +00:00 committed by GitHub
commit f5b16a2e1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 197 additions and 174 deletions

View File

@ -2,7 +2,7 @@ import * as React from 'react';
export const { isValidElement } = React;
export function isFragment(child: React.ReactElement): boolean {
export function isFragment(child: any): boolean {
return child && isValidElement(child) && child.type === React.Fragment;
}

View File

@ -87,7 +87,7 @@ class Wave extends React.Component<WaveProps> {
onClick = (node: HTMLElement, waveColor: string) => {
const { insertExtraNode, disabled } = this.props;
if (disabled || !node || isHidden(node) || node.className.indexOf('-leave') >= 0) {
if (disabled || !node || isHidden(node) || node.className.includes('-leave')) {
return;
}
@ -163,7 +163,7 @@ class Wave extends React.Component<WaveProps> {
!node ||
!node.getAttribute ||
node.getAttribute('disabled') ||
node.className.indexOf('disabled') >= 0
node.className.includes('disabled')
) {
return;
}

View File

@ -21,7 +21,7 @@ Alert component for feedback.
| banner | Whether to show as banner | boolean | false | |
| closable | Whether Alert can be closed | boolean | - | |
| closeText | Close text to show | ReactNode | - | |
| closeIcon | Custom close icon | ReactNode | `<CloseOutlined />` | 4.17.0 |
| closeIcon | Custom close icon | ReactNode | `<CloseOutlined />` | 4.18.0 |
| description | Additional content of Alert | ReactNode | - | |
| icon | Custom icon, effective when `showIcon` is true | ReactNode | - | |
| message | Content of Alert | ReactNode | - | |

View File

@ -22,7 +22,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/8emPa3fjl/Alert.svg
| banner | 是否用作顶部公告 | boolean | false | |
| closable | 默认不显示关闭按钮 | boolean | - | |
| closeText | 自定义关闭按钮 | ReactNode | - | |
| closeIcon | 自定义关闭 Icon | ReactNode | `<CloseOutlined />` | 4.17.0 |
| closeIcon | 自定义关闭 Icon | ReactNode | `<CloseOutlined />` | 4.18.0 |
| description | 警告提示的辅助性文字介绍 | ReactNode | - | |
| icon | 自定义图标,`showIcon` 为 true 时有效 | ReactNode | - | |
| message | 警告提示内容 | ReactNode | - | |

View File

@ -2,13 +2,15 @@ import React from 'react';
import BackTop from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, sleep } from '../../../tests/utils';
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
describe('BackTop', () => {
mountTest(BackTop);
rtlTest(BackTop);
it('should scroll to top after click it', async () => {
jest.useFakeTimers();
const { container } = render(<BackTop visibilityHeight={-1} />);
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((_, y) => {
window.scrollY = y;
@ -18,9 +20,12 @@ describe('BackTop', () => {
window.scrollTo(0, 400);
expect(document.documentElement.scrollTop).toBe(400);
fireEvent.click(container.querySelector('.ant-back-top')!);
await sleep(500);
await waitFakeTimer();
expect(document.documentElement.scrollTop).toBe(0);
scrollToSpy.mockRestore();
jest.clearAllTimers();
jest.useRealTimers();
});
it('support onClick', async () => {

View File

@ -2,5 +2,5 @@ import { PresetColorTypes } from '../_util/colors';
// eslint-disable-next-line import/prefer-default-export
export function isPresetColor(color?: string): boolean {
return (PresetColorTypes as any[]).indexOf(color) !== -1;
return (PresetColorTypes as any[]).includes(color);
}

View File

@ -8,7 +8,7 @@ import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext';
import { useCompactItemContext } from '../space/Compact';
import { cloneElement } from '../_util/reactNode';
import { cloneElement, isFragment } from '../_util/reactNode';
import { tuple } from '../_util/type';
import warning from '../_util/warning';
import Wave from '../_util/wave';
@ -25,10 +25,6 @@ function isUnBorderedButtonType(type: ButtonType | undefined) {
return type === 'text' || type === 'link';
}
function isReactFragment(node: React.ReactNode) {
return React.isValidElement(node) && node.type === React.Fragment;
}
// Insert one space between two chinese characters automatically.
function insertSpace(child: React.ReactElement | string | number, needInserted: boolean) {
// Check the child if is undefined or null.
@ -50,7 +46,7 @@ function insertSpace(child: React.ReactElement | string | number, needInserted:
if (typeof child === 'string') {
return isTwoCNChar(child) ? <span>{child.split('').join(SPACE)}</span> : <span>{child}</span>;
}
if (isReactFragment(child)) {
if (isFragment(child)) {
return <span>{child}</span>;
}
return child;

View File

@ -3,7 +3,7 @@ import type { CarouselRef } from '..';
import Carousel from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { sleep, render, act } from '../../../tests/utils';
import { waitFakeTimer, render } from '../../../tests/utils';
describe('Carousel', () => {
mountTest(Carousel);
@ -17,14 +17,6 @@ describe('Carousel', () => {
jest.useRealTimers();
});
function runAllTimersWithAct(times = 1) {
for (let i = 0; i < times; i++) {
act(() => {
jest.runAllTimers();
});
}
}
it('should has innerSlider', () => {
const ref = React.createRef<CarouselRef>();
render(
@ -51,16 +43,16 @@ describe('Carousel', () => {
expect(typeof goTo).toBe('function');
expect(ref.current?.innerSlider.state.currentSlide).toBe(0);
ref.current?.goTo(2);
runAllTimersWithAct(1);
await waitFakeTimer();
expect(ref.current?.innerSlider.state.currentSlide).toBe(2);
// wait for animation to be finished
runAllTimersWithAct(2);
await waitFakeTimer();
ref.current?.prev();
runAllTimersWithAct(1);
await waitFakeTimer();
expect(ref.current?.innerSlider.state.currentSlide).toBe(1);
runAllTimersWithAct(2);
await waitFakeTimer();
ref.current?.next();
runAllTimersWithAct(1);
await waitFakeTimer();
expect(ref.current?.innerSlider.state.currentSlide).toBe(2);
});
@ -77,7 +69,7 @@ describe('Carousel', () => {
const spy = jest.spyOn(ref.current?.innerSlider, 'autoPlay');
window.resizeTo(1000, window.outerHeight);
expect(spy).not.toHaveBeenCalled();
await sleep(500);
await waitFakeTimer();
expect(spy).toHaveBeenCalled();
});

View File

@ -102,7 +102,7 @@ const InternalCheckbox: React.ForwardRefRenderFunction<HTMLInputElement, Checkbo
}
};
checkboxProps.name = checkboxGroup.name;
checkboxProps.checked = checkboxGroup.value.indexOf(restProps.value) !== -1;
checkboxProps.checked = checkboxGroup.value.includes(restProps.value);
}
const classString = classNames(
{

View File

@ -101,7 +101,7 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
const opts = getOptions();
onChange?.(
newValue
.filter(val => registeredValues.indexOf(val) !== -1)
.filter(val => registeredValues.includes(val))
.sort((a, b) => {
const indexA = opts.findIndex(opt => opt.value === a);
const indexB = opts.findIndex(opt => opt.value === b);
@ -122,7 +122,7 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
key={option.value.toString()}
disabled={'disabled' in option ? option.disabled : restProps.disabled}
value={option.value}
checked={value.indexOf(option.value) !== -1}
checked={value.includes(option.value)}
onChange={option.onChange}
className={`${groupPrefixCls}-item`}
style={option.style}

View File

@ -1,6 +1,6 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { sleep, render, fireEvent } from '../../../tests/utils';
import { waitFakeTimer, render, fireEvent } from '../../../tests/utils';
import { resetWarned } from '../../_util/warning';
describe('Collapse', () => {
@ -66,6 +66,7 @@ describe('Collapse', () => {
});
it('could be expand and collapse', async () => {
jest.useFakeTimers();
const { container } = render(
<Collapse>
<Collapse.Panel header="This is panel header 1" key="1">
@ -77,10 +78,11 @@ describe('Collapse', () => {
container.querySelector('.ant-collapse-item')?.classList.contains('ant-collapse-item-active'),
).toBe(false);
fireEvent.click(container.querySelector('.ant-collapse-header')!);
await sleep(400);
await waitFakeTimer();
expect(
container.querySelector('.ant-collapse-item')?.classList.contains('ant-collapse-item-active'),
).toBe(true);
jest.useRealTimers();
});
it('could override default openMotion', () => {

View File

@ -4,7 +4,7 @@ import Dropdown from '..';
import type { DropDownProps } from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { act, fireEvent, render, sleep } from '../../../tests/utils';
import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import Menu from '../../menu';
let triggerProps: TriggerProps;
@ -55,6 +55,7 @@ describe('Dropdown', () => {
});
it('support Menu expandIcon', async () => {
jest.useFakeTimers();
const props: DropDownProps = {
overlay: (
<Menu expandIcon={<span id="customExpandIcon" />}>
@ -73,8 +74,9 @@ describe('Dropdown', () => {
<button type="button">button</button>
</Dropdown>,
);
await sleep(500);
await waitFakeTimer();
expect(container.querySelectorAll('#customExpandIcon').length).toBe(1);
jest.useRealTimers();
});
it('should warn if use topCenter or bottomCenter', () => {

View File

@ -1,27 +1,59 @@
import React from 'react';
import type { FormListFieldData, FormListOperation } from '..';
import Form from '..';
import { fireEvent, render, sleep, act } from '../../../tests/utils';
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import Button from '../../button';
import Input from '../../input';
describe('Form.List', () => {
const change = async (
wrapper: ReturnType<typeof render>['container'],
index: number,
// const change = async (
// wrapper: ReturnType<typeof render>['container'],
// index: number,
// value: string,
// ) => {
// fireEvent.change(wrapper.getElementsByClassName('ant-input')?.[index], { target: { value } });
// await sleep();
// };
const changeValue = async (
input: HTMLElement | null | number,
value: string,
advTimer = 1000,
) => {
fireEvent.change(wrapper.getElementsByClassName('ant-input')?.[index], { target: { value } });
await sleep();
let element: HTMLElement;
if (typeof input === 'number') {
element = document.querySelectorAll('input')[input];
}
expect(element!).toBeTruthy();
fireEvent.change(element!, {
target: {
value,
},
});
if (advTimer) {
await waitFakeTimer(advTimer / 20);
}
};
beforeEach(() => {
document.body.innerHTML = '';
jest.useFakeTimers();
});
afterAll(() => {
jest.clearAllTimers();
jest.useRealTimers();
});
const testList = (
name: string,
renderField: (value: FormListFieldData) => React.ReactNode,
): void => {
it(name, async () => {
jest.useFakeTimers();
const { container } = render(
<Form>
<Form.List name="list">
@ -39,39 +71,31 @@ describe('Form.List', () => {
</Form>,
);
function operate(className: string) {
async function operate(className: string) {
fireEvent.click(container.querySelector(className)!);
act(() => {
jest.runAllTimers();
});
await waitFakeTimer();
}
operate('.add');
expect(container.getElementsByClassName('ant-input').length).toBe(1);
await operate('.add');
expect(container.querySelectorAll('.ant-input').length).toBe(1);
operate('.add');
expect(container.getElementsByClassName('ant-input').length).toBe(2);
await operate('.add');
expect(container.querySelectorAll('.ant-input').length).toBe(2);
operate('.add');
expect(container.getElementsByClassName('ant-input').length).toBe(3);
await operate('.add');
expect(container.querySelectorAll('.ant-input').length).toBe(3);
await change(container, 2, '');
for (let i = 0; i < 10; i += 1) {
act(() => {
jest.runAllTimers();
});
}
expect(container.getElementsByClassName('ant-form-item-explain').length).toBe(1);
await changeValue(2, '');
operate('.remove-0');
expect(container.getElementsByClassName('ant-input').length).toBe(2);
expect(container.getElementsByClassName('ant-form-item-explain').length).toBe(1);
expect(container.querySelectorAll('.ant-form-item-explain').length).toBe(1);
operate('.remove-1');
expect(container.getElementsByClassName('ant-input').length).toBe(1);
expect(container.getElementsByClassName('ant-form-item-explain').length).toBe(0);
await operate('.remove-0');
expect(container.querySelectorAll('.ant-input').length).toBe(2);
expect(container.querySelectorAll('.ant-form-item-explain').length).toBe(1);
jest.useRealTimers();
await operate('.remove-1');
expect(container.querySelectorAll('.ant-input').length).toBe(1);
expect(container.querySelectorAll('.ant-form-item-explain').length).toBe(0);
});
};
@ -131,28 +155,26 @@ describe('Form.List', () => {
);
await click(container, '.add');
await change(container, 0, 'input1');
await changeValue(0, 'input1');
fireEvent.submit(container.querySelector('form')!);
await sleep();
await waitFakeTimer();
expect(onFinish).toHaveBeenLastCalledWith({ list: ['input1'] });
await click(container, '.add');
await change(container, 1, 'input2');
await changeValue(1, 'input2');
await click(container, '.add');
await change(container, 2, 'input3');
await changeValue(2, 'input3');
fireEvent.submit(container.querySelector('form')!);
await sleep();
await waitFakeTimer();
expect(onFinish).toHaveBeenLastCalledWith({ list: ['input1', 'input2', 'input3'] });
await click(container, '.remove'); // will remove first input
fireEvent.submit(container.querySelector('form')!);
await sleep();
await waitFakeTimer();
expect(onFinish).toHaveBeenLastCalledWith({ list: ['input2', 'input3'] });
});
it('list errors', async () => {
jest.useFakeTimers();
let operation: FormListOperation;
const { container } = render(
<Form>
@ -177,15 +199,8 @@ describe('Form.List', () => {
);
async function addItem() {
await act(async () => {
operation.add();
await sleep(100);
jest.runAllTimers();
});
act(() => {
jest.runAllTimers();
});
operation.add();
await waitFakeTimer();
}
await addItem();
@ -193,8 +208,6 @@ describe('Form.List', () => {
await addItem();
expect(container.getElementsByClassName('ant-form-item-explain div')).toHaveLength(0);
jest.useRealTimers();
});
it('should render empty without errors', () => {
@ -243,7 +256,7 @@ describe('Form.List', () => {
const { container } = render(<Demo />);
fireEvent.click(container.querySelector('button')!);
await sleep();
await waitFakeTimer();
expect(errorSpy).not.toHaveBeenCalled();

View File

@ -22,7 +22,7 @@ export function getFieldId(namePath: InternalNamePath, formName?: string): strin
return `${formName}_${mergedId}`;
}
const isIllegalName = formItemNameBlackList.indexOf(mergedId) >= 0;
const isIllegalName = formItemNameBlackList.includes(mergedId);
return isIllegalName ? `${defaultItemNamePrefixCls}_${mergedId}` : mergedId;
}

View File

@ -75,7 +75,7 @@ const Progress: React.FC<ProgressProps> = (props: ProgressProps) => {
function getProgressStatus() {
const { status } = props;
if (ProgressStatuses.indexOf(status!) < 0 && getPercentNumber() >= 100) {
if (!ProgressStatuses.includes(status!) && getPercentNumber() >= 100) {
return 'success';
}
return status || 'normal';
@ -119,7 +119,7 @@ const Progress: React.FC<ProgressProps> = (props: ProgressProps) => {
const strokeColorNotArray = Array.isArray(strokeColor) ? strokeColor[0] : strokeColor;
const strokeColorNotGradient =
typeof strokeColor === 'string' || Array.isArray(strokeColor) ? strokeColor : undefined;
let progress;
let progress: React.ReactNode;
// Render progress shape
if (type === 'line') {
progress = steps ? (

View File

@ -41,7 +41,7 @@ export function formatTimeStr(duration: number, format: string) {
const templateText = format.replace(escapeRegex, '[]');
const replacedText = timeUnits.reduce((current, [name, unit]) => {
if (current.indexOf(name) !== -1) {
if (current.includes(name)) {
const value = Math.floor(leftDuration / unit);
leftDuration -= value * unit;
return current.replace(new RegExp(`${name}+`, 'g'), (match: string) => {

View File

@ -482,8 +482,8 @@ function InternalTable<RecordType extends object = any>(
const defaultPosition = direction === 'rtl' ? 'left' : 'right';
const { position } = mergedPagination;
if (position !== null && Array.isArray(position)) {
const topPos = position.find(p => p.indexOf('top') !== -1);
const bottomPos = position.find(p => p.indexOf('bottom') !== -1);
const topPos = position.find(p => p.includes('top'));
const bottomPos = position.find(p => p.includes('bottom'));
const isDisable = position.every(p => `${p}` === 'none');
if (!topPos && !bottomPos && !isDisable) {
bottomPaginationNode = renderPagination(defaultPosition);

View File

@ -237,14 +237,14 @@ const Tooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
const transformOrigin = { top: '50%', left: '50%' };
if (['top', 'Bottom'].includes(placement)) {
if (/top|Bottom/.test(placement)) {
transformOrigin.top = `${rect.height - align.offset![1]}px`;
} else if (['Top', 'bottom'].includes(placement)) {
} else if (/Top|bottom/.test(placement)) {
transformOrigin.top = `${-align.offset![1]}px`;
}
if (['left', 'Right'].includes(placement)) {
if (/left|Right/.test(placement)) {
transformOrigin.left = `${rect.width - align.offset![0]}px`;
} else if (['right', 'Left'].includes(placement)) {
} else if (/right|Left/.test(placement)) {
transformOrigin.left = `${-align.offset![0]}px`;
}
domNode.style.transformOrigin = `${transformOrigin.left} ${transformOrigin.top}`;

View File

@ -71,7 +71,7 @@ class ListBody<RecordType extends KeyWiseTransferItem> extends React.Component<
onItemSelect = (item: RecordType) => {
const { onItemSelect, selectedKeys } = this.props;
const checked = selectedKeys.indexOf(item.key) >= 0;
const checked = selectedKeys.includes(item.key);
onItemSelect(item.key, !checked);
};
@ -144,7 +144,7 @@ class ListBody<RecordType extends KeyWiseTransferItem> extends React.Component<
>
{this.getItems().map(({ renderedEl, renderedText, item }: RenderedItem<RecordType>) => {
const { disabled } = item;
const checked = selectedKeys.indexOf(item.key) >= 0;
const checked = selectedKeys.includes(item.key);
return (
<ListItem

View File

@ -1,5 +1,6 @@
import React from 'react';
import { render } from '../../../tests/utils';
import type { TransferProps } from '../index';
import Transfer from '../index';
describe('Transfer.Customize', () => {
@ -15,7 +16,7 @@ describe('Transfer.Customize', () => {
it('props#body does not work anymore', () => {
const body = jest.fn();
const props = { body };
const props = { body } as TransferProps<any>;
render(<Transfer {...props} />);
expect(errorSpy).not.toHaveBeenCalled();
expect(body).not.toHaveBeenCalled();

View File

@ -14,6 +14,11 @@ const listCommonProps: TransferListProps<any> = {
notFoundContent: 'Not Found',
} as TransferListProps<any>;
const listProps: TransferListProps<any> = {
...listCommonProps,
dataSource: undefined as unknown as any[],
};
describe('Transfer.List', () => {
it('should render correctly', () => {
const { container } = render(<List {...listCommonProps} />);
@ -43,4 +48,9 @@ describe('Transfer.List', () => {
expect(instance.current?.handleFilter({ target: 'test' })).toBe(undefined);
expect(handleFilter).toHaveBeenCalled();
});
it('should render correctly when dataSource is not exists', () => {
expect(() => {
render(<List {...listProps} />);
}).not.toThrow();
});
});

View File

@ -29,9 +29,11 @@ interface RecordType {
}
interface DataType {
key: string;
title: string;
tag: string;
description: string;
disabled: boolean;
tag: string;
}
interface TableTransferProps extends TransferProps<TransferItem> {

View File

@ -69,7 +69,7 @@ export interface TransferProps<RecordType> {
prefixCls?: string;
className?: string;
disabled?: boolean;
dataSource: RecordType[];
dataSource?: RecordType[];
targetKeys?: string[];
selectedKeys?: string[];
render?: TransferRender<RecordType>;
@ -116,13 +116,6 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
static Search = Search;
static defaultProps = {
dataSource: [],
locale: {},
showSearch: false,
listStyle: () => {},
};
static getDerivedStateFromProps<T>({
selectedKeys,
targetKeys,
@ -156,8 +149,8 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
const { selectedKeys = [], targetKeys = [] } = props;
this.state = {
sourceSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) === -1),
targetSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) > -1),
sourceSelectedKeys: selectedKeys.filter(key => !targetKeys.includes(key)),
targetSelectedKeys: selectedKeys.filter(key => targetKeys.includes(key)),
};
}
@ -180,11 +173,10 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
return this.props.titles ?? transferLocale.titles ?? [];
}
getLocale = (transferLocale: TransferLocale, renderEmpty: RenderEmptyHandler) => ({
...transferLocale,
notFoundContent: renderEmpty('Transfer'),
...this.props.locale,
});
getLocale = (transferLocale: TransferLocale, renderEmpty: RenderEmptyHandler) => {
const { locale = {} } = this.props;
return { ...transferLocale, notFoundContent: renderEmpty('Transfer'), ...locale };
};
moveTo = (direction: TransferDirection) => {
const { targetKeys = [], dataSource = [], onChange } = this.props;
@ -192,13 +184,13 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
const moveKeys = direction === 'right' ? sourceSelectedKeys : targetSelectedKeys;
// filter the disabled options
const newMoveKeys = moveKeys.filter(
(key: string) => !dataSource.some(data => !!(key === data.key && data.disabled)),
key => !dataSource.some(data => !!(key === data.key && data.disabled)),
);
// move items to target box
const newTargetKeys =
direction === 'right'
? newMoveKeys.concat(targetKeys)
: targetKeys.filter(targetKey => newMoveKeys.indexOf(targetKey) === -1);
: targetKeys.filter(targetKey => !newMoveKeys.includes(targetKey));
// empty checked keys
const oppositeDirection = direction === 'right' ? 'left' : 'right';
@ -214,13 +206,13 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
onItemSelectAll = (direction: TransferDirection, selectedKeys: string[], checkAll: boolean) => {
this.setStateKeys(direction, prevKeys => {
let mergedCheckedKeys = [];
let mergedCheckedKeys: string[] = [];
if (checkAll) {
// Merge current keys with origin key
mergedCheckedKeys = Array.from(new Set([...prevKeys, ...selectedKeys]));
mergedCheckedKeys = Array.from(new Set<string>([...prevKeys, ...selectedKeys]));
} else {
// Remove current keys from origin keys
mergedCheckedKeys = prevKeys.filter((key: string) => selectedKeys.indexOf(key) === -1);
mergedCheckedKeys = prevKeys.filter(key => !selectedKeys.includes(key));
}
this.handleSelectChange(direction, mergedCheckedKeys);
@ -324,7 +316,7 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
};
separateDataSource() {
const { dataSource, rowKey, targetKeys = [] } = this.props;
const { dataSource = [], rowKey, targetKeys = [] } = this.props;
const leftDataSource: KeyWise<RecordType>[] = [];
const rightDataSource: KeyWise<RecordType>[] = new Array(targetKeys.length);
@ -365,10 +357,10 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
className,
disabled,
operations = [],
showSearch,
showSearch = false,
footer,
style,
listStyle,
listStyle = {},
operationStyle,
filterOption,
render,

View File

@ -81,12 +81,6 @@ interface TransferListState {
export default class TransferList<
RecordType extends KeyWiseTransferItem,
> extends React.PureComponent<TransferListProps<RecordType>, TransferListState> {
static defaultProps = {
dataSource: [],
titleText: '',
showSearch: false,
};
timer: number;
triggerScrollTimer: number;
@ -109,7 +103,7 @@ export default class TransferList<
if (checkedKeys.length === 0) {
return 'none';
}
if (filteredItems.every(item => checkedKeys.indexOf(item.key) >= 0 || !!item.disabled)) {
if (filteredItems.every(item => checkedKeys.includes(item.key) || !!item.disabled)) {
return 'all';
}
return 'part';
@ -161,7 +155,7 @@ export default class TransferList<
if (filterOption) {
return filterOption(filterValue, item);
}
return text.indexOf(filterValue) >= 0;
return text.includes(filterValue);
};
// =============================== Render ===============================
@ -305,12 +299,12 @@ export default class TransferList<
const { filterValue } = this.state;
const {
prefixCls,
dataSource,
titleText,
dataSource = [],
titleText = '',
checkedKeys,
disabled,
footer,
showSearch,
showSearch = false,
style,
searchPlaceholder,
notFoundContent,

View File

@ -65,12 +65,7 @@ export function calcRangeKeys({
// Append selection
keys.push(key);
}
if (expandedKeys.indexOf(key) === -1) {
return false;
}
return true;
return expandedKeys.includes(key);
});
return keys;

View File

@ -19,7 +19,7 @@ const Title = React.forwardRef<HTMLElement, TitleProps>((props, ref) => {
const { level = 1, ...restProps } = props;
let component: keyof JSX.IntrinsicElements;
if (TITLE_ELE_LIST.indexOf(level) !== -1) {
if (TITLE_ELE_LIST.includes(level)) {
component = `h${level}`;
} else {
warning(

View File

@ -262,14 +262,14 @@ describe('Typography', () => {
</Paragraph>,
);
if (triggerType === undefined || triggerType.indexOf('icon') !== -1) {
if (triggerType === undefined || triggerType.includes('icon')) {
if (icon) {
expect(wrapper.querySelectorAll('.anticon-highlight').length).toBeGreaterThan(0);
} else {
expect(wrapper.querySelectorAll('.anticon-edit').length).toBeGreaterThan(0);
}
if (triggerType === undefined || triggerType.indexOf('text') === -1) {
if (triggerType === undefined || !triggerType.includes('text')) {
fireEvent.click(wrapper.firstChild!);
expect(onStart).not.toHaveBeenCalled();
}
@ -295,15 +295,15 @@ describe('Typography', () => {
fireEvent.click(wrapper.querySelectorAll('.ant-typography-edit')[0]);
expect(onStart).toHaveBeenCalled();
if (triggerType !== undefined && triggerType.indexOf('text') !== -1) {
if (triggerType !== undefined && triggerType.includes('text')) {
fireEvent.keyDown(wrapper.querySelector('textarea')!, { keyCode: KeyCode.ESC });
fireEvent.keyUp(wrapper.querySelector('textarea')!, { keyCode: KeyCode.ESC });
expect(onChange).not.toHaveBeenCalled();
}
}
if (triggerType !== undefined && triggerType.indexOf('text') !== -1) {
if (triggerType.indexOf('icon') === -1) {
if (triggerType !== undefined && triggerType.includes('text')) {
if (!triggerType.includes('icon')) {
expect(wrapper.querySelectorAll('.anticon-highlight').length).toBe(0);
expect(wrapper.querySelectorAll('.anticon-edit').length).toBe(0);
}

View File

@ -49,6 +49,18 @@ See: https://ant.design/docs/react/customize-theme .
While you can override a component's style, we don't recommend doing so. antd is not only a set of React components, but also a design specification as well.
## How to avoid breaking change when update version?
antd will avoid breaking change in minor & patch version. You can safe do follow things:
- Official demo usage
- FAQ suggestion. Including codesandbox sample, marked as FAQ issue
And which you should avoid to do:
- Bug as feature. It will break in any other case (e.g. Use div as Tabs children)
- Use magic code to realize requirement but which can be realized with normal API
## How do I replace Moment.js with Day.js to reduce bundle size
Please refer to [Replace Moment.js](/docs/react/replace-moment).

View File

@ -49,6 +49,18 @@ title: FAQ
你可以覆盖它们的样式但是我们不推荐这么做。antd 是一系列 React 组件,但同样是一套设计规范。
## 如何避免升级导致的破坏性变更?
antd 在 minor 和 patch 版本迭代中会避免引入破坏性变更,遵从以下原则会确保不会破坏你的代码:
- 使用出现在官方 Demo 中的写法
- FAQ 中出现的解法,包含代码片段以及 codesandbox 示例、issue 中当前版本标记 FAQ label 的
而下述变更则需要开发者自行校验:
- 特定场景的错误用法BUG as Feature例如 Tabs 下直接包 div 的用法)
- 可以通过正常用法实现功能需求却魔改的
## 如何使用 Day.js 替换 Moment.js 来减小打包大小?
可以参考[替换 Moment.js](/docs/react/replace-moment)。

View File

@ -68,10 +68,13 @@ Let's create a `ProductList` component that we can use in multiple places to sho
Create `src/components/ProductList.tsx` by typing:
```js
```tsx
import { Table, Popconfirm, Button } from 'antd';
const ProductList = ({ onDelete, products }) => {
const ProductList: React.FC<{ products: { name: string }[]; onDelete: (id: string) => void }> = ({
onDelete,
products,
}) => {
const columns = [
{
title: 'Name',
@ -112,16 +115,12 @@ export function queryProductList() {
export function queryProductList() {
return new Promise(resolve => {
setTimeout(() => {
resolve([
{
id: 1,
name: 'dva',
},
{
id: 2,
name: 'antd',
},
]);
resolve({
data: [
{ id: 1, name: 'dva' },
{ id: 2, name: 'antd' },
],
});
}, 2000);
});
}
@ -134,7 +133,7 @@ import { useRequest } from 'umi';
import { queryProductList } from '@/services/product';
export default function useProductList(params: { pageSize: number; current: number }) {
const msg = useRequest(() => queryUserList(params));
const msg = useRequest(() => queryProductList(params));
const deleteProducts = async (id: string) => {
try {

View File

@ -109,16 +109,12 @@ export function queryProductList() {
export function queryProductList() {
return new Promise(resolve => {
setTimeout(() => {
resolve([
{
id: 1,
name: 'dva',
},
{
id: 2,
name: 'antd',
},
]);
resolve({
data: [
{ id: 1, name: 'dva' },
{ id: 2, name: 'antd' },
],
});
}, 2000);
});
}

View File

@ -21,7 +21,7 @@ async function execute() {
logs = _.remove(logs, ({ author_email: email }) => {
for (let i = 0; i < excludes.length; i++) {
const item = excludes[i];
if (email.indexOf(item) !== -1) {
if (email.includes(item)) {
return false;
}
}

View File

@ -19,7 +19,7 @@ export default class Palette extends React.Component {
this.hexColors = {};
Object.keys(this.colorNodes).forEach(key => {
const computedColor = getComputedStyle(this.colorNodes[key])['background-color'];
if (computedColor.indexOf('rgba') >= 0) {
if (computedColor.includes('rgba')) {
this.hexColors[key] = computedColor;
} else {
this.hexColors[key] = rgbToHex(computedColor);

View File

@ -3,7 +3,7 @@ import MainContent from './MainContent';
import * as utils from '../utils';
function isChangelog(pathname) {
return pathname.indexOf('changelog') >= 0;
return pathname.includes('changelog');
}
export default collect(async nextProps => {