diff --git a/README.md b/README.md index a8dae913..71313b88 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ online example: https://tree-select-react-component.vercel.app/ |treeDefaultExpandAll | default expand all treeNode | bool | false | |treeDefaultExpandedKeys | default expanded treeNode keys | Array | - | |treeExpandedKeys | set tree expanded keys | Array | - | +|treeExpandAction | Tree open logic, optional: false \| `click` \| `doubleClick`, same as `expandAction` of `rc-tree` | string \| boolean | `click` | |treeCheckable | whether tree show checkbox (select callback will not fire) | bool | false | |treeCheckStrictly | check node precisely, parent and children nodes are not associated| bool | false | |filterTreeNode | whether filter treeNodes by input value. default filter by treeNode's treeNodeFilterProp prop's value | bool/Function(inputValue:string, treeNode:TreeNode) | Function | diff --git a/package.json b/package.json index b29866b8..ece7d37f 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@babel/runtime": "^7.10.1", "classnames": "2.x", "rc-select": "~14.1.0", - "rc-tree": "~5.5.0", + "rc-tree": "~5.6.1", "rc-util": "^5.16.1" } } diff --git a/src/OptionList.tsx b/src/OptionList.tsx index 9f74f90a..f0cb4121 100644 --- a/src/OptionList.tsx +++ b/src/OptionList.tsx @@ -41,6 +41,7 @@ const OptionList: React.RefForwardingComponent = (_, r fieldNames, onSelect, dropdownMatchSelectWidth, + treeExpandAction, } = React.useContext(TreeSelectContext); const { @@ -244,6 +245,7 @@ const OptionList: React.RefForwardingComponent = (_, r onExpand={onInternalExpand} onLoad={onTreeLoad} filterTreeNode={filterTreeNode} + expandAction={treeExpandAction} /> ); diff --git a/src/TreeSelect.tsx b/src/TreeSelect.tsx index 48d2a689..4600481d 100644 --- a/src/TreeSelect.tsx +++ b/src/TreeSelect.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { BaseSelect } from 'rc-select'; import type { IconType } from 'rc-tree/lib/interface'; +import type { ExpandAction } from 'rc-tree/lib/Tree'; import type { BaseSelectRef, BaseSelectPropsWithoutPrivate, @@ -152,6 +153,7 @@ export interface TreeSelectProps< treeExpandedKeys?: React.Key[]; treeDefaultExpandedKeys?: React.Key[]; onTreeExpand?: (expandedKeys: React.Key[]) => void; + treeExpandAction?: ExpandAction; // >>> Options virtual?: boolean; @@ -217,6 +219,7 @@ const TreeSelect = React.forwardRef((props, ref) treeExpandedKeys, treeDefaultExpandedKeys, onTreeExpand, + treeExpandAction, // Options virtual, @@ -647,6 +650,7 @@ const TreeSelect = React.forwardRef((props, ref) treeData: filteredTreeData, fieldNames: mergedFieldNames, onSelect: onOptionSelect, + treeExpandAction, }), [ virtual, @@ -656,6 +660,7 @@ const TreeSelect = React.forwardRef((props, ref) filteredTreeData, mergedFieldNames, onOptionSelect, + treeExpandAction, ], ); diff --git a/src/TreeSelectContext.ts b/src/TreeSelectContext.ts index 2875fe08..0cb40e0f 100644 --- a/src/TreeSelectContext.ts +++ b/src/TreeSelectContext.ts @@ -1,4 +1,5 @@ import * as React from 'react'; +import type { ExpandAction } from 'rc-tree/lib/Tree'; import type { DefaultOptionType, InternalFieldName, OnInternalSelect } from './TreeSelect'; export interface TreeSelectContextProps { @@ -9,6 +10,7 @@ export interface TreeSelectContextProps { treeData: DefaultOptionType[]; fieldNames: InternalFieldName; onSelect: OnInternalSelect; + treeExpandAction?: ExpandAction; } const TreeSelectContext = React.createContext(null as any); diff --git a/tests/Select.tree.treeExpandAction.spec.js b/tests/Select.tree.treeExpandAction.spec.js new file mode 100644 index 00000000..d269a713 --- /dev/null +++ b/tests/Select.tree.treeExpandAction.spec.js @@ -0,0 +1,144 @@ +/* eslint-disable no-undef, react/no-multi-comp, no-console */ +import React from 'react'; +import { mount } from 'enzyme'; +import TreeSelect from '../src'; + +describe('treeExpandAction with selectable props', () => { + const treeData = [ + { + title: '0-0', + value: '0-0', + selectable: false, + children: [ + { + title: '0-0-0', + value: '0-0-0', + selectable: false, + children: [ + { + title: '0-0-0-0', + value: '0-0-0-0', + selectable: false, + }, + { + title: '0-0-0-0', + value: '0-0-0-1', + selectable: false, + }, + ], + }, + { + title: '0-0-1', + value: '0-0-1', + selectable: false, + children: [ + { + title: '0-0-1-0', + value: '0-0-1-0', + selectable: false, + }, + { + title: '0-0-1-1', + value: '0-0-1-1', + selectable: false, + }, + ], + }, + ], + }, + ]; + + const clickNodeTitle = (wrapper, title) => { + wrapper.find(`[title="${title}"]`).hostNodes().simulate('click'); + }; + + const doubleClickNodeTitle = (wrapper, title) => { + wrapper.find(`[title="${title}"]`).hostNodes().simulate('doubleClick'); + }; + + it('title expandable when selectable is false and treeExpandAction is "click"', () => { + const onSelect = jest.fn(); + const onTreeExpand = jest.fn(); + + const wrapper = mount( + , + ); + + clickNodeTitle(wrapper, '0-0'); + expect(onSelect).not.toHaveBeenCalled(); + expect(onTreeExpand).toHaveBeenCalledWith(['0-0']); + + onSelect.mockReset(); + onTreeExpand.mockReset(); + + clickNodeTitle(wrapper, '0-0-1'); + expect(onSelect).not.toHaveBeenCalled(); + expect(onTreeExpand).toHaveBeenCalledWith(['0-0', '0-0-1']); + + onSelect.mockReset(); + onTreeExpand.mockReset(); + + clickNodeTitle(wrapper, '0-0-1'); + expect(onSelect).not.toHaveBeenCalled(); + expect(onTreeExpand).toHaveBeenCalledWith(['0-0']); + }); + + it('title expandable when selectable is false and treeExpandAction is "doubleClick"', () => { + const onSelect = jest.fn(); + const onTreeExpand = jest.fn(); + + const wrapper = mount( + , + ); + + doubleClickNodeTitle(wrapper, '0-0'); + expect(onSelect).not.toHaveBeenCalled(); + expect(onTreeExpand).toHaveBeenCalledWith(['0-0']); + + onSelect.mockReset(); + onTreeExpand.mockReset(); + + doubleClickNodeTitle(wrapper, '0-0-1'); + expect(onSelect).not.toHaveBeenCalled(); + expect(onTreeExpand).toHaveBeenCalledWith(['0-0', '0-0-1']); + + onSelect.mockReset(); + onTreeExpand.mockReset(); + + doubleClickNodeTitle(wrapper, '0-0-1'); + expect(onSelect).not.toHaveBeenCalled(); + expect(onTreeExpand).toHaveBeenCalledWith(['0-0']); + }); + + it('title un-expandable when selectable is false and treeExpandAction is false', () => { + const onSelect = jest.fn(); + const onTreeExpand = jest.fn(); + + const wrapper = mount( + , + ); + + clickNodeTitle(wrapper, '0-0-1'); + expect(onSelect).not.toHaveBeenCalled(); + expect(onTreeExpand).not.toHaveBeenCalled(); + }); +}); diff --git a/tests/setup.js b/tests/setup.js index 1523942a..6084fb66 100644 --- a/tests/setup.js +++ b/tests/setup.js @@ -14,14 +14,10 @@ Object.assign(Enzyme.ReactWrapper.prototype, { this.find('.rc-tree-select-selector').simulate('mousedown'); }, selectNode(index = 0) { - this.find('.rc-tree-select-tree-node-content-wrapper') - .at(index) - .simulate('click'); + this.find('.rc-tree-select-tree-node-content-wrapper').at(index).simulate('click'); }, switchNode(index = 0) { - this.find('.rc-tree-select-tree-switcher') - .at(index) - .simulate('click'); + this.find('.rc-tree-select-tree-switcher').at(index).simulate('click'); }, getSelection(index) { const selections = this.find('.rc-tree-select-selection-item'); @@ -41,17 +37,12 @@ Object.assign(Enzyme.ReactWrapper.prototype, { .simulate('click'); }, clearAll() { - return this.find('.rc-tree-select-clear') - .first() - .simulate('mouseDown'); + return this.find('.rc-tree-select-clear').first().simulate('mouseDown'); }, search(text) { - this.find('input.rc-tree-select-selection-search-input').simulate( - 'change', - { - target: { value: text }, - }, - ); + this.find('input.rc-tree-select-selection-search-input').simulate('change', { + target: { value: text }, + }); }, isOpen() { return this.find('.rc-tree-select').hasClass('rc-tree-select-open');