@douyinfe/semi-ui 2.6.0 → 2.7.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/button/__test__/button.test.js +7 -0
- package/button/buttonGroup.tsx +5 -2
- package/cascader/__test__/cascader.test.js +159 -81
- package/cascader/_story/cascader.stories.js +36 -23
- package/cascader/index.tsx +26 -5
- package/datePicker/_story/v2/InsetInput.jsx +104 -0
- package/datePicker/_story/v2/InsetInputE2E.jsx +69 -0
- package/datePicker/_story/v2/index.js +2 -0
- package/datePicker/dateInput.tsx +95 -9
- package/datePicker/datePicker.tsx +89 -15
- package/datePicker/index.tsx +15 -0
- package/datePicker/insetInput.tsx +76 -0
- package/datePicker/monthsGrid.tsx +14 -7
- package/dist/css/semi.css +104 -2
- package/dist/css/semi.min.css +1 -1
- package/dist/umd/semi-ui.js +902 -147
- package/dist/umd/semi-ui.js.map +1 -1
- package/dist/umd/semi-ui.min.js +1 -1
- package/dist/umd/semi-ui.min.js.map +1 -1
- package/input/_story/input.stories.js +13 -0
- package/lib/cjs/button/buttonGroup.d.ts +1 -0
- package/lib/cjs/button/buttonGroup.js +6 -2
- package/lib/cjs/cascader/index.d.ts +1 -0
- package/lib/cjs/cascader/index.js +38 -9
- package/lib/cjs/datePicker/dateInput.d.ts +9 -2
- package/lib/cjs/datePicker/dateInput.js +92 -9
- package/lib/cjs/datePicker/datePicker.d.ts +7 -2
- package/lib/cjs/datePicker/datePicker.js +123 -18
- package/lib/cjs/datePicker/index.js +24 -2
- package/lib/cjs/datePicker/insetInput.d.ts +21 -0
- package/lib/cjs/datePicker/insetInput.js +80 -0
- package/lib/cjs/datePicker/monthsGrid.js +19 -7
- package/lib/cjs/tree/index.js +5 -3
- package/lib/cjs/tree/interface.d.ts +1 -0
- package/lib/cjs/tree/nodeList.js +2 -1
- package/lib/cjs/treeSelect/index.js +7 -3
- package/lib/es/button/buttonGroup.d.ts +1 -0
- package/lib/es/button/buttonGroup.js +5 -2
- package/lib/es/cascader/index.d.ts +1 -0
- package/lib/es/cascader/index.js +35 -6
- package/lib/es/datePicker/dateInput.d.ts +9 -2
- package/lib/es/datePicker/dateInput.js +91 -9
- package/lib/es/datePicker/datePicker.d.ts +7 -2
- package/lib/es/datePicker/datePicker.js +124 -18
- package/lib/es/datePicker/index.js +20 -0
- package/lib/es/datePicker/insetInput.d.ts +21 -0
- package/lib/es/datePicker/insetInput.js +65 -0
- package/lib/es/datePicker/monthsGrid.js +19 -7
- package/lib/es/tree/index.js +5 -3
- package/lib/es/tree/interface.d.ts +1 -0
- package/lib/es/tree/nodeList.js +2 -1
- package/lib/es/treeSelect/index.js +7 -3
- package/package.json +9 -9
- package/tree/__test__/tree.test.js +87 -2
- package/tree/_story/tree.stories.js +65 -5
- package/tree/index.tsx +4 -2
- package/tree/interface.ts +1 -0
- package/tree/nodeList.tsx +2 -2
- package/treeSelect/__test__/treeSelect.test.js +28 -0
- package/treeSelect/_story/treeSelect.stories.js +55 -2
- package/treeSelect/index.tsx +11 -3
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import _get from "lodash/get";
|
|
2
|
+
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import Input from '../input';
|
|
5
|
+
export function InsetDateInput(props) {
|
|
6
|
+
const {
|
|
7
|
+
insetInputValue,
|
|
8
|
+
valuePath,
|
|
9
|
+
onFocus,
|
|
10
|
+
onChange,
|
|
11
|
+
placeholder,
|
|
12
|
+
forwardRef
|
|
13
|
+
} = props;
|
|
14
|
+
|
|
15
|
+
const value = _get(insetInputValue, valuePath);
|
|
16
|
+
|
|
17
|
+
return /*#__PURE__*/React.createElement(Input, {
|
|
18
|
+
value: value,
|
|
19
|
+
onChange: (value, event) => {
|
|
20
|
+
onChange({
|
|
21
|
+
value,
|
|
22
|
+
event,
|
|
23
|
+
insetInputValue,
|
|
24
|
+
valuePath
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
onFocus: onFocus,
|
|
28
|
+
placeholder: placeholder,
|
|
29
|
+
ref: forwardRef
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function InsetTimeInput(props) {
|
|
33
|
+
const {
|
|
34
|
+
insetInputValue,
|
|
35
|
+
valuePath,
|
|
36
|
+
type,
|
|
37
|
+
onFocus,
|
|
38
|
+
onChange,
|
|
39
|
+
placeholder,
|
|
40
|
+
disabled
|
|
41
|
+
} = props;
|
|
42
|
+
|
|
43
|
+
const _isTimeType = _includesInstanceProperty(type).call(type, 'Time');
|
|
44
|
+
|
|
45
|
+
if (!_isTimeType) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const value = _get(insetInputValue, valuePath);
|
|
50
|
+
|
|
51
|
+
return /*#__PURE__*/React.createElement(Input, {
|
|
52
|
+
value: value,
|
|
53
|
+
onChange: (value, event) => {
|
|
54
|
+
onChange({
|
|
55
|
+
value,
|
|
56
|
+
event,
|
|
57
|
+
insetInputValue,
|
|
58
|
+
valuePath
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
onFocus: onFocus,
|
|
62
|
+
placeholder: placeholder,
|
|
63
|
+
disabled: disabled
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -9,6 +9,8 @@ import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/ins
|
|
|
9
9
|
|
|
10
10
|
/* eslint-disable jsx-a11y/interactive-supports-focus,jsx-a11y/click-events-have-key-events */
|
|
11
11
|
|
|
12
|
+
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
|
13
|
+
|
|
12
14
|
/* eslint-disable react/no-did-update-set-state */
|
|
13
15
|
|
|
14
16
|
/* eslint-disable max-len */
|
|
@@ -284,6 +286,9 @@ export default class MonthsGrid extends BaseComponent {
|
|
|
284
286
|
monthRight,
|
|
285
287
|
currentPanelHeight
|
|
286
288
|
} = this.state;
|
|
289
|
+
const {
|
|
290
|
+
insetInput
|
|
291
|
+
} = this.props;
|
|
287
292
|
const panelDetail = panelType === strings.PANEL_TYPE_RIGHT ? monthRight : monthLeft;
|
|
288
293
|
const {
|
|
289
294
|
isTimePickerOpen,
|
|
@@ -306,7 +311,7 @@ export default class MonthsGrid extends BaseComponent {
|
|
|
306
311
|
style.minWidth = wrap.getBoundingClientRect().width;
|
|
307
312
|
}
|
|
308
313
|
|
|
309
|
-
if (this.leftIsYearOrTime() && this.rightIsYearOrTime()) {
|
|
314
|
+
if (this.leftIsYearOrTime() && this.rightIsYearOrTime() && !insetInput) {
|
|
310
315
|
/**
|
|
311
316
|
* left和right同时为tpk时,panel会有一个minHeight
|
|
312
317
|
* 如果缓存的currentPanelHeight为0,则需要计算滚动列表的高度
|
|
@@ -316,7 +321,7 @@ export default class MonthsGrid extends BaseComponent {
|
|
|
316
321
|
* When left and right are tpk at the same time, the panel will have a minHeight
|
|
317
322
|
* If the cached currentPanelHeight is 0, you need to calculate the height of the scrolling list
|
|
318
323
|
* If there is a cached value, use currentPanelHeight (if this height is less than the actual value, it will affect the number of cycles in the ScrollList to render the list)
|
|
319
|
-
* See packages/semi-foundation/scrollList/
|
|
324
|
+
* See packages/semi-foundation/scrollList/itemFoundation.js initWheelList function
|
|
320
325
|
*/
|
|
321
326
|
style.minHeight = currentPanelHeight ? currentPanelHeight : this.calcScrollListHeight();
|
|
322
327
|
}
|
|
@@ -324,10 +329,14 @@ export default class MonthsGrid extends BaseComponent {
|
|
|
324
329
|
monthCls = classnames(monthCls, "".concat(prefixCls, "-yam-showing"));
|
|
325
330
|
}
|
|
326
331
|
|
|
332
|
+
const _isDatePanelOpen = !(isYearPickerOpen || isTimePickerOpen);
|
|
333
|
+
|
|
334
|
+
const xOpenType = _isDatePanelOpen ? 'date' : isYearPickerOpen ? 'year' : 'time';
|
|
327
335
|
return /*#__PURE__*/React.createElement("div", {
|
|
328
336
|
className: monthCls,
|
|
329
337
|
key: panelType,
|
|
330
|
-
style: style
|
|
338
|
+
style: style,
|
|
339
|
+
"x-open-type": xOpenType
|
|
331
340
|
}, yearAndMonthLayer, timePickerLayer, this.foundation.isRangeType() ? panelContent : isYearPickerOpen || isTimePickerOpen ? null : panelContent, this.renderSwitch(panelType));
|
|
332
341
|
}
|
|
333
342
|
|
|
@@ -538,10 +547,11 @@ export default class MonthsGrid extends BaseComponent {
|
|
|
538
547
|
locale,
|
|
539
548
|
disabledTimePicker,
|
|
540
549
|
density,
|
|
541
|
-
dateFnsLocale
|
|
542
|
-
|
|
550
|
+
dateFnsLocale,
|
|
551
|
+
insetInput
|
|
552
|
+
} = this.props; // Type: date, dateRange, year, month, inset input no rendering required
|
|
543
553
|
|
|
544
|
-
if (!_includesInstanceProperty(type).call(type, 'Time')) {
|
|
554
|
+
if (!_includesInstanceProperty(type).call(type, 'Time') || insetInput) {
|
|
545
555
|
return null;
|
|
546
556
|
} // switch year/month & time
|
|
547
557
|
|
|
@@ -614,7 +624,8 @@ export default class MonthsGrid extends BaseComponent {
|
|
|
614
624
|
monthRight
|
|
615
625
|
} = this.state;
|
|
616
626
|
const {
|
|
617
|
-
type
|
|
627
|
+
type,
|
|
628
|
+
insetInput
|
|
618
629
|
} = this.props;
|
|
619
630
|
const monthGridCls = classnames({
|
|
620
631
|
["".concat(prefixCls, "-month-grid")]: true
|
|
@@ -636,6 +647,7 @@ export default class MonthsGrid extends BaseComponent {
|
|
|
636
647
|
className: monthGridCls,
|
|
637
648
|
"x-type": type,
|
|
638
649
|
"x-panel-yearandmonth-open-type": yearOpenType,
|
|
650
|
+
"x-insetInput": insetInput ? "true" : "false",
|
|
639
651
|
ref: current => this.cacheRefCurrent('monthGrid', current)
|
|
640
652
|
}, content);
|
|
641
653
|
}
|
package/lib/es/tree/index.js
CHANGED
|
@@ -216,7 +216,8 @@ class Tree extends BaseComponent {
|
|
|
216
216
|
const isSeaching = Boolean(props.filterTreeNode && prevState.inputValue && prevState.inputValue.length);
|
|
217
217
|
const newState = {
|
|
218
218
|
prevProps: props
|
|
219
|
-
};
|
|
219
|
+
};
|
|
220
|
+
const isExpandControlled = ('expandedKeys' in props); // Accept a props field as a parameter to determine whether to update the field
|
|
220
221
|
|
|
221
222
|
const needUpdate = name => {
|
|
222
223
|
const firstInProps = !prevProps && name in props;
|
|
@@ -260,7 +261,8 @@ class Tree extends BaseComponent {
|
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
|
|
263
|
-
const
|
|
264
|
+
const dataUpdated = needUpdate('treeDataSimpleJson') || needUpdate('treeData');
|
|
265
|
+
const expandAllWhenDataChange = dataUpdated && props.expandAll;
|
|
264
266
|
|
|
265
267
|
if (!isSeaching) {
|
|
266
268
|
// Update expandedKeys
|
|
@@ -286,7 +288,7 @@ class Tree extends BaseComponent {
|
|
|
286
288
|
newState.expandedKeys = calcExpandedKeys(props.defaultExpandedKeys, keyEntities);
|
|
287
289
|
} else if (!prevProps && props.defaultValue) {
|
|
288
290
|
newState.expandedKeys = calcExpandedKeysForValues(props.defaultValue, keyEntities, props.multiple, valueEntities);
|
|
289
|
-
} else if (!prevProps && props.value) {
|
|
291
|
+
} else if ((!prevProps || !isExpandControlled && dataUpdated) && props.value) {
|
|
290
292
|
newState.expandedKeys = calcExpandedKeysForValues(props.value, keyEntities, props.multiple, valueEntities);
|
|
291
293
|
}
|
|
292
294
|
|
|
@@ -109,6 +109,7 @@ export interface NodeListProps {
|
|
|
109
109
|
motionKeys: Set<string>;
|
|
110
110
|
motionType: string;
|
|
111
111
|
flattenList: FlattenNode[] | undefined;
|
|
112
|
+
searchTargetIsDeep?: boolean;
|
|
112
113
|
renderTreeNode: (treeNode: FlattenNode, ind?: number, style?: React.CSSProperties) => ReactNode;
|
|
113
114
|
}
|
|
114
115
|
export declare type TransitionNodes<T> = Array<T | Array<T>>;
|
package/lib/es/tree/nodeList.js
CHANGED
|
@@ -83,12 +83,13 @@ export default class NodeList extends PureComponent {
|
|
|
83
83
|
const {
|
|
84
84
|
flattenNodes,
|
|
85
85
|
motionType,
|
|
86
|
+
searchTargetIsDeep,
|
|
86
87
|
renderTreeNode
|
|
87
88
|
} = this.props;
|
|
88
89
|
const {
|
|
89
90
|
transitionNodes
|
|
90
91
|
} = this.state;
|
|
91
|
-
const mapData = transitionNodes.length ? transitionNodes : flattenNodes;
|
|
92
|
+
const mapData = transitionNodes.length && !searchTargetIsDeep ? transitionNodes : flattenNodes;
|
|
92
93
|
|
|
93
94
|
const options = _mapInstanceProperty(mapData).call(mapData, treeNode => {
|
|
94
95
|
const isMotionNode = _Array$isArray(treeNode);
|
|
@@ -741,7 +741,8 @@ class TreeSelect extends BaseComponent {
|
|
|
741
741
|
const {
|
|
742
742
|
flattenNodes,
|
|
743
743
|
motionKeys,
|
|
744
|
-
motionType
|
|
744
|
+
motionType,
|
|
745
|
+
filteredKeys
|
|
745
746
|
} = this.state;
|
|
746
747
|
const {
|
|
747
748
|
direction
|
|
@@ -750,6 +751,7 @@ class TreeSelect extends BaseComponent {
|
|
|
750
751
|
virtualize,
|
|
751
752
|
motionExpand
|
|
752
753
|
} = this.props;
|
|
754
|
+
const isExpandControlled = ('expandedKeys' in this.props);
|
|
753
755
|
|
|
754
756
|
if (!virtualize || _isEmpty(virtualize)) {
|
|
755
757
|
return /*#__PURE__*/React.createElement(NodeList, {
|
|
@@ -757,6 +759,8 @@ class TreeSelect extends BaseComponent {
|
|
|
757
759
|
flattenList: this._flattenNodes,
|
|
758
760
|
motionKeys: motionExpand ? motionKeys : new _Set([]),
|
|
759
761
|
motionType: motionType,
|
|
762
|
+
// When motionKeys is empty, but filteredKeys is not empty (that is, the search hits), this situation should be distinguished from ordinary motionKeys
|
|
763
|
+
searchTargetIsDeep: isExpandControlled && motionExpand && _isEmpty(motionKeys) && !_isEmpty(filteredKeys),
|
|
760
764
|
onMotionEnd: this.onMotionEnd,
|
|
761
765
|
renderTreeNode: this.renderTreeNode
|
|
762
766
|
});
|
|
@@ -1076,8 +1080,8 @@ class TreeSelect extends BaseComponent {
|
|
|
1076
1080
|
notifySelect: (selectKey, bool, node) => {
|
|
1077
1081
|
this.props.onSelect && this.props.onSelect(selectKey, bool, node);
|
|
1078
1082
|
},
|
|
1079
|
-
notifySearch: input => {
|
|
1080
|
-
this.props.onSearch && this.props.onSearch(input);
|
|
1083
|
+
notifySearch: (input, filteredExpandedKeys) => {
|
|
1084
|
+
this.props.onSearch && this.props.onSearch(input, filteredExpandedKeys);
|
|
1081
1085
|
},
|
|
1082
1086
|
cacheFlattenNodes: bool => {
|
|
1083
1087
|
this._flattenNodes = bool ? cloneDeep(this.state.flattenNodes) : null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@douyinfe/semi-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0-beta.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"module": "lib/es/index.js",
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@babel/runtime-corejs3": "^7.15.4",
|
|
17
|
-
"@douyinfe/semi-animation": "2.
|
|
18
|
-
"@douyinfe/semi-animation-react": "2.
|
|
19
|
-
"@douyinfe/semi-foundation": "2.
|
|
20
|
-
"@douyinfe/semi-icons": "2.
|
|
21
|
-
"@douyinfe/semi-illustrations": "2.
|
|
22
|
-
"@douyinfe/semi-theme-default": "2.
|
|
17
|
+
"@douyinfe/semi-animation": "2.7.0-beta.0",
|
|
18
|
+
"@douyinfe/semi-animation-react": "2.7.0-beta.0",
|
|
19
|
+
"@douyinfe/semi-foundation": "2.7.0-beta.0",
|
|
20
|
+
"@douyinfe/semi-icons": "2.7.0-beta.0",
|
|
21
|
+
"@douyinfe/semi-illustrations": "2.7.0-beta.0",
|
|
22
|
+
"@douyinfe/semi-theme-default": "2.7.0-beta.0",
|
|
23
23
|
"@types/react-window": "^1.8.2",
|
|
24
24
|
"async-validator": "^3.5.0",
|
|
25
25
|
"classnames": "^2.2.6",
|
|
@@ -69,13 +69,13 @@
|
|
|
69
69
|
],
|
|
70
70
|
"author": "",
|
|
71
71
|
"license": "MIT",
|
|
72
|
-
"gitHead": "
|
|
72
|
+
"gitHead": "51cc4f6fa52f3275728d2112a98446ba54619c5b",
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@babel/plugin-proposal-decorators": "^7.15.8",
|
|
75
75
|
"@babel/plugin-transform-runtime": "^7.15.8",
|
|
76
76
|
"@babel/preset-env": "^7.15.8",
|
|
77
77
|
"@babel/preset-react": "^7.14.5",
|
|
78
|
-
"@douyinfe/semi-scss-compile": "2.
|
|
78
|
+
"@douyinfe/semi-scss-compile": "2.7.0-beta.0",
|
|
79
79
|
"@storybook/addon-knobs": "^6.3.1",
|
|
80
80
|
"@types/lodash": "^4.14.176",
|
|
81
81
|
"babel-loader": "^8.2.2",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BASE_CLASS_PREFIX } from '../../../semi-foundation/base/constants';
|
|
1
|
+
import { isEqual } from 'lodash';
|
|
3
2
|
import { IconMapPin } from '@douyinfe/semi-icons';
|
|
3
|
+
import { BASE_CLASS_PREFIX } from '../../../semi-foundation/base/constants';
|
|
4
|
+
import { Tree, Button } from '../../index';
|
|
4
5
|
|
|
5
6
|
const treeChildren = [
|
|
6
7
|
{
|
|
@@ -672,6 +673,90 @@ describe('Tree', () => {
|
|
|
672
673
|
expect(tree.state().selectedKeys.length).toEqual(0);
|
|
673
674
|
});
|
|
674
675
|
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Detect whether the expanded item will be expanded according to the value when the value
|
|
679
|
+
* or treeData is changed, when expandedKeys is not controlled
|
|
680
|
+
*/
|
|
681
|
+
const treeJsonData1 = {
|
|
682
|
+
"Node0": {
|
|
683
|
+
"Child Node0-0": '0-0',
|
|
684
|
+
"Child Node0-1": '0-1',
|
|
685
|
+
},
|
|
686
|
+
"Node1": {
|
|
687
|
+
"Child Node1-0": '1-0',
|
|
688
|
+
"Child Node1-1": '1-1',
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
const treeJsonData2 = {
|
|
692
|
+
"Updated Node0": {
|
|
693
|
+
"Updated Child Node0-0": {
|
|
694
|
+
'Updated Child Node0-0-0':'0-0'
|
|
695
|
+
},
|
|
696
|
+
"Updated Child Node0-1": '0-1',
|
|
697
|
+
},
|
|
698
|
+
"Updated Node1": {
|
|
699
|
+
"Updated Child Node1-0": '1-0',
|
|
700
|
+
"Updated Child Node1-1": '1-1',
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
it('expandedKeys when treeDataSimpleJson update', () => {
|
|
705
|
+
const tree = mount(
|
|
706
|
+
<Tree
|
|
707
|
+
value='0-0'
|
|
708
|
+
multiple
|
|
709
|
+
treeDataSimpleJson={treeJsonData1}
|
|
710
|
+
/>,
|
|
711
|
+
{
|
|
712
|
+
attachTo: document.getElementById('container')
|
|
713
|
+
}
|
|
714
|
+
);
|
|
715
|
+
const treeDataButton = mount(
|
|
716
|
+
<Button
|
|
717
|
+
onClick={() => {
|
|
718
|
+
if (isEqual(tree.props().treeDataSimpleJson, treeJsonData1)) {
|
|
719
|
+
tree.setProps({ treeDataSimpleJson: treeJsonData2 });
|
|
720
|
+
tree.update();
|
|
721
|
+
} else {
|
|
722
|
+
tree.setProps({ treeDataSimpleJson: treeJsonData1 });
|
|
723
|
+
tree.update();
|
|
724
|
+
}
|
|
725
|
+
}}
|
|
726
|
+
>
|
|
727
|
+
update treeData
|
|
728
|
+
</Button>
|
|
729
|
+
);
|
|
730
|
+
expect(
|
|
731
|
+
tree
|
|
732
|
+
.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-1`)
|
|
733
|
+
.at(0)
|
|
734
|
+
.hasClass(`${BASE_CLASS_PREFIX}-tree-option-collapsed`)
|
|
735
|
+
).toEqual(false);
|
|
736
|
+
expect(
|
|
737
|
+
tree
|
|
738
|
+
.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`)
|
|
739
|
+
.at(0)
|
|
740
|
+
.hasClass(`${BASE_CLASS_PREFIX}-tree-option-collapsed`)
|
|
741
|
+
).toEqual(true);
|
|
742
|
+
expect(
|
|
743
|
+
tree
|
|
744
|
+
.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-1`)
|
|
745
|
+
.at(1)
|
|
746
|
+
.hasClass(`${BASE_CLASS_PREFIX}-tree-option-collapsed`)
|
|
747
|
+
).toEqual(true);
|
|
748
|
+
treeDataButton.simulate('click');
|
|
749
|
+
expect(
|
|
750
|
+
tree
|
|
751
|
+
.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`)
|
|
752
|
+
.at(0)
|
|
753
|
+
.hasClass(`${BASE_CLASS_PREFIX}-tree-option-collapsed`)
|
|
754
|
+
).toEqual(false);
|
|
755
|
+
treeDataButton.simulate('click');
|
|
756
|
+
tree.unmount();
|
|
757
|
+
treeDataButton.unmount();
|
|
758
|
+
});
|
|
759
|
+
|
|
675
760
|
it('expandAction = false / default behavior', () => {
|
|
676
761
|
const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => { } } }
|
|
677
762
|
let spyOnExpand = sinon.spy(() => { });
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import React, { useRef, useState } from 'react';
|
|
2
|
+
import { cloneDeep, difference, isEqual } from 'lodash';
|
|
3
|
+
import { IconEdit, IconMapPin, IconMore } from '@douyinfe/semi-icons';
|
|
2
4
|
import Tree from '../index';
|
|
3
5
|
import AutoSizer from '../autoSizer';
|
|
4
|
-
import { Button, ButtonGroup, Input, Popover, Toast } from '../../index';
|
|
6
|
+
import { Button, ButtonGroup, Input, Popover, Toast, Space } from '../../index';
|
|
5
7
|
import BigTree from './BigData';
|
|
6
8
|
import testData from './data';
|
|
7
|
-
import { cloneDeep, difference, isEqual } from 'lodash';
|
|
8
|
-
import { IconEdit, IconMapPin, IconMore } from '@douyinfe/semi-icons';
|
|
9
|
-
|
|
10
9
|
const TreeNode = Tree.TreeNode;
|
|
11
10
|
|
|
12
11
|
export default {
|
|
@@ -2338,4 +2337,65 @@ export const CheckRelationDemo = () => {
|
|
|
2338
2337
|
/>
|
|
2339
2338
|
</>
|
|
2340
2339
|
);
|
|
2341
|
-
};
|
|
2340
|
+
};
|
|
2341
|
+
|
|
2342
|
+
export const ValueImpactExpansionWithDynamicTreeData = () => {
|
|
2343
|
+
const json = {
|
|
2344
|
+
"Node0": {
|
|
2345
|
+
"Child Node0-0": '0-0',
|
|
2346
|
+
"Child Node0-1": '0-1',
|
|
2347
|
+
},
|
|
2348
|
+
"Node1": {
|
|
2349
|
+
"Child Node1-0": '1-0',
|
|
2350
|
+
"Child Node1-1": '1-1',
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
const json2 = {
|
|
2354
|
+
"Updated Node0": {
|
|
2355
|
+
"Updated Child Node0-0": {
|
|
2356
|
+
'Updated Child Node0-0-0':'0-0'
|
|
2357
|
+
},
|
|
2358
|
+
"Updated Child Node0-1": '0-1',
|
|
2359
|
+
},
|
|
2360
|
+
"Updated Node1": {
|
|
2361
|
+
"Updated Child Node1-0": '1-0',
|
|
2362
|
+
"Updated Child Node1-1": '1-1',
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
const style = {
|
|
2366
|
+
width: 260,
|
|
2367
|
+
height: 420,
|
|
2368
|
+
border: '1px solid var(--color-border)'
|
|
2369
|
+
}
|
|
2370
|
+
const [value, setValue] = useState('0-0')
|
|
2371
|
+
const [tree, setTree] = useState(json);
|
|
2372
|
+
const handleValueButtonClick = () => {
|
|
2373
|
+
if (value === '0-0') {
|
|
2374
|
+
setValue('1-0');
|
|
2375
|
+
} else {
|
|
2376
|
+
setValue('0-0');
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
const handleTreeDataButtonClick = () => {
|
|
2380
|
+
if(isEqual(tree, json)){
|
|
2381
|
+
setTree(json2);
|
|
2382
|
+
} else {
|
|
2383
|
+
setTree(json);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
return (
|
|
2387
|
+
<>
|
|
2388
|
+
<div>value 受控时,当 treeData/treeDataSimpleJson 改变时,应该根据 value 自动展开</div>
|
|
2389
|
+
<Tree
|
|
2390
|
+
value={value}
|
|
2391
|
+
treeDataSimpleJson={tree}
|
|
2392
|
+
style={style}
|
|
2393
|
+
onChange={v => setValue(v)}
|
|
2394
|
+
/>
|
|
2395
|
+
<Space>
|
|
2396
|
+
<Button onClick={handleValueButtonClick}>改变 value</Button>
|
|
2397
|
+
<Button onClick={handleTreeDataButtonClick}>改变 TreeData</Button>
|
|
2398
|
+
</Space>
|
|
2399
|
+
</>
|
|
2400
|
+
)
|
|
2401
|
+
}
|
package/tree/index.tsx
CHANGED
|
@@ -192,6 +192,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
|
|
|
192
192
|
const newState: Partial<TreeState> = {
|
|
193
193
|
prevProps: props,
|
|
194
194
|
};
|
|
195
|
+
const isExpandControlled = 'expandedKeys' in props;
|
|
195
196
|
|
|
196
197
|
// Accept a props field as a parameter to determine whether to update the field
|
|
197
198
|
const needUpdate = (name: string) => {
|
|
@@ -239,7 +240,8 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
|
|
|
239
240
|
newState.motionType = null;
|
|
240
241
|
}
|
|
241
242
|
}
|
|
242
|
-
const
|
|
243
|
+
const dataUpdated = needUpdate('treeDataSimpleJson') || needUpdate('treeData');
|
|
244
|
+
const expandAllWhenDataChange = dataUpdated && props.expandAll;
|
|
243
245
|
if (!isSeaching) {
|
|
244
246
|
// Update expandedKeys
|
|
245
247
|
if (needUpdate('expandedKeys') || (prevProps && needUpdate('autoExpandParent'))) {
|
|
@@ -273,7 +275,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
|
|
|
273
275
|
props.multiple,
|
|
274
276
|
valueEntities
|
|
275
277
|
);
|
|
276
|
-
} else if (!prevProps && props.value) {
|
|
278
|
+
} else if ((!prevProps || (!isExpandControlled && dataUpdated)) && props.value) {
|
|
277
279
|
newState.expandedKeys = calcExpandedKeysForValues(
|
|
278
280
|
props.value,
|
|
279
281
|
keyEntities,
|
package/tree/interface.ts
CHANGED
|
@@ -131,6 +131,7 @@ export interface NodeListProps {
|
|
|
131
131
|
motionKeys: Set<string>;
|
|
132
132
|
motionType: string;
|
|
133
133
|
flattenList: FlattenNode[] | undefined;
|
|
134
|
+
searchTargetIsDeep?: boolean;
|
|
134
135
|
renderTreeNode: (treeNode: FlattenNode, ind?: number, style?: React.CSSProperties) => ReactNode;
|
|
135
136
|
}
|
|
136
137
|
export type TransitionNodes<T> = Array<T | Array<T>>;
|
package/tree/nodeList.tsx
CHANGED
|
@@ -59,9 +59,9 @@ export default class NodeList extends PureComponent<NodeListProps, NodeListState
|
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
render() {
|
|
62
|
-
const { flattenNodes, motionType, renderTreeNode } = this.props;
|
|
62
|
+
const { flattenNodes, motionType, searchTargetIsDeep, renderTreeNode } = this.props;
|
|
63
63
|
const { transitionNodes } = this.state;
|
|
64
|
-
const mapData = transitionNodes.length ? transitionNodes : flattenNodes;
|
|
64
|
+
const mapData = transitionNodes.length && !searchTargetIsDeep ? transitionNodes : flattenNodes;
|
|
65
65
|
const options = mapData.map(treeNode => {
|
|
66
66
|
const isMotionNode = Array.isArray(treeNode);
|
|
67
67
|
if (isMotionNode && !(treeNode as FlattenNode[]).length) {
|
|
@@ -552,6 +552,17 @@ describe('TreeSelect', () => {
|
|
|
552
552
|
searchWrapper.find('input').simulate('change', event);
|
|
553
553
|
expect(spyOnSearch.calledOnce).toBe(true);
|
|
554
554
|
expect(spyOnSearch.calledWithMatch(searchValue)).toBe(true);
|
|
555
|
+
|
|
556
|
+
/* Check the input parameters of onSearch */
|
|
557
|
+
searchValue = '北京';
|
|
558
|
+
event = { target: { value: searchValue } };
|
|
559
|
+
searchWrapper.find('input').simulate('change', event);
|
|
560
|
+
expect(spyOnSearch.callCount).toBe(2);
|
|
561
|
+
const firstCall = spyOnSearch.getCall(1);
|
|
562
|
+
const args = firstCall.args;
|
|
563
|
+
expect(args[0]).toEqual('北京');
|
|
564
|
+
expect(args[1].includes('yazhou')).toEqual(true);
|
|
565
|
+
expect(args[1].includes('zhongguo')).toEqual(true);
|
|
555
566
|
});
|
|
556
567
|
|
|
557
568
|
it('filterTreeNode shows correct result', () => {
|
|
@@ -937,4 +948,21 @@ describe('TreeSelect', () => {
|
|
|
937
948
|
).toEqual(0);
|
|
938
949
|
});
|
|
939
950
|
|
|
951
|
+
it('expandedKeys controlled + filterTreeNode', () => {
|
|
952
|
+
const spyOnExpand = sinon.spy(() => { });
|
|
953
|
+
const treeSelect = getTreeSelect({
|
|
954
|
+
expandedKeys: [],
|
|
955
|
+
onExpand: spyOnExpand,
|
|
956
|
+
filterTreeNode: true,
|
|
957
|
+
});
|
|
958
|
+
const searchWrapper = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-search-wrapper`);
|
|
959
|
+
const searchValue = '北京';
|
|
960
|
+
const event = { target: { value: searchValue } };
|
|
961
|
+
searchWrapper.find('input').simulate('change', event);
|
|
962
|
+
expect(spyOnExpand.callCount).toBe(0);
|
|
963
|
+
/* filter won't impact on the expansion of node when expandedKeys is controlled */
|
|
964
|
+
const topNode = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-1`);
|
|
965
|
+
expect(topNode.at(0).hasClass(`${BASE_CLASS_PREFIX}-tree-option-collapsed`)).toEqual(true);
|
|
966
|
+
expect(topNode.at(1).hasClass(`${BASE_CLASS_PREFIX}-tree-option-collapsed`)).toEqual(true);
|
|
967
|
+
});
|
|
940
968
|
})
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import { Icon, Button, Form, Popover, Tag } from '../../index';
|
|
2
|
+
import { Icon, Button, Form, Popover, Tag, Typography } from '../../index';
|
|
3
3
|
import TreeSelect from '../index';
|
|
4
4
|
import { flattenDeep } from 'lodash';
|
|
5
5
|
import CustomTrigger from './CustomTrigger';
|
|
6
6
|
import { IconCreditCard } from '@douyinfe/semi-icons';
|
|
7
7
|
const TreeNode = TreeSelect.TreeNode;
|
|
8
|
+
const { Title } = Typography;
|
|
8
9
|
|
|
9
10
|
export default {
|
|
10
11
|
title: 'TreeSelect',
|
|
@@ -1406,4 +1407,56 @@ export const CheckRelationDemo = () => {
|
|
|
1406
1407
|
/>
|
|
1407
1408
|
</>
|
|
1408
1409
|
);
|
|
1409
|
-
};
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1412
|
+
export const SearchableAndExpandedKeys = () => {
|
|
1413
|
+
const [expandedKeys1, setExpandedKeys1] = useState([]);
|
|
1414
|
+
const [expandedKeys2, setExpandedKeys2] = useState([]);
|
|
1415
|
+
const [expandedKeys3, setExpandedKeys3] = useState([]);
|
|
1416
|
+
return (
|
|
1417
|
+
<>
|
|
1418
|
+
<Title heading={6}>expandedKeys 受控</Title>
|
|
1419
|
+
<TreeSelect
|
|
1420
|
+
style={{ width: 300, marginBottom: 30 }}
|
|
1421
|
+
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
|
|
1422
|
+
treeData={treeData2}
|
|
1423
|
+
expandedKeys={expandedKeys1}
|
|
1424
|
+
defaultValue='beijing'
|
|
1425
|
+
onExpand={v => {
|
|
1426
|
+
console.log('onExpand value: ', v);
|
|
1427
|
+
setExpandedKeys1(v);
|
|
1428
|
+
}}
|
|
1429
|
+
/>
|
|
1430
|
+
<Title heading={6}>expandedKeys 受控 + 开启搜索</Title>
|
|
1431
|
+
<TreeSelect
|
|
1432
|
+
style={{ width: 300, marginBottom: 30 }}
|
|
1433
|
+
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
|
|
1434
|
+
treeData={treeData2}
|
|
1435
|
+
filterTreeNode
|
|
1436
|
+
defaultValue='beijing'
|
|
1437
|
+
expandedKeys={expandedKeys2}
|
|
1438
|
+
onExpand={v => {
|
|
1439
|
+
console.log('onExpand value: ', v);
|
|
1440
|
+
setExpandedKeys2(v);
|
|
1441
|
+
}}
|
|
1442
|
+
/>
|
|
1443
|
+
<Title heading={6}>expandedKeys 受控 + 开启搜索 + 搜索时更新 expandedKeys</Title>
|
|
1444
|
+
<TreeSelect
|
|
1445
|
+
style={{ width: 300, marginBottom: 30 }}
|
|
1446
|
+
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
|
|
1447
|
+
treeData={treeData2}
|
|
1448
|
+
filterTreeNode
|
|
1449
|
+
expandedKeys={expandedKeys3}
|
|
1450
|
+
defaultValue='beijing'
|
|
1451
|
+
onExpand={v => {
|
|
1452
|
+
console.log('onExpand value: ', v);
|
|
1453
|
+
setExpandedKeys3(v)
|
|
1454
|
+
}}
|
|
1455
|
+
onSearch={(input, filterExpandedKeys) => {
|
|
1456
|
+
console.log('onExpand filterExpandedKeys: ', filterExpandedKeys);
|
|
1457
|
+
setExpandedKeys3(filterExpandedKeys);
|
|
1458
|
+
}}
|
|
1459
|
+
/>
|
|
1460
|
+
</>
|
|
1461
|
+
)
|
|
1462
|
+
}
|
package/treeSelect/index.tsx
CHANGED
|
@@ -561,8 +561,8 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
|
|
|
561
561
|
notifySelect: ((selectKey, bool, node) => {
|
|
562
562
|
this.props.onSelect && this.props.onSelect(selectKey, bool, node);
|
|
563
563
|
}),
|
|
564
|
-
notifySearch: input => {
|
|
565
|
-
this.props.onSearch && this.props.onSearch(input);
|
|
564
|
+
notifySearch: (input, filteredExpandedKeys) => {
|
|
565
|
+
this.props.onSearch && this.props.onSearch(input, filteredExpandedKeys);
|
|
566
566
|
},
|
|
567
567
|
cacheFlattenNodes: bool => {
|
|
568
568
|
this._flattenNodes = bool ? cloneDeep(this.state.flattenNodes) : null;
|
|
@@ -1232,9 +1232,10 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
|
|
|
1232
1232
|
};
|
|
1233
1233
|
|
|
1234
1234
|
renderNodeList = () => {
|
|
1235
|
-
const { flattenNodes, motionKeys, motionType } = this.state;
|
|
1235
|
+
const { flattenNodes, motionKeys, motionType, filteredKeys } = this.state;
|
|
1236
1236
|
const { direction } = this.context;
|
|
1237
1237
|
const { virtualize, motionExpand } = this.props;
|
|
1238
|
+
const isExpandControlled = 'expandedKeys' in this.props;
|
|
1238
1239
|
if (!virtualize || isEmpty(virtualize)) {
|
|
1239
1240
|
return (
|
|
1240
1241
|
<NodeList
|
|
@@ -1242,6 +1243,13 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
|
|
|
1242
1243
|
flattenList={this._flattenNodes}
|
|
1243
1244
|
motionKeys={motionExpand ? motionKeys : new Set([])}
|
|
1244
1245
|
motionType={motionType}
|
|
1246
|
+
// When motionKeys is empty, but filteredKeys is not empty (that is, the search hits), this situation should be distinguished from ordinary motionKeys
|
|
1247
|
+
searchTargetIsDeep={
|
|
1248
|
+
isExpandControlled &&
|
|
1249
|
+
motionExpand &&
|
|
1250
|
+
isEmpty(motionKeys) &&
|
|
1251
|
+
!isEmpty(filteredKeys)
|
|
1252
|
+
}
|
|
1245
1253
|
onMotionEnd={this.onMotionEnd}
|
|
1246
1254
|
renderTreeNode={this.renderTreeNode}
|
|
1247
1255
|
/>
|