Skip to content

Commit

Permalink
fix: 4.x Transfer selectInvert should be corrected (ant-design#47134)
Browse files Browse the repository at this point in the history
  • Loading branch information
linxianxi authored Jan 27, 2024
1 parent df63d1c commit 6a0f811
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 58 deletions.
53 changes: 33 additions & 20 deletions components/transfer/__tests__/dropdown.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,30 +87,43 @@ describe('Transfer.Dropdown', () => {
});

describe('select invert', () => {
[
{ name: 'with pagination', props: listProps, index: 2, keys: ['c', 'd'] },
{
name: 'without pagination',
props: { ...listProps, pagination: null as any },
index: 1,
keys: ['c', 'd', 'e'],
},
].forEach(({ name, props, index, keys }) => {
it(name, () => {
jest.useFakeTimers();
it('with pagination', () => {
jest.useFakeTimers();

const onSelectChange = jest.fn();
const { container } = render(
<Transfer {...listProps} selectedKeys={undefined} onSelectChange={onSelectChange} />,
);
fireEvent.mouseEnter(container.querySelector('.ant-transfer-list-header-dropdown')!);
act(() => {
jest.runAllTimers();
});

const onSelectChange = jest.fn();
const { container } = render(<Transfer {...props} onSelectChange={onSelectChange} />);
fireEvent.mouseEnter(container.querySelector('.ant-transfer-list-header-dropdown')!);
act(() => {
jest.runAllTimers();
});
clickItem(container, 0);
expect(onSelectChange).toHaveBeenCalledWith(['b', 'c', 'd', 'e'], []);

clickItem(container, index);
expect(onSelectChange).toHaveBeenCalledWith(keys, []);
clickItem(container, 2);
expect(onSelectChange).toHaveBeenCalledWith(['e'], []);

jest.useRealTimers();
jest.useRealTimers();
});

it('without pagination', () => {
jest.useFakeTimers();

const onSelectChange = jest.fn();
const { container } = render(
<Transfer {...listProps} pagination={null as any} onSelectChange={onSelectChange} />,
);
fireEvent.mouseEnter(container.querySelector('.ant-transfer-list-header-dropdown')!);
act(() => {
jest.runAllTimers();
});

clickItem(container, 1);
expect(onSelectChange).toHaveBeenCalledWith(['c', 'd', 'e'], []);

jest.useRealTimers();
});
});

Expand Down
30 changes: 18 additions & 12 deletions components/transfer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
if (selectedKeys) {
const mergedTargetKeys = targetKeys || [];
return {
sourceSelectedKeys: selectedKeys.filter(key => !mergedTargetKeys.includes(key)),
targetSelectedKeys: selectedKeys.filter(key => mergedTargetKeys.includes(key)),
sourceSelectedKeys: selectedKeys.filter((key) => !mergedTargetKeys.includes(key)),
targetSelectedKeys: selectedKeys.filter((key) => mergedTargetKeys.includes(key)),
};
}

Expand All @@ -149,8 +149,8 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com

const { selectedKeys = [], targetKeys = [] } = props;
this.state = {
sourceSelectedKeys: selectedKeys.filter(key => !targetKeys.includes(key)),
targetSelectedKeys: selectedKeys.filter(key => targetKeys.includes(key)),
sourceSelectedKeys: selectedKeys.filter((key) => !targetKeys.includes(key)),
targetSelectedKeys: selectedKeys.filter((key) => targetKeys.includes(key)),
};
}

Expand Down Expand Up @@ -184,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 => !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.includes(targetKey));
: targetKeys.filter((targetKey) => !newMoveKeys.includes(targetKey));

// empty checked keys
const oppositeDirection = direction === 'right' ? 'left' : 'right';
Expand All @@ -204,15 +204,21 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com

moveToRight = () => this.moveTo('right');

onItemSelectAll = (direction: TransferDirection, selectedKeys: string[], checkAll: boolean) => {
this.setStateKeys(direction, prevKeys => {
onItemSelectAll = (
direction: TransferDirection,
selectedKeys: string[],
checkAll: boolean | 'replace',
) => {
this.setStateKeys(direction, (prevKeys) => {
let mergedCheckedKeys: string[] = [];
if (checkAll) {
if (checkAll === 'replace') {
mergedCheckedKeys = selectedKeys;
} else if (checkAll) {
// Merge current keys with origin key
mergedCheckedKeys = Array.from(new Set<string>([...prevKeys, ...selectedKeys]));
} else {
// Remove current keys from origin keys
mergedCheckedKeys = prevKeys.filter(key => !selectedKeys.includes(key));
mergedCheckedKeys = prevKeys.filter((key) => !selectedKeys.includes(key));
}

this.handleSelectChange(direction, mergedCheckedKeys);
Expand Down Expand Up @@ -275,7 +281,7 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
this.setStateKeys('right', []);

onChange?.(
targetKeys.filter(key => !selectedKeys.includes(key)),
targetKeys.filter((key) => !selectedKeys.includes(key)),
'left',
[...selectedKeys],
);
Expand Down Expand Up @@ -347,7 +353,7 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
render() {
return (
<LocaleReceiver componentName="Transfer" defaultLocale={defaultLocale.Transfer}>
{contextLocale => (
{(contextLocale) => (
<ConfigConsumer>
{({ getPrefixCls, renderEmpty, direction }: ConfigConsumerProps) => (
<FormItemInputContext.Consumer>
Expand Down
42 changes: 16 additions & 26 deletions components/transfer/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function isRenderResultPlainObject(result: RenderResult): result is RenderResult
}

function getEnabledItemKeys<RecordType extends KeyWiseTransferItem>(items: RecordType[]) {
return items.filter(data => !data.disabled).map(data => data.key);
return items.filter((data) => !data.disabled).map((data) => data.key);
}

export interface RenderedItem<RecordType> {
Expand All @@ -50,7 +50,7 @@ export interface TransferListProps<RecordType> extends TransferLocale {
checkedKeys: string[];
handleFilter: (e: React.ChangeEvent<HTMLInputElement>) => void;
onItemSelect: (key: string, check: boolean) => void;
onItemSelectAll: (dataSource: string[], checkAll: boolean) => void;
onItemSelectAll: (dataSource: string[], checkAll: boolean | 'replace') => void;
onItemRemove?: (keys: string[]) => void;
handleClear: () => void;
/** Render item */
Expand Down Expand Up @@ -103,7 +103,7 @@ export default class TransferList<
if (checkedKeys.length === 0) {
return 'none';
}
if (filteredItems.every(item => checkedKeys.includes(item.key) || !!item.disabled)) {
if (filteredItems.every((item) => checkedKeys.includes(item.key) || !!item.disabled)) {
return 'all';
}
return 'part';
Expand All @@ -117,7 +117,7 @@ export default class TransferList<
const filteredItems: RecordType[] = [];
const filteredRenderItems: RenderedItem<RecordType>[] = [];

dataSource.forEach(item => {
dataSource.forEach((item) => {
const renderedItem = this.renderItem(item);
const { renderedText } = renderedItem;

Expand Down Expand Up @@ -257,7 +257,7 @@ export default class TransferList<
onChange={() => {
// Only select enabled items
onItemSelectAll(
filteredItems.filter(item => !item.disabled).map(({ key }) => key),
filteredItems.filter((item) => !item.disabled).map(({ key }) => key),
!checkedAll,
);
}}
Expand Down Expand Up @@ -367,7 +367,7 @@ export default class TransferList<
key: 'removeCurrent',
onClick: () => {
const pageKeys = getEnabledItemKeys(
(this.defaultListBodyRef.current?.getItems() || []).map(entity => entity.item),
(this.defaultListBodyRef.current?.getItems() || []).map((entity) => entity.item),
);
onItemRemove?.(pageKeys);
},
Expand All @@ -383,7 +383,7 @@ export default class TransferList<
},
label: removeAll,
},
].filter(item => item);
].filter((item) => item);
} else {
items = [
{
Expand All @@ -399,7 +399,7 @@ export default class TransferList<
key: 'selectCurrent',
onClick: () => {
const pageItems = this.defaultListBodyRef.current?.getItems() || [];
onItemSelectAll(getEnabledItemKeys(pageItems.map(entity => entity.item)), true);
onItemSelectAll(getEnabledItemKeys(pageItems.map((entity) => entity.item)), true);
},
label: selectCurrent,
}
Expand All @@ -408,29 +408,19 @@ export default class TransferList<
{
key: 'selectInvert',
onClick: () => {
let availableKeys: string[];
if (pagination) {
availableKeys = getEnabledItemKeys(
(this.defaultListBodyRef.current?.getItems() || []).map(entity => entity.item),
);
} else {
availableKeys = getEnabledItemKeys(filteredItems);
}

const availablePageItemKeys = getEnabledItemKeys(
(this.defaultListBodyRef.current?.getItems() || []).map((entity) => entity.item),
);
const checkedKeySet = new Set(checkedKeys);
const newCheckedKeys: string[] = [];
const newUnCheckedKeys: string[] = [];

availableKeys.forEach(key => {
const newCheckedKeysSet = new Set(checkedKeySet);
availablePageItemKeys.forEach((key) => {
if (checkedKeySet.has(key)) {
newUnCheckedKeys.push(key);
newCheckedKeysSet.delete(key);
} else {
newCheckedKeys.push(key);
newCheckedKeysSet.add(key);
}
});

onItemSelectAll(newCheckedKeys, true);
onItemSelectAll(newUnCheckedKeys, false);
onItemSelectAll?.(Array.from(newCheckedKeysSet), 'replace');
},
label: selectInvert,
},
Expand Down

0 comments on commit 6a0f811

Please sign in to comment.