Fix transfer selected keys (#51523)

* fix: transfer selected keys

* test: add transfer test case

* test: remove unused code

* refactor: use cal data

---------

Co-authored-by: karos <chenwenfan@harmonycloud.cn>
Co-authored-by: 二货机器人 <smith3816@gmail.com>
This commit is contained in:
IsKaros 2024-11-12 15:16:19 +08:00 committed by GitHub
parent 77804ad1d4
commit d80828ceff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 92 additions and 16 deletions

View File

@ -7,6 +7,7 @@ import Transfer from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
import Button from '../../button'; import Button from '../../button';
import { waitFakeTimer } from '../../../tests/utils';
const listCommonProps: { const listCommonProps: {
dataSource: { key: string; title: string; disabled?: boolean }[]; dataSource: { key: string; title: string; disabled?: boolean }[];
@ -767,6 +768,70 @@ describe('Transfer', () => {
errSpy.mockRestore(); errSpy.mockRestore();
}); });
it('it checks correctly after changing the dataSource', async () => {
const mockData = Array.from({ length: 10 }).map((_, i) => ({
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
}));
const initialTargetKeys = mockData
.filter((item) => Number(item.key) > 4)
.map((item) => item.key);
const defaultCheckedKeys = ['1', '2'];
const handleSelectChange = jest.fn();
const App: React.FC = () => {
const [targetKeys, setTargetKeys] = useState<TransferProps['targetKeys']>(initialTargetKeys);
const [selectedKeys, setSelectedKeys] = useState<TransferProps['targetKeys']>([]);
const [dataSource, setDataSource] = useState(mockData);
const onChange: TransferProps['onChange'] = (nextTargetKeys) => {
setTargetKeys(nextTargetKeys);
};
return (
<>
<Button
className="update-btn"
onClick={() => {
setSelectedKeys(defaultCheckedKeys);
setDataSource([]);
setDataSource([...mockData]);
// setTimeout(() => {
// setDataSource([...mockData]);
// });
}}
>
update
</Button>
<Transfer
dataSource={dataSource}
titles={['Source', 'Target']}
targetKeys={targetKeys}
selectedKeys={selectedKeys}
onChange={onChange}
onSelectChange={handleSelectChange}
render={(item) => item.title}
/>
</>
);
};
const { container } = render(<App />);
fireEvent.click(container.querySelector('.update-btn')!);
await waitFakeTimer();
defaultCheckedKeys.forEach((item) => {
expect(
container
?.querySelectorAll('.ant-transfer-list-content-item')
?.item(Number(item))
?.querySelector('input[type="checkbox"]')!,
).toBeChecked();
});
});
}); });
describe('immutable data', () => { describe('immutable data', () => {

View File

@ -1,4 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { useEvent, useMergedState } from 'rc-util';
import type { TransferKey } from '../interface'; import type { TransferKey } from '../interface';
@ -16,12 +17,12 @@ function flattenKeys(keys: Set<TransferKey>) {
export default function useSelection<T extends { key: TransferKey }>( export default function useSelection<T extends { key: TransferKey }>(
leftDataSource: T[], leftDataSource: T[],
rightDataSource: T[], rightDataSource: T[],
selectedKeys: TransferKey[] = EMPTY_KEYS, selectedKeys?: TransferKey[],
): [ ): [
sourceSelectedKeys: TransferKey[], sourceSelectedKeys: TransferKey[],
targetSelectedKeys: TransferKey[], targetSelectedKeys: TransferKey[],
setSourceSelectedKeys: React.Dispatch<React.SetStateAction<TransferKey[]>>, setSourceSelectedKeys: (srcKeys: TransferKey[]) => void,
setTargetSelectedKeys: React.Dispatch<React.SetStateAction<TransferKey[]>>, setTargetSelectedKeys: (srcKeys: TransferKey[]) => void,
] { ] {
// Prepare `dataSource` keys // Prepare `dataSource` keys
const [leftKeys, rightKeys] = React.useMemo( const [leftKeys, rightKeys] = React.useMemo(
@ -33,25 +34,35 @@ export default function useSelection<T extends { key: TransferKey }>(
); );
// Selected Keys // Selected Keys
const [sourceSelectedKeys, setSourceSelectedKeys] = React.useState(() => const [mergedSelectedKeys, setMergedSelectedKeys] = useMergedState(EMPTY_KEYS, {
filterKeys(selectedKeys, leftKeys), value: selectedKeys,
});
const sourceSelectedKeys = React.useMemo(
() => filterKeys(mergedSelectedKeys, leftKeys),
[mergedSelectedKeys, leftKeys],
); );
const [targetSelectedKeys, setTargetSelectedKeys] = React.useState(() => const targetSelectedKeys = React.useMemo(
filterKeys(selectedKeys, rightKeys), () => filterKeys(mergedSelectedKeys, rightKeys),
[mergedSelectedKeys, rightKeys],
); );
// Fill selected keys // // Reset when data changed
React.useEffect(() => { React.useEffect(() => {
setSourceSelectedKeys(filterKeys(selectedKeys, leftKeys)); setMergedSelectedKeys([
setTargetSelectedKeys(filterKeys(selectedKeys, rightKeys)); ...filterKeys(mergedSelectedKeys, leftKeys),
}, [selectedKeys]); ...filterKeys(mergedSelectedKeys, rightKeys),
]);
// Reset when data changed
React.useEffect(() => {
setSourceSelectedKeys(filterKeys(sourceSelectedKeys, leftKeys));
setTargetSelectedKeys(filterKeys(targetSelectedKeys, rightKeys));
}, [flattenKeys(leftKeys), flattenKeys(rightKeys)]); }, [flattenKeys(leftKeys), flattenKeys(rightKeys)]);
// Update keys
const setSourceSelectedKeys = useEvent((nextSrcKeys: TransferKey[]) => {
setMergedSelectedKeys([...nextSrcKeys, ...targetSelectedKeys]);
});
const setTargetSelectedKeys = useEvent((nextTargetKeys: TransferKey[]) => {
setMergedSelectedKeys([...sourceSelectedKeys, ...nextTargetKeys]);
});
return [ return [
// Keys // Keys
sourceSelectedKeys, sourceSelectedKeys,