Skip to content

Commit 2bc48a3

Browse files
committed
Feat: support 'treeExpandAction' prop for TreeSelect
1 parent ddc4ce0 commit 2bc48a3

File tree

7 files changed

+170
-30
lines changed

7 files changed

+170
-30
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ online example: https://tree-select-react-component.vercel.app/
8181
|treeDefaultExpandAll | default expand all treeNode | bool | false |
8282
|treeDefaultExpandedKeys | default expanded treeNode keys | Array<String> | - |
8383
|treeExpandedKeys | set tree expanded keys | Array<String> | - |
84+
|treeExpandAction | Tree open logic, optional: false \| `click` \| `doubleClick`, same as `expandAction` of `rc-tree` | string \| boolean | `click` |
8485
|treeCheckable | whether tree show checkbox (select callback will not fire) | bool | false |
8586
|treeCheckStrictly | check node precisely, parent and children nodes are not associated| bool | false |
8687
|filterTreeNode | whether filter treeNodes by input value. default filter by treeNode's treeNodeFilterProp prop's value | bool/Function(inputValue:string, treeNode:TreeNode) | Function |

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
"@babel/runtime": "^7.10.1",
7070
"classnames": "2.x",
7171
"rc-select": "~14.1.0",
72-
"rc-tree": "~5.5.0",
72+
"rc-tree": "~5.6.1",
7373
"rc-util": "^5.16.1"
7474
}
7575
}

src/OptionList.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const OptionList: React.RefForwardingComponent<ReviseRefOptionListProps> = (_, r
4141
fieldNames,
4242
onSelect,
4343
dropdownMatchSelectWidth,
44+
treeExpandAction,
4445
} = React.useContext(TreeSelectContext);
4546

4647
const {
@@ -244,6 +245,7 @@ const OptionList: React.RefForwardingComponent<ReviseRefOptionListProps> = (_, r
244245
onExpand={onInternalExpand}
245246
onLoad={onTreeLoad}
246247
filterTreeNode={filterTreeNode}
248+
expandAction={treeExpandAction}
247249
/>
248250
</div>
249251
);

src/TreeSelect.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react';
22
import { BaseSelect } from 'rc-select';
33
import type { IconType } from 'rc-tree/lib/interface';
4+
import type { ExpandAction } from 'rc-tree/lib/Tree';
45
import type {
56
BaseSelectRef,
67
BaseSelectPropsWithoutPrivate,
@@ -152,6 +153,7 @@ export interface TreeSelectProps<
152153
treeExpandedKeys?: React.Key[];
153154
treeDefaultExpandedKeys?: React.Key[];
154155
onTreeExpand?: (expandedKeys: React.Key[]) => void;
156+
treeExpandAction: ExpandAction;
155157

156158
// >>> Options
157159
virtual?: boolean;
@@ -217,6 +219,7 @@ const TreeSelect = React.forwardRef<BaseSelectRef, TreeSelectProps>((props, ref)
217219
treeExpandedKeys,
218220
treeDefaultExpandedKeys,
219221
onTreeExpand,
222+
treeExpandAction,
220223

221224
// Options
222225
virtual,
@@ -647,6 +650,7 @@ const TreeSelect = React.forwardRef<BaseSelectRef, TreeSelectProps>((props, ref)
647650
treeData: filteredTreeData,
648651
fieldNames: mergedFieldNames,
649652
onSelect: onOptionSelect,
653+
treeExpandAction,
650654
}),
651655
[
652656
virtual,
@@ -656,6 +660,7 @@ const TreeSelect = React.forwardRef<BaseSelectRef, TreeSelectProps>((props, ref)
656660
filteredTreeData,
657661
mergedFieldNames,
658662
onOptionSelect,
663+
treeExpandAction,
659664
],
660665
);
661666

src/TreeSelectContext.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react';
2+
import type { ExpandAction } from 'rc-tree/lib/Tree';
23
import type { DefaultOptionType, InternalFieldName, OnInternalSelect } from './TreeSelect';
34

45
export interface TreeSelectContextProps {
@@ -9,6 +10,7 @@ export interface TreeSelectContextProps {
910
treeData: DefaultOptionType[];
1011
fieldNames: InternalFieldName;
1112
onSelect: OnInternalSelect;
13+
treeExpandAction: ExpandAction;
1214
}
1315

1416
const TreeSelectContext = React.createContext<TreeSelectContextProps>(null as any);

tests/Select.tree.spec.js

Lines changed: 147 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,25 @@ import { resetWarned } from 'rc-util/lib/warning';
55
import TreeSelect, { TreeNode as SelectNode } from '../src';
66

77
describe('TreeSelect.tree', () => {
8-
const createSelect = props => (
9-
<TreeSelect {...props}>
10-
<SelectNode key="0-0" value="0-0">
11-
<SelectNode key="0-0-0" value="0-0-0">
12-
<SelectNode key="0-0-0-0" value="0-0-0-0" />
13-
<SelectNode key="0-0-0-1" />
14-
invalid element
15-
</SelectNode>
16-
<SelectNode key="0-0-1" value="0-0-1">
17-
<SelectNode key="0-0-1-0" value="0-0-1-0" />
18-
<SelectNode key="0-0-1-1" value="0-0-1-1" />
8+
const createSelect = props => {
9+
const { selectable = true } = props;
10+
11+
return (
12+
<TreeSelect {...props}>
13+
<SelectNode key="0-0" value="0-0" title="0-0" selectable={selectable}>
14+
<SelectNode key="0-0-0" value="0-0-0" title="0-0-0" selectable={selectable}>
15+
<SelectNode key="0-0-0-0" value="0-0-0-0" title="0-0-0-0" selectable={selectable} />
16+
<SelectNode key="0-0-0-1" title="0-0-0-1" selectable={selectable} />
17+
invalid element
18+
</SelectNode>
19+
<SelectNode key="0-0-1" value="0-0-1" title="0-0-1" selectable={selectable}>
20+
<SelectNode key="0-0-1-0" value="0-0-1-0" title="0-0-1-0" selectable={selectable} />
21+
<SelectNode key="0-0-1-1" value="0-0-1-1" title="0-0-1-1" selectable={selectable} />
22+
</SelectNode>
1923
</SelectNode>
20-
</SelectNode>
21-
</TreeSelect>
22-
);
24+
</TreeSelect>
25+
);
26+
};
2327

2428
it('treeExpandedKeys', () => {
2529
class Test extends React.Component {
@@ -68,6 +72,135 @@ describe('TreeSelect.tree', () => {
6872
expect(wrapper.find('Tree').props().expandedKeys).toEqual([]);
6973
});
7074

75+
describe('treeExpandAction with selectable props', () => {
76+
it('title expandable when selectable is false and treeExpandAction is "click"', () => {
77+
const onSelect = jest.fn();
78+
79+
class Test extends React.Component {
80+
state = {
81+
treeExpandedKeys: [],
82+
};
83+
84+
onTreeExpand = treeExpandedKeys => {
85+
this.setState({
86+
treeExpandedKeys,
87+
});
88+
};
89+
90+
render() {
91+
const { treeExpandedKeys } = this.state;
92+
93+
return createSelect({
94+
open: true,
95+
treeExpandedKeys,
96+
onTreeExpand: this.onTreeExpand,
97+
selectable: false,
98+
treeExpandAction: 'click',
99+
onSelect,
100+
});
101+
}
102+
}
103+
104+
const wrapper = mount(<Test />);
105+
106+
wrapper.clickNodeTitle('0-0');
107+
expect(onSelect).not.toHaveBeenCalled();
108+
expect(wrapper.find('Tree').props().expandedKeys).toEqual(['0-0']);
109+
110+
onSelect.mockReset();
111+
112+
wrapper.clickNodeTitle('0-0-1');
113+
expect(onSelect).not.toHaveBeenCalled();
114+
expect(wrapper.find('Tree').props().expandedKeys).toEqual(['0-0', '0-0-1']);
115+
116+
onSelect.mockReset();
117+
wrapper.clickNodeTitle('0-0-1');
118+
expect(onSelect).not.toHaveBeenCalled();
119+
expect(wrapper.find('Tree').props().expandedKeys).toEqual(['0-0']);
120+
});
121+
122+
it('title expandable when selectable is false and treeExpandAction is "doubleClick"', () => {
123+
const onSelect = jest.fn();
124+
125+
class Test extends React.Component {
126+
state = {
127+
treeExpandedKeys: [],
128+
};
129+
130+
onTreeExpand = treeExpandedKeys => {
131+
this.setState({
132+
treeExpandedKeys,
133+
});
134+
};
135+
136+
render() {
137+
const { treeExpandedKeys } = this.state;
138+
139+
return createSelect({
140+
open: true,
141+
treeExpandedKeys,
142+
onTreeExpand: this.onTreeExpand,
143+
selectable: false,
144+
treeExpandAction: 'doubleClick',
145+
onSelect,
146+
});
147+
}
148+
}
149+
150+
const wrapper = mount(<Test />);
151+
152+
wrapper.doubleClickNodeTitle('0-0');
153+
expect(onSelect).not.toHaveBeenCalled();
154+
expect(wrapper.find('Tree').props().expandedKeys).toEqual(['0-0']);
155+
156+
onSelect.mockReset();
157+
158+
wrapper.doubleClickNodeTitle('0-0-1');
159+
expect(onSelect).not.toHaveBeenCalled();
160+
expect(wrapper.find('Tree').props().expandedKeys).toEqual(['0-0', '0-0-1']);
161+
162+
onSelect.mockReset();
163+
wrapper.doubleClickNodeTitle('0-0-1');
164+
expect(onSelect).not.toHaveBeenCalled();
165+
expect(wrapper.find('Tree').props().expandedKeys).toEqual(['0-0']);
166+
});
167+
168+
it('title un-expandable when selectable is false and treeExpandAction is false', () => {
169+
const onSelect = jest.fn();
170+
171+
class Test extends React.Component {
172+
state = {
173+
treeExpandedKeys: ['0-0'],
174+
};
175+
176+
onTreeExpand = treeExpandedKeys => {
177+
this.setState({
178+
treeExpandedKeys,
179+
});
180+
};
181+
182+
render() {
183+
const { treeExpandedKeys } = this.state;
184+
185+
return createSelect({
186+
open: true,
187+
treeExpandedKeys,
188+
onTreeExpand: this.onTreeExpand,
189+
selectable: false,
190+
treeExpandAction: false,
191+
onSelect,
192+
});
193+
}
194+
}
195+
196+
const wrapper = mount(<Test />);
197+
198+
wrapper.clickNodeTitle('0-0-1');
199+
expect(onSelect).not.toHaveBeenCalled();
200+
expect(wrapper.find('Tree').props().expandedKeys).toEqual(['0-0']);
201+
});
202+
});
203+
71204
it('warning if node key are not same as value', () => {
72205
resetWarned();
73206
const spy = jest.spyOn(console, 'error').mockImplementation(() => {});

tests/setup.js

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ Object.assign(Enzyme.ReactWrapper.prototype, {
1414
this.find('.rc-tree-select-selector').simulate('mousedown');
1515
},
1616
selectNode(index = 0) {
17-
this.find('.rc-tree-select-tree-node-content-wrapper')
18-
.at(index)
19-
.simulate('click');
17+
this.find('.rc-tree-select-tree-node-content-wrapper').at(index).simulate('click');
2018
},
2119
switchNode(index = 0) {
22-
this.find('.rc-tree-select-tree-switcher')
23-
.at(index)
24-
.simulate('click');
20+
this.find('.rc-tree-select-tree-switcher').at(index).simulate('click');
21+
},
22+
clickNodeTitle(title) {
23+
this.find(`[title="${title}"]`).hostNodes().simulate('click');
24+
},
25+
doubleClickNodeTitle(title) {
26+
this.find(`[title="${title}"]`).hostNodes().simulate('doubleClick');
2527
},
2628
getSelection(index) {
2729
const selections = this.find('.rc-tree-select-selection-item');
@@ -41,17 +43,12 @@ Object.assign(Enzyme.ReactWrapper.prototype, {
4143
.simulate('click');
4244
},
4345
clearAll() {
44-
return this.find('.rc-tree-select-clear')
45-
.first()
46-
.simulate('mouseDown');
46+
return this.find('.rc-tree-select-clear').first().simulate('mouseDown');
4747
},
4848
search(text) {
49-
this.find('input.rc-tree-select-selection-search-input').simulate(
50-
'change',
51-
{
52-
target: { value: text },
53-
},
54-
);
49+
this.find('input.rc-tree-select-selection-search-input').simulate('change', {
50+
target: { value: text },
51+
});
5552
},
5653
isOpen() {
5754
return this.find('.rc-tree-select').hasClass('rc-tree-select-open');

0 commit comments

Comments
 (0)