Skip to content

Commit

Permalink
feat(useSelections): support object array
Browse files Browse the repository at this point in the history
  • Loading branch information
liuyib committed Feb 25, 2024
1 parent 39284dc commit 393bb70
Show file tree
Hide file tree
Showing 7 changed files with 463 additions and 118 deletions.
271 changes: 171 additions & 100 deletions packages/hooks/src/useSelections/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,125 +1,196 @@
import { act, renderHook } from '@testing-library/react';
import useSelections from '../index';
import type { Options } from '../index';

const data = [1, 2, 3];
const _data = [1, 2, 3];
const _selected = [1];
const _selectedItem = 1;

const setup = <T>(items: T[], defaultSelected?: T[]) => {
return renderHook(() => useSelections(items, defaultSelected));
const _dataObj = [{ id: 1 }, { id: 2 }, { id: 3 }];
const _selectedObj = [{ id: 1 }];
const _selectedItemObj = { id: 1 };

const setup = <T>(items: T[], options?: T[] | Options<T>) => {
return renderHook(() => useSelections(items, options));
};

interface CaseCallback<T = number | object> {
(data: T[], selected: T[], selectedItem: T): void;
}

const runCaseCallback = (
dataCallback: CaseCallback<number>,
objDataCallback: CaseCallback<object>,
) => {
dataCallback(_data, _selected, _selectedItem);
objDataCallback(_dataObj, _selectedObj, _selectedItemObj);
};

describe('useSelections', () => {
it('defaultSelected should work correct', () => {
const { result } = setup(data, [1]);
expect(result.current.selected).toEqual([1]);
expect(result.current.isSelected(1)).toBe(true);
const caseCallback: CaseCallback = (data, selected, selectedItem) => {
const { result } = setup(data, {
defaultSelected: selected,
itemKey: 'id',
});

expect(result.current.selected).toEqual(selected);
expect(result.current.isSelected(selectedItem)).toBe(true);
};

runCaseCallback(caseCallback, caseCallback);
});

it('select and unSelect should work correct', () => {
const { result } = setup(data, [1]);
const { unSelect, select } = result.current;
act(() => {
unSelect(1);
});
expect(result.current.selected).toEqual([]);
expect(result.current.isSelected(1)).toBe(false);
expect(result.current.allSelected).toBe(false);
act(() => {
select(1);
});
expect(result.current.selected).toEqual([1]);
expect(result.current.isSelected(1)).toBe(true);
expect(result.current.allSelected).toBe(false);
const caseCallback: CaseCallback = (data, selected, selectedItem) => {
const { result } = setup(data, {
defaultSelected: selected,
itemKey: 'id',
});
const { unSelect, select } = result.current;

act(() => {
unSelect(selectedItem);
});
expect(result.current.selected).toEqual([]);
expect(result.current.isSelected(selectedItem)).toBe(false);
expect(result.current.allSelected).toBe(false);

act(() => {
select(selectedItem);
});
expect(result.current.selected).toEqual(selected);
expect(result.current.isSelected(selectedItem)).toBe(true);
expect(result.current.allSelected).toBe(false);
};

runCaseCallback(caseCallback, caseCallback);
});

it('toggle should work correct', () => {
const { result } = setup(data);
const { toggle } = result.current;
act(() => {
toggle(1);
});
expect(result.current.selected).toEqual([1]);
expect(result.current.isSelected(1)).toBe(true);
expect(result.current.allSelected).toBe(false);
act(() => {
toggle(1);
});
expect(result.current.selected).toEqual([]);
expect(result.current.isSelected(1)).toBe(false);
expect(result.current.allSelected).toBe(false);
const caseCallback: CaseCallback = (data, selected, selectedItem) => {
const { result } = setup(data, {
itemKey: 'id',
});
const { toggle } = result.current;

act(() => {
toggle(selectedItem);
});
expect(result.current.selected).toEqual(selected);
expect(result.current.isSelected(selectedItem)).toBe(true);
expect(result.current.allSelected).toBe(false);

act(() => {
toggle(selectedItem);
});
expect(result.current.selected).toEqual([]);
expect(result.current.isSelected(selectedItem)).toBe(false);
expect(result.current.allSelected).toBe(false);
};

runCaseCallback(caseCallback, caseCallback);
});

it('selectAll and unSelectAll should work correct', async () => {
const { result } = setup(data);
const { selectAll, unSelectAll } = result.current;

expect(result.current.noneSelected).toBe(true);
act(() => {
selectAll();
});
expect(result.current.selected).toEqual([1, 2, 3]);
expect(result.current.allSelected).toBe(true);
expect(result.current.noneSelected).toBe(false);
expect(result.current.partiallySelected).toBe(false);

act(() => {
unSelectAll();
});
expect(result.current.selected).toEqual([]);
expect(result.current.allSelected).toBe(false);
expect(result.current.noneSelected).toBe(true);
expect(result.current.partiallySelected).toBe(false);
const caseCallback: CaseCallback = (data) => {
const { result } = setup(data, {
itemKey: 'id',
});
const { selectAll, unSelectAll } = result.current;

expect(result.current.noneSelected).toBe(true);
act(() => {
selectAll();
});
expect(result.current.selected).toEqual(data);
expect(result.current.allSelected).toBe(true);
expect(result.current.noneSelected).toBe(false);
expect(result.current.partiallySelected).toBe(false);

act(() => {
unSelectAll();
});
expect(result.current.selected).toEqual([]);
expect(result.current.allSelected).toBe(false);
expect(result.current.noneSelected).toBe(true);
expect(result.current.partiallySelected).toBe(false);
};

runCaseCallback(caseCallback, caseCallback);
});

it('toggleAll should work correct', async () => {
const { result } = setup(data);
const { toggleAll } = result.current;
expect(result.current.noneSelected).toBe(true);
act(() => {
toggleAll();
});
expect(result.current.selected).toEqual([1, 2, 3]);
expect(result.current.allSelected).toBe(true);
expect(result.current.noneSelected).toBe(false);
expect(result.current.partiallySelected).toBe(false);

act(() => {
toggleAll();
});
expect(result.current.selected).toEqual([]);
expect(result.current.allSelected).toBe(false);
expect(result.current.noneSelected).toBe(true);
expect(result.current.partiallySelected).toBe(false);
const caseCallback: CaseCallback = (data) => {
const { result } = setup(data, {
itemKey: 'id',
});
const { toggleAll } = result.current;

expect(result.current.noneSelected).toBe(true);
act(() => {
toggleAll();
});
expect(result.current.selected).toEqual(data);
expect(result.current.allSelected).toBe(true);
expect(result.current.noneSelected).toBe(false);
expect(result.current.partiallySelected).toBe(false);

act(() => {
toggleAll();
});
expect(result.current.selected).toEqual([]);
expect(result.current.allSelected).toBe(false);
expect(result.current.noneSelected).toBe(true);
expect(result.current.partiallySelected).toBe(false);
};

runCaseCallback(caseCallback, caseCallback);
});

it('setSelected should work correct', async () => {
const { result } = setup(data);
const { setSelected } = result.current;
expect(result.current.noneSelected).toBe(true);
act(() => {
setSelected([1]);
});
expect(result.current.selected).toEqual([1]);
expect(result.current.isSelected(1)).toBe(true);
expect(result.current.noneSelected).toBe(false);
expect(result.current.allSelected).toBe(false);
expect(result.current.partiallySelected).toBe(true);

act(() => {
setSelected([]);
});
expect(result.current.selected).toEqual([]);
expect(result.current.isSelected(1)).toBe(false);
expect(result.current.noneSelected).toBe(true);
expect(result.current.allSelected).toBe(false);
expect(result.current.partiallySelected).toBe(false);

act(() => {
setSelected([1, 2, 3]);
});
expect(result.current.selected).toEqual([1, 2, 3]);
expect(result.current.isSelected(1)).toBe(true);
expect(result.current.noneSelected).toBe(false);
expect(result.current.allSelected).toBe(true);
expect(result.current.partiallySelected).toBe(false);
const caseCallback: CaseCallback = (data, selected, selectedItem) => {
const { result } = setup(data, {
itemKey: 'id',
});
const { setSelected } = result.current;

expect(result.current.noneSelected).toBe(true);
act(() => {
setSelected(selected);
});
expect(result.current.selected).toEqual(selected);
expect(result.current.isSelected(selectedItem)).toBe(true);
expect(result.current.noneSelected).toBe(false);
expect(result.current.allSelected).toBe(false);
expect(result.current.partiallySelected).toBe(true);

act(() => {
setSelected([]);
});
expect(result.current.selected).toEqual([]);
expect(result.current.isSelected(selectedItem)).toBe(false);
expect(result.current.noneSelected).toBe(true);
expect(result.current.allSelected).toBe(false);
expect(result.current.partiallySelected).toBe(false);

act(() => {
setSelected(data);
});
expect(result.current.selected).toEqual(data);
expect(result.current.isSelected(selectedItem)).toBe(true);
expect(result.current.noneSelected).toBe(false);
expect(result.current.allSelected).toBe(true);
expect(result.current.partiallySelected).toBe(false);
};

runCaseCallback(caseCallback, caseCallback);
});

it('legacy parameter should work in <4.0', async () => {
const { result } = setup(_data, _selected);

expect(result.current.selected).toEqual(_selected);
expect(result.current.isSelected(_selectedItem)).toBe(true);
});
});
6 changes: 4 additions & 2 deletions packages/hooks/src/useSelections/demo/demo1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ export default () => {

const { selected, allSelected, isSelected, toggle, toggleAll, partiallySelected } = useSelections(
list,
[1],
{
defaultSelected: [1],
},
);

return (
<div>
<div>Selected : {selected.join(',')}</div>
<div>Selected: {selected.join(',')}</div>
<div style={{ borderBottom: '1px solid #E9E9E9', padding: '10px 0' }}>
<Checkbox checked={allSelected} onClick={toggleAll} indeterminate={partiallySelected}>
Check all
Expand Down
52 changes: 52 additions & 0 deletions packages/hooks/src/useSelections/demo/demo2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* title: Object array
* desc: When array items are object, you need to specify the field name for the unique key.
*
* title.zh-CN: 对象数组
* desc.zh-CN: 数组项是对象时,需要指定唯一 key 的字段名称。
*/

import { Checkbox, Col, Row } from 'antd';
import React, { useMemo, useState } from 'react';
import { useSelections } from 'ahooks';

export default () => {
const [hideOdd, setHideOdd] = useState(false);
const list = useMemo(() => {
if (hideOdd) {
return [2, 4, 6, 8].map((id) => ({ id }));
}
return [1, 2, 3, 4, 5, 6, 7, 8].map((id) => ({ id }));
}, [hideOdd]);

const { selected, allSelected, isSelected, toggle, toggleAll, partiallySelected } = useSelections(
list,
{
defaultSelected: [{ id: 1 }],
itemKey: 'id',
},
);

return (
<div>
<div>Selected: {JSON.stringify(selected)}</div>
<div style={{ borderBottom: '1px solid #E9E9E9', padding: '10px 0' }}>
<Checkbox checked={allSelected} onClick={toggleAll} indeterminate={partiallySelected}>
Check all
</Checkbox>
<Checkbox checked={hideOdd} onClick={() => setHideOdd((v) => !v)}>
Hide Odd
</Checkbox>
</div>
<Row style={{ padding: '10px 0' }}>
{list.map((item) => (
<Col span={12} key={item.id}>
<Checkbox checked={isSelected(item)} onClick={() => toggle(item)}>
{item.id}
</Checkbox>
</Col>
))}
</Row>
</div>
);
};
Loading

0 comments on commit 393bb70

Please sign in to comment.