Skip to content

Commit 265a921

Browse files
authored
feat(useSelections): support object array (#2485)
* feat(useSelections): support object array * fix: solve error
1 parent c7aea2b commit 265a921

File tree

7 files changed

+463
-118
lines changed

7 files changed

+463
-118
lines changed
Lines changed: 171 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,196 @@
11
import { act, renderHook } from '@testing-library/react';
22
import useSelections from '../index';
3+
import type { Options } from '../index';
34

4-
const data = [1, 2, 3];
5+
const _data = [1, 2, 3];
6+
const _selected = [1];
7+
const _selectedItem = 1;
58

6-
const setup = <T>(items: T[], defaultSelected?: T[]) => {
7-
return renderHook(() => useSelections(items, defaultSelected));
9+
const _dataObj = [{ id: 1 }, { id: 2 }, { id: 3 }];
10+
const _selectedObj = [{ id: 1 }];
11+
const _selectedItemObj = { id: 1 };
12+
13+
const setup = <T>(items: T[], options?: T[] | Options<T>) => {
14+
return renderHook(() => useSelections(items, options));
15+
};
16+
17+
interface CaseCallback<T = number | object> {
18+
(data: T[], selected: T[], selectedItem: T): void;
19+
}
20+
21+
const runCaseCallback = (
22+
dataCallback: CaseCallback<number>,
23+
objDataCallback: CaseCallback<object>,
24+
) => {
25+
dataCallback(_data, _selected, _selectedItem);
26+
objDataCallback(_dataObj, _selectedObj, _selectedItemObj);
827
};
928

1029
describe('useSelections', () => {
1130
it('defaultSelected should work correct', () => {
12-
const { result } = setup(data, [1]);
13-
expect(result.current.selected).toEqual([1]);
14-
expect(result.current.isSelected(1)).toBe(true);
31+
const caseCallback: CaseCallback = (data, selected, selectedItem) => {
32+
const { result } = setup(data, {
33+
defaultSelected: selected,
34+
itemKey: 'id',
35+
});
36+
37+
expect(result.current.selected).toEqual(selected);
38+
expect(result.current.isSelected(selectedItem)).toBe(true);
39+
};
40+
41+
runCaseCallback(caseCallback, caseCallback);
1542
});
1643

1744
it('select and unSelect should work correct', () => {
18-
const { result } = setup(data, [1]);
19-
const { unSelect, select } = result.current;
20-
act(() => {
21-
unSelect(1);
22-
});
23-
expect(result.current.selected).toEqual([]);
24-
expect(result.current.isSelected(1)).toBe(false);
25-
expect(result.current.allSelected).toBe(false);
26-
act(() => {
27-
select(1);
28-
});
29-
expect(result.current.selected).toEqual([1]);
30-
expect(result.current.isSelected(1)).toBe(true);
31-
expect(result.current.allSelected).toBe(false);
45+
const caseCallback: CaseCallback = (data, selected, selectedItem) => {
46+
const { result } = setup(data, {
47+
defaultSelected: selected,
48+
itemKey: 'id',
49+
});
50+
const { unSelect, select } = result.current;
51+
52+
act(() => {
53+
unSelect(selectedItem);
54+
});
55+
expect(result.current.selected).toEqual([]);
56+
expect(result.current.isSelected(selectedItem)).toBe(false);
57+
expect(result.current.allSelected).toBe(false);
58+
59+
act(() => {
60+
select(selectedItem);
61+
});
62+
expect(result.current.selected).toEqual(selected);
63+
expect(result.current.isSelected(selectedItem)).toBe(true);
64+
expect(result.current.allSelected).toBe(false);
65+
};
66+
67+
runCaseCallback(caseCallback, caseCallback);
3268
});
3369

3470
it('toggle should work correct', () => {
35-
const { result } = setup(data);
36-
const { toggle } = result.current;
37-
act(() => {
38-
toggle(1);
39-
});
40-
expect(result.current.selected).toEqual([1]);
41-
expect(result.current.isSelected(1)).toBe(true);
42-
expect(result.current.allSelected).toBe(false);
43-
act(() => {
44-
toggle(1);
45-
});
46-
expect(result.current.selected).toEqual([]);
47-
expect(result.current.isSelected(1)).toBe(false);
48-
expect(result.current.allSelected).toBe(false);
71+
const caseCallback: CaseCallback = (data, selected, selectedItem) => {
72+
const { result } = setup(data, {
73+
itemKey: 'id',
74+
});
75+
const { toggle } = result.current;
76+
77+
act(() => {
78+
toggle(selectedItem);
79+
});
80+
expect(result.current.selected).toEqual(selected);
81+
expect(result.current.isSelected(selectedItem)).toBe(true);
82+
expect(result.current.allSelected).toBe(false);
83+
84+
act(() => {
85+
toggle(selectedItem);
86+
});
87+
expect(result.current.selected).toEqual([]);
88+
expect(result.current.isSelected(selectedItem)).toBe(false);
89+
expect(result.current.allSelected).toBe(false);
90+
};
91+
92+
runCaseCallback(caseCallback, caseCallback);
4993
});
5094

5195
it('selectAll and unSelectAll should work correct', async () => {
52-
const { result } = setup(data);
53-
const { selectAll, unSelectAll } = result.current;
54-
55-
expect(result.current.noneSelected).toBe(true);
56-
act(() => {
57-
selectAll();
58-
});
59-
expect(result.current.selected).toEqual([1, 2, 3]);
60-
expect(result.current.allSelected).toBe(true);
61-
expect(result.current.noneSelected).toBe(false);
62-
expect(result.current.partiallySelected).toBe(false);
63-
64-
act(() => {
65-
unSelectAll();
66-
});
67-
expect(result.current.selected).toEqual([]);
68-
expect(result.current.allSelected).toBe(false);
69-
expect(result.current.noneSelected).toBe(true);
70-
expect(result.current.partiallySelected).toBe(false);
96+
const caseCallback: CaseCallback = (data) => {
97+
const { result } = setup(data, {
98+
itemKey: 'id',
99+
});
100+
const { selectAll, unSelectAll } = result.current;
101+
102+
expect(result.current.noneSelected).toBe(true);
103+
act(() => {
104+
selectAll();
105+
});
106+
expect(result.current.selected).toEqual(data);
107+
expect(result.current.allSelected).toBe(true);
108+
expect(result.current.noneSelected).toBe(false);
109+
expect(result.current.partiallySelected).toBe(false);
110+
111+
act(() => {
112+
unSelectAll();
113+
});
114+
expect(result.current.selected).toEqual([]);
115+
expect(result.current.allSelected).toBe(false);
116+
expect(result.current.noneSelected).toBe(true);
117+
expect(result.current.partiallySelected).toBe(false);
118+
};
119+
120+
runCaseCallback(caseCallback, caseCallback);
71121
});
72122

73123
it('toggleAll should work correct', async () => {
74-
const { result } = setup(data);
75-
const { toggleAll } = result.current;
76-
expect(result.current.noneSelected).toBe(true);
77-
act(() => {
78-
toggleAll();
79-
});
80-
expect(result.current.selected).toEqual([1, 2, 3]);
81-
expect(result.current.allSelected).toBe(true);
82-
expect(result.current.noneSelected).toBe(false);
83-
expect(result.current.partiallySelected).toBe(false);
84-
85-
act(() => {
86-
toggleAll();
87-
});
88-
expect(result.current.selected).toEqual([]);
89-
expect(result.current.allSelected).toBe(false);
90-
expect(result.current.noneSelected).toBe(true);
91-
expect(result.current.partiallySelected).toBe(false);
124+
const caseCallback: CaseCallback = (data) => {
125+
const { result } = setup(data, {
126+
itemKey: 'id',
127+
});
128+
const { toggleAll } = result.current;
129+
130+
expect(result.current.noneSelected).toBe(true);
131+
act(() => {
132+
toggleAll();
133+
});
134+
expect(result.current.selected).toEqual(data);
135+
expect(result.current.allSelected).toBe(true);
136+
expect(result.current.noneSelected).toBe(false);
137+
expect(result.current.partiallySelected).toBe(false);
138+
139+
act(() => {
140+
toggleAll();
141+
});
142+
expect(result.current.selected).toEqual([]);
143+
expect(result.current.allSelected).toBe(false);
144+
expect(result.current.noneSelected).toBe(true);
145+
expect(result.current.partiallySelected).toBe(false);
146+
};
147+
148+
runCaseCallback(caseCallback, caseCallback);
92149
});
93150

94151
it('setSelected should work correct', async () => {
95-
const { result } = setup(data);
96-
const { setSelected } = result.current;
97-
expect(result.current.noneSelected).toBe(true);
98-
act(() => {
99-
setSelected([1]);
100-
});
101-
expect(result.current.selected).toEqual([1]);
102-
expect(result.current.isSelected(1)).toBe(true);
103-
expect(result.current.noneSelected).toBe(false);
104-
expect(result.current.allSelected).toBe(false);
105-
expect(result.current.partiallySelected).toBe(true);
106-
107-
act(() => {
108-
setSelected([]);
109-
});
110-
expect(result.current.selected).toEqual([]);
111-
expect(result.current.isSelected(1)).toBe(false);
112-
expect(result.current.noneSelected).toBe(true);
113-
expect(result.current.allSelected).toBe(false);
114-
expect(result.current.partiallySelected).toBe(false);
115-
116-
act(() => {
117-
setSelected([1, 2, 3]);
118-
});
119-
expect(result.current.selected).toEqual([1, 2, 3]);
120-
expect(result.current.isSelected(1)).toBe(true);
121-
expect(result.current.noneSelected).toBe(false);
122-
expect(result.current.allSelected).toBe(true);
123-
expect(result.current.partiallySelected).toBe(false);
152+
const caseCallback: CaseCallback = (data, selected, selectedItem) => {
153+
const { result } = setup(data, {
154+
itemKey: 'id',
155+
});
156+
const { setSelected } = result.current;
157+
158+
expect(result.current.noneSelected).toBe(true);
159+
act(() => {
160+
setSelected(selected);
161+
});
162+
expect(result.current.selected).toEqual(selected);
163+
expect(result.current.isSelected(selectedItem)).toBe(true);
164+
expect(result.current.noneSelected).toBe(false);
165+
expect(result.current.allSelected).toBe(false);
166+
expect(result.current.partiallySelected).toBe(true);
167+
168+
act(() => {
169+
setSelected([]);
170+
});
171+
expect(result.current.selected).toEqual([]);
172+
expect(result.current.isSelected(selectedItem)).toBe(false);
173+
expect(result.current.noneSelected).toBe(true);
174+
expect(result.current.allSelected).toBe(false);
175+
expect(result.current.partiallySelected).toBe(false);
176+
177+
act(() => {
178+
setSelected(data);
179+
});
180+
expect(result.current.selected).toEqual(data);
181+
expect(result.current.isSelected(selectedItem)).toBe(true);
182+
expect(result.current.noneSelected).toBe(false);
183+
expect(result.current.allSelected).toBe(true);
184+
expect(result.current.partiallySelected).toBe(false);
185+
};
186+
187+
runCaseCallback(caseCallback, caseCallback);
188+
});
189+
190+
it('legacy parameter should work in <4.0', async () => {
191+
const { result } = setup(_data, _selected);
192+
193+
expect(result.current.selected).toEqual(_selected);
194+
expect(result.current.isSelected(_selectedItem)).toBe(true);
124195
});
125196
});

packages/hooks/src/useSelections/demo/demo1.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ export default () => {
2121

2222
const { selected, allSelected, isSelected, toggle, toggleAll, partiallySelected } = useSelections(
2323
list,
24-
[1],
24+
{
25+
defaultSelected: [1],
26+
},
2527
);
2628

2729
return (
2830
<div>
29-
<div>Selected : {selected.join(',')}</div>
31+
<div>Selected: {selected.join(',')}</div>
3032
<div style={{ borderBottom: '1px solid #E9E9E9', padding: '10px 0' }}>
3133
<Checkbox checked={allSelected} onClick={toggleAll} indeterminate={partiallySelected}>
3234
Check all
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* title: Object array
3+
* desc: When array items are object, you need to specify the field name for the unique key.
4+
*
5+
* title.zh-CN: 对象数组
6+
* desc.zh-CN: 数组项是对象时,需要指定唯一 key 的字段名称。
7+
*/
8+
9+
import { Checkbox, Col, Row } from 'antd';
10+
import React, { useMemo, useState } from 'react';
11+
import { useSelections } from 'ahooks';
12+
13+
export default () => {
14+
const [hideOdd, setHideOdd] = useState(false);
15+
const list = useMemo(() => {
16+
if (hideOdd) {
17+
return [2, 4, 6, 8].map((id) => ({ id }));
18+
}
19+
return [1, 2, 3, 4, 5, 6, 7, 8].map((id) => ({ id }));
20+
}, [hideOdd]);
21+
22+
const { selected, allSelected, isSelected, toggle, toggleAll, partiallySelected } = useSelections(
23+
list,
24+
{
25+
defaultSelected: [{ id: 1 }],
26+
itemKey: 'id',
27+
},
28+
);
29+
30+
return (
31+
<div>
32+
<div>Selected: {JSON.stringify(selected)}</div>
33+
<div style={{ borderBottom: '1px solid #E9E9E9', padding: '10px 0' }}>
34+
<Checkbox checked={allSelected} onClick={toggleAll} indeterminate={partiallySelected}>
35+
Check all
36+
</Checkbox>
37+
<Checkbox checked={hideOdd} onClick={() => setHideOdd((v) => !v)}>
38+
Hide Odd
39+
</Checkbox>
40+
</div>
41+
<Row style={{ padding: '10px 0' }}>
42+
{list.map((item) => (
43+
<Col span={12} key={item.id}>
44+
<Checkbox checked={isSelected(item)} onClick={() => toggle(item)}>
45+
{item.id}
46+
</Checkbox>
47+
</Col>
48+
))}
49+
</Row>
50+
</div>
51+
);
52+
};

0 commit comments

Comments
 (0)