@douyinfe/semi-ui 2.6.0 → 2.7.1
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/_base/_story/index.stories.js +2 -6
- package/_portal/_story/portal.stories.js +1 -5
- package/_utils/hooks/usePrevFocus.ts +1 -0
- 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 +109 -7
- package/dist/css/semi.min.css +1 -1
- package/dist/umd/semi-ui.js +925 -152
- 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/form/hooks/useFormApi.tsx +3 -2
- package/input/_story/input.stories.js +23 -1
- package/lib/cjs/_utils/hooks/usePrevFocus.js +1 -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/form/hooks/useFormApi.d.ts +2 -1
- package/lib/cjs/modal/useModal/HookModal.js +2 -0
- package/lib/cjs/radio/radioGroup.js +6 -0
- package/lib/cjs/select/index.js +5 -2
- package/lib/cjs/tag/group.d.ts +2 -0
- package/lib/cjs/tag/group.js +4 -2
- package/lib/cjs/tooltip/index.js +1 -1
- package/lib/cjs/tree/index.js +5 -3
- package/lib/cjs/tree/interface.d.ts +1 -0
- package/lib/cjs/tree/nodeList.js +3 -1
- package/lib/cjs/treeSelect/index.js +11 -3
- package/lib/es/_utils/hooks/usePrevFocus.js +2 -0
- 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/form/hooks/useFormApi.d.ts +2 -1
- package/lib/es/modal/useModal/HookModal.js +2 -0
- package/lib/es/radio/radioGroup.js +6 -0
- package/lib/es/select/index.js +5 -2
- package/lib/es/tag/group.d.ts +2 -0
- package/lib/es/tag/group.js +4 -2
- package/lib/es/tooltip/index.js +1 -1
- package/lib/es/tree/index.js +5 -3
- package/lib/es/tree/interface.d.ts +1 -0
- package/lib/es/tree/nodeList.js +3 -1
- package/lib/es/treeSelect/index.js +11 -3
- package/modal/_story/modal.stories.js +93 -1
- package/modal/useModal/HookModal.tsx +1 -0
- package/notification/_story/useNotification/index.jsx +21 -7
- package/package.json +9 -9
- package/radio/__test__/radioGroup.test.jsx +9 -1
- package/radio/_story/radio.stories.js +22 -1
- package/radio/radioGroup.tsx +9 -0
- package/select/_story/select.stories.js +73 -2
- package/select/index.tsx +5 -3
- package/table/_story/v2/FixedMemoryLeak/index.jsx +33 -0
- package/table/_story/v2/index.js +2 -1
- package/tag/group.tsx +5 -3
- package/toast/_story/toast.stories.js +41 -0
- package/tooltip/index.tsx +1 -1
- 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 +3 -2
- package/treeSelect/__test__/treeSelect.test.js +28 -0
- package/treeSelect/_story/treeSelect.stories.js +55 -2
- package/treeSelect/index.tsx +14 -3
|
@@ -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
|
});
|
|
@@ -897,6 +901,10 @@ class TreeSelect extends BaseComponent {
|
|
|
897
901
|
this.clickOutsideHandler = null;
|
|
898
902
|
this.foundation = new TreeSelectFoundation(this.adapter);
|
|
899
903
|
this.treeSelectID = _sliceInstanceProperty(_context2 = Math.random().toString(36)).call(_context2, 2);
|
|
904
|
+
|
|
905
|
+
this.onMotionEnd = () => {
|
|
906
|
+
this.adapter.rePositionDropdown();
|
|
907
|
+
};
|
|
900
908
|
} // eslint-disable-next-line max-lines-per-function
|
|
901
909
|
|
|
902
910
|
|
|
@@ -1076,8 +1084,8 @@ class TreeSelect extends BaseComponent {
|
|
|
1076
1084
|
notifySelect: (selectKey, bool, node) => {
|
|
1077
1085
|
this.props.onSelect && this.props.onSelect(selectKey, bool, node);
|
|
1078
1086
|
},
|
|
1079
|
-
notifySearch: input => {
|
|
1080
|
-
this.props.onSearch && this.props.onSearch(input);
|
|
1087
|
+
notifySearch: (input, filteredExpandedKeys) => {
|
|
1088
|
+
this.props.onSearch && this.props.onSearch(input, filteredExpandedKeys);
|
|
1081
1089
|
},
|
|
1082
1090
|
cacheFlattenNodes: bool => {
|
|
1083
1091
|
this._flattenNodes = bool ? cloneDeep(this.state.flattenNodes) : null;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import
|
|
2
|
+
import en_GB from '../../locale/source/en_GB';
|
|
3
|
+
|
|
4
|
+
import { Select, Modal, Button, Tooltip, Popover, ConfigProvider, Tag, Space } from '../../index';
|
|
3
5
|
import CollapsibleInModal from './CollapsibleInModal';
|
|
4
6
|
import DynamicContextDemo from './DynamicContext';
|
|
5
7
|
|
|
@@ -248,4 +250,94 @@ KeepDomNotLazy.story = {
|
|
|
248
250
|
name: 'keepDOM && not lazy',
|
|
249
251
|
};
|
|
250
252
|
|
|
253
|
+
export const UseModalDemo = () => {
|
|
254
|
+
const [modal, contextHolder] = Modal.useModal();
|
|
255
|
+
const config = { 'title': 'old title', 'content': 'old content' };
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<ConfigProvider locale={en_GB}>
|
|
259
|
+
<div>
|
|
260
|
+
<Button
|
|
261
|
+
onClick={() => {
|
|
262
|
+
const currentModal = modal.confirm(config);
|
|
263
|
+
|
|
264
|
+
setTimeout(() => {
|
|
265
|
+
currentModal.update({ title: "new title", content: "new content" });
|
|
266
|
+
}, 1000);
|
|
267
|
+
}}
|
|
268
|
+
>
|
|
269
|
+
Confirm Modal
|
|
270
|
+
</Button>
|
|
271
|
+
</div>
|
|
272
|
+
{contextHolder}
|
|
273
|
+
</ConfigProvider>
|
|
274
|
+
);
|
|
275
|
+
};
|
|
276
|
+
UseModalDemo.storyName = "useModal";
|
|
277
|
+
|
|
278
|
+
export const UseModalDestroy = () => {
|
|
279
|
+
const [modal, contextHolder] = Modal.useModal();
|
|
280
|
+
const config = { 'title': 'old title', 'content': 'old content' };
|
|
281
|
+
|
|
282
|
+
return (
|
|
283
|
+
<ConfigProvider locale={en_GB}>
|
|
284
|
+
<div>
|
|
285
|
+
<Button
|
|
286
|
+
onClick={() => {
|
|
287
|
+
const currentModal = modal.confirm(config);
|
|
288
|
+
|
|
289
|
+
setTimeout(() => {
|
|
290
|
+
currentModal.destroy();
|
|
291
|
+
}, 1000);
|
|
292
|
+
}}
|
|
293
|
+
>
|
|
294
|
+
Confirm Modal
|
|
295
|
+
</Button>
|
|
296
|
+
</div>
|
|
297
|
+
{contextHolder}
|
|
298
|
+
</ConfigProvider>
|
|
299
|
+
);
|
|
300
|
+
};
|
|
301
|
+
UseModalDestroy.storyName = "useModal destroy";
|
|
302
|
+
|
|
303
|
+
export const UseModalAfterClose = () => {
|
|
304
|
+
const [modal, contextHolder] = Modal.useModal();
|
|
305
|
+
const [closed, setClosed] = React.useState(false);
|
|
306
|
+
const [leave, setLeave] = React.useState(false);
|
|
307
|
+
|
|
308
|
+
const config = {
|
|
309
|
+
title: 'old title',
|
|
310
|
+
content: 'old content',
|
|
311
|
+
afterClose: () => {
|
|
312
|
+
setClosed(true);
|
|
313
|
+
},
|
|
314
|
+
motion: {
|
|
315
|
+
didLeave: () => {
|
|
316
|
+
console.log('didLeave');
|
|
317
|
+
setLeave(true);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
251
321
|
|
|
322
|
+
return (
|
|
323
|
+
<ConfigProvider locale={en_GB}>
|
|
324
|
+
<Space>
|
|
325
|
+
<Button
|
|
326
|
+
onClick={() => {
|
|
327
|
+
const currentModal = modal.confirm(config);
|
|
328
|
+
|
|
329
|
+
setTimeout(() => {
|
|
330
|
+
currentModal.destroy();
|
|
331
|
+
}, 0);
|
|
332
|
+
}}
|
|
333
|
+
>
|
|
334
|
+
Confirm Modal
|
|
335
|
+
</Button>
|
|
336
|
+
<Tag>{`closed: ${closed}`}</Tag>
|
|
337
|
+
{/* <Tag>{`motion leave: ${leave}`}</Tag> */}
|
|
338
|
+
</Space>
|
|
339
|
+
{contextHolder}
|
|
340
|
+
</ConfigProvider>
|
|
341
|
+
);
|
|
342
|
+
};
|
|
343
|
+
UseModalAfterClose.storyName = "useModal afterClose";
|
|
@@ -4,18 +4,32 @@ import { Button, ConfigProvider } from '../../../index';
|
|
|
4
4
|
import Context from './context';
|
|
5
5
|
|
|
6
6
|
function App({ children, globalVars }) {
|
|
7
|
-
return
|
|
7
|
+
return (
|
|
8
|
+
<div data-cy="notice-container">
|
|
9
|
+
<Context.Provider value={{ title: '1111', ...globalVars }}>{children}</Context.Provider>
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
export default function Demo() {
|
|
11
|
-
const [
|
|
15
|
+
const [notice, elements] = useNotification();
|
|
16
|
+
const config = {
|
|
17
|
+
content: 'Hello World',
|
|
18
|
+
position: 'top',
|
|
19
|
+
title: <Context.Consumer>{({ title }) => <strong>{title}</strong>}</Context.Consumer>,
|
|
20
|
+
duration: 0,
|
|
21
|
+
};
|
|
12
22
|
|
|
13
23
|
const addNotice = () => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
const id1 = notice.info(config);
|
|
25
|
+
const id2 = notice.success(config);
|
|
26
|
+
const id3 = notice.warning(config);
|
|
27
|
+
const id4 = notice.error(config);
|
|
28
|
+
const id5 = notice.open(config);
|
|
29
|
+
|
|
30
|
+
// setTimeout(() => {
|
|
31
|
+
// notice.close(id5);
|
|
32
|
+
// }, 1000);
|
|
19
33
|
};
|
|
20
34
|
|
|
21
35
|
return (
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@douyinfe/semi-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
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.1",
|
|
18
|
+
"@douyinfe/semi-animation-react": "2.7.1",
|
|
19
|
+
"@douyinfe/semi-foundation": "2.7.1",
|
|
20
|
+
"@douyinfe/semi-icons": "2.7.1",
|
|
21
|
+
"@douyinfe/semi-illustrations": "2.7.1",
|
|
22
|
+
"@douyinfe/semi-theme-default": "2.7.1",
|
|
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": "d0dbd6f932a74386b429dedbc32ae92d9e0af7b9",
|
|
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.1",
|
|
79
79
|
"@storybook/addon-knobs": "^6.3.1",
|
|
80
80
|
"@types/lodash": "^4.14.176",
|
|
81
81
|
"babel-loader": "^8.2.2",
|
|
@@ -196,4 +196,12 @@ describe('RadioGroup', () => {
|
|
|
196
196
|
expect(middleRadio.exists(`.${BASE_CLASS_PREFIX}-radio-addon-buttonRadio-middle`)).toEqual(true);
|
|
197
197
|
expect(largeRadio.exists(`.${BASE_CLASS_PREFIX}-radio-addon-buttonRadio-large`)).toEqual(true);
|
|
198
198
|
});
|
|
199
|
-
|
|
199
|
+
|
|
200
|
+
it('does not trigger Maximum update exceeded when setting radio-group\'s value to NaN', () => {
|
|
201
|
+
const radioGroup = mount(
|
|
202
|
+
createRadioGroup({ value: NaN }),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
expect(radioGroup.exists(`${BASE_CLASS_PREFIX}-radio-checked`)).toEqual(false);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
@@ -865,4 +865,25 @@ export const FixWithFieldLossRef = () => {
|
|
|
865
865
|
</Form>
|
|
866
866
|
);
|
|
867
867
|
}
|
|
868
|
-
FixWithFieldLossRef.storyName = '修复 Form Field 丢失 ref 问题 #384';
|
|
868
|
+
FixWithFieldLossRef.storyName = '修复 Form Field 丢失 ref 问题 #384';
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
export const SwitchValueToNaN = () => {
|
|
872
|
+
const [val, setVal] = useState(1);
|
|
873
|
+
|
|
874
|
+
return (
|
|
875
|
+
<>
|
|
876
|
+
<RadioGroup direction="vertical" aria-label="单选组合示例" value={val}>
|
|
877
|
+
<Radio value={1}>A</Radio>
|
|
878
|
+
<Radio value={2}>B</Radio>
|
|
879
|
+
<Radio value={3}>C</Radio>
|
|
880
|
+
<Radio value={4}>D</Radio>
|
|
881
|
+
</RadioGroup>
|
|
882
|
+
<Space>
|
|
883
|
+
<Button onClick={() => setVal(NaN)}>NaN</Button>
|
|
884
|
+
<Button onClick={() => setVal(2)}>2</Button>
|
|
885
|
+
</Space>
|
|
886
|
+
</>
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
SwitchValueToNaN.storyName = 'SwitchValueToNaN';
|
package/radio/radioGroup.tsx
CHANGED
|
@@ -97,6 +97,15 @@ class RadioGroup extends BaseComponent<RadioGroupProps, RadioGroupState> {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
componentDidUpdate(prevProps: RadioGroupProps) {
|
|
100
|
+
if (typeof prevProps.value === 'number'
|
|
101
|
+
&& isNaN(prevProps.value)
|
|
102
|
+
&& typeof this.props.value === 'number'
|
|
103
|
+
&& isNaN(this.props.value)
|
|
104
|
+
) {
|
|
105
|
+
// `NaN === NaN` returns false, and this will fail the next if check
|
|
106
|
+
// therefore triggering an infinite loop
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
100
109
|
if (prevProps.value !== this.props.value) {
|
|
101
110
|
this.foundation.handlePropValueChange(this.props.value);
|
|
102
111
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
import './select.scss';
|
|
4
|
-
import { Input, Select, Button, Icon, Avatar, Checkbox, Form, withField, Space } from '../../index';
|
|
4
|
+
import { Input, Select, Button, Icon, Avatar, Checkbox, Form, withField, Space, Tag } from '../../index';
|
|
5
5
|
import CustomTrigger from './CustomTrigger';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
7
|
import { getHighLightTextHTML } from '../../_utils/index';
|
|
@@ -2903,4 +2903,75 @@ export const AutoClearSearchValue = () => {
|
|
|
2903
2903
|
|
|
2904
2904
|
SelectInputPropsDemo.story = {
|
|
2905
2905
|
name: 'AutoClearSearchValue',
|
|
2906
|
-
};
|
|
2906
|
+
};
|
|
2907
|
+
|
|
2908
|
+
|
|
2909
|
+
export const RenderSelectedItemCallCount = () => {
|
|
2910
|
+
const list = [
|
|
2911
|
+
{ "name": "夏可漫", "email": "xiakeman@example.com", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/avatarDemo.jpeg" },
|
|
2912
|
+
{ "name": "申悦", "email": "shenyue@example.com", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg" },
|
|
2913
|
+
{ "name": "曲晨一", "email": "quchenyi@example.com", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/8bd8224511db085ed74fea37205aede5.jpg" },
|
|
2914
|
+
{ "name": "文嘉茂", "email": "wenjiamao@example.com", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/6fbafc2d-e3e6-4cff-a1e2-17709c680624.png" },
|
|
2915
|
+
{ "name": "文嘉茂2", "email": "wenjiamao@example.com", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/6fbafc2d-e3e6-4cff-a1e2-17709c680624.png" },
|
|
2916
|
+
{ "name": "文嘉茂3", "email": "wenjiamao@example.com", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/6fbafc2d-e3e6-4cff-a1e2-17709c680624.png" },
|
|
2917
|
+
]
|
|
2918
|
+
|
|
2919
|
+
const renderMultipleWithCustomTag = (optionNode, { onClose }) => {
|
|
2920
|
+
console.count('rerender')
|
|
2921
|
+
const content = (
|
|
2922
|
+
<Tag
|
|
2923
|
+
avatarSrc={optionNode.avatar}
|
|
2924
|
+
avatarShape='circle'
|
|
2925
|
+
closable={true}
|
|
2926
|
+
onClose={onClose}
|
|
2927
|
+
size='large'
|
|
2928
|
+
>
|
|
2929
|
+
{optionNode.name}
|
|
2930
|
+
</Tag>
|
|
2931
|
+
);
|
|
2932
|
+
return {
|
|
2933
|
+
isRenderInTag: false,
|
|
2934
|
+
content
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
const renderCustomOption = (item, index) => {
|
|
2939
|
+
const optionStyle = {
|
|
2940
|
+
display: 'flex',
|
|
2941
|
+
paddingLeft: 24,
|
|
2942
|
+
paddingTop: 10,
|
|
2943
|
+
paddingBottom: 10
|
|
2944
|
+
}
|
|
2945
|
+
return (
|
|
2946
|
+
<Select.Option key={index} value={item.name} style={optionStyle} showTick={true} {...item} key={item.email}>
|
|
2947
|
+
<Avatar size="small" src={item.avatar} />
|
|
2948
|
+
<div style={{ marginLeft: 8 }}>
|
|
2949
|
+
<div style={{ fontSize: 14 }}>{item.name}</div>
|
|
2950
|
+
<div style={{ color: 'var(--color-text-2)', fontSize: 12, lineHeight: '16px', fontWeight: 'normal' }}>{item.email}</div>
|
|
2951
|
+
</div>
|
|
2952
|
+
</Select.Option>
|
|
2953
|
+
)
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
return (
|
|
2957
|
+
<>
|
|
2958
|
+
<Select
|
|
2959
|
+
placeholder='请选择'
|
|
2960
|
+
showClear
|
|
2961
|
+
multiple
|
|
2962
|
+
maxTagCount={2}
|
|
2963
|
+
style={{ width: 280, height: 40 }}
|
|
2964
|
+
onChange={v => console.log(v)}
|
|
2965
|
+
defaultValue={'夏可漫'}
|
|
2966
|
+
renderSelectedItem={renderMultipleWithCustomTag}
|
|
2967
|
+
>
|
|
2968
|
+
{list.map((item, index) => renderCustomOption(item, index))}
|
|
2969
|
+
</Select>
|
|
2970
|
+
</>
|
|
2971
|
+
);
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
|
|
2975
|
+
RenderSelectedItemCallCount.story = {
|
|
2976
|
+
name: 'RenderSelectedItemCallCount',
|
|
2977
|
+
};
|
package/select/index.tsx
CHANGED
|
@@ -898,8 +898,10 @@ class Select extends BaseComponent<SelectProps, SelectState> {
|
|
|
898
898
|
content: optionNode.label,
|
|
899
899
|
});
|
|
900
900
|
}
|
|
901
|
+
|
|
902
|
+
const mapItems = maxTagCount ? selectedItems.slice(0, maxTagCount) : selectedItems; // no need to render rest tag when maxTagCount is setting
|
|
901
903
|
|
|
902
|
-
const tags =
|
|
904
|
+
const tags = mapItems.map((item, i) => {
|
|
903
905
|
const label = item[0];
|
|
904
906
|
const { value } = item[1];
|
|
905
907
|
const disabled = item[1].disabled || selectDisabled;
|
|
@@ -939,11 +941,11 @@ class Select extends BaseComponent<SelectProps, SelectState> {
|
|
|
939
941
|
// [prefixcls + '-selection-text-inactive']: !inputValue && !tags.length,
|
|
940
942
|
});
|
|
941
943
|
const placeholderText = placeholder && !inputValue ? <span className={spanCls}>{placeholder}</span> : null;
|
|
942
|
-
const n =
|
|
944
|
+
const n = selectedItems.length > maxTagCount ? maxTagCount : undefined;
|
|
943
945
|
|
|
944
946
|
const NotOneLine = !maxTagCount; // Multiple lines (that is, do not set maxTagCount), do not use TagGroup, directly traverse with Tag, otherwise Input cannot follow the correct position
|
|
945
947
|
|
|
946
|
-
const tagContent = NotOneLine ? tags : <TagGroup tagList={tags} maxTagCount={n} size="large" mode="custom" />;
|
|
948
|
+
const tagContent = NotOneLine ? tags : <TagGroup tagList={tags} maxTagCount={n} restCount={maxTagCount ? selectedItems.length - maxTagCount : undefined} size="large" mode="custom" />;
|
|
947
949
|
|
|
948
950
|
return (
|
|
949
951
|
<>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Popconfirm, Table } from "@douyinfe/semi-ui";
|
|
3
|
+
|
|
4
|
+
export default function App() {
|
|
5
|
+
const [data, setData] = useState([{ a: 1 }]);
|
|
6
|
+
return (
|
|
7
|
+
<Table
|
|
8
|
+
dataSource={data}
|
|
9
|
+
columns={[
|
|
10
|
+
{
|
|
11
|
+
dataIndex: "a",
|
|
12
|
+
title: "a",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
dataIndex: "b",
|
|
16
|
+
render: () => {
|
|
17
|
+
return (
|
|
18
|
+
<Popconfirm
|
|
19
|
+
onConfirm={() => {
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
setData([]);
|
|
22
|
+
});
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
删除
|
|
26
|
+
</Popconfirm>
|
|
27
|
+
);
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
]}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
}
|
package/table/_story/v2/index.js
CHANGED
|
@@ -3,4 +3,5 @@ export { default as FixedColumnsChange } from './FixedColumnsChange';
|
|
|
3
3
|
export { default as FixedZIndex } from './FixedZIndex';
|
|
4
4
|
export { default as FixedHeaderMerge } from './FixedHeaderMerge';
|
|
5
5
|
export { default as FixedResizable } from './FixedResizable';
|
|
6
|
-
export { default as FixedExpandedRow } from './FixedExpandedRow';
|
|
6
|
+
export { default as FixedExpandedRow } from './FixedExpandedRow';
|
|
7
|
+
export { default as FixedMemoryLeak } from './FixedMemoryLeak';
|
package/tag/group.tsx
CHANGED
|
@@ -14,12 +14,13 @@ export interface TagGroupProps {
|
|
|
14
14
|
style?: React.CSSProperties;
|
|
15
15
|
className?: string;
|
|
16
16
|
maxTagCount?: number;
|
|
17
|
+
restCount?: number;
|
|
17
18
|
tagList?: (TagProps | React.ReactNode)[];
|
|
18
19
|
size?: 'small' | 'large';
|
|
19
20
|
showPopover?: boolean;
|
|
20
21
|
popoverProps?: PopoverProps;
|
|
21
22
|
avatarShape?: AvatarShape;
|
|
22
|
-
mode?: string;
|
|
23
|
+
mode?: string;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export default class TagGroup extends PureComponent<TagGroupProps> {
|
|
@@ -35,6 +36,7 @@ export default class TagGroup extends PureComponent<TagGroupProps> {
|
|
|
35
36
|
style: PropTypes.object,
|
|
36
37
|
className: PropTypes.string,
|
|
37
38
|
maxTagCount: PropTypes.number,
|
|
39
|
+
restCount: PropTypes.number,
|
|
38
40
|
tagList: PropTypes.array,
|
|
39
41
|
size: PropTypes.oneOf(tagSize),
|
|
40
42
|
mode: PropTypes.string,
|
|
@@ -77,8 +79,8 @@ export default class TagGroup extends PureComponent<TagGroupProps> {
|
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
renderMergeTags(tags: (Tag | React.ReactNode)[]) {
|
|
80
|
-
const { maxTagCount, tagList } = this.props;
|
|
81
|
-
const n = tagList.length - maxTagCount;
|
|
82
|
+
const { maxTagCount, tagList, restCount } = this.props;
|
|
83
|
+
const n = restCount ? restCount : tagList.length - maxTagCount;
|
|
82
84
|
let renderTags: (Tag | React.ReactNode)[] = tags;
|
|
83
85
|
|
|
84
86
|
const normalTags: (Tag | React.ReactNode)[] = tags.slice(0, maxTagCount);
|
|
@@ -96,3 +96,44 @@ export const _Toast = () => (
|
|
|
96
96
|
_Toast.story = {
|
|
97
97
|
name: 'toast',
|
|
98
98
|
};
|
|
99
|
+
|
|
100
|
+
const ReachableContext = React.createContext();
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* test with cypress
|
|
104
|
+
* @returns
|
|
105
|
+
*/
|
|
106
|
+
export const useToastDemo = () => {
|
|
107
|
+
const [toast, contextHolder] = Toast.useToast();
|
|
108
|
+
const config = {
|
|
109
|
+
duration: 0,
|
|
110
|
+
title: 'This is a success message',
|
|
111
|
+
content: <ReachableContext.Consumer>{name => `ReachableContext: ${name}`}</ReachableContext.Consumer>,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<ReachableContext.Provider value="Light">
|
|
116
|
+
<div>
|
|
117
|
+
<Button
|
|
118
|
+
onClick={() => {
|
|
119
|
+
toast.success(config);
|
|
120
|
+
toast.info(config);
|
|
121
|
+
toast.error(config);
|
|
122
|
+
toast.warning(config);
|
|
123
|
+
const id = toast.open(config);
|
|
124
|
+
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
toast.close(id);
|
|
127
|
+
}, 100);
|
|
128
|
+
}}
|
|
129
|
+
>
|
|
130
|
+
Hook Toast
|
|
131
|
+
</Button>
|
|
132
|
+
</div>
|
|
133
|
+
<div data-cy="context-holder">
|
|
134
|
+
{contextHolder}
|
|
135
|
+
</div>
|
|
136
|
+
</ReachableContext.Provider>
|
|
137
|
+
);
|
|
138
|
+
};
|
|
139
|
+
useToastDemo.storyName = "useToast";
|
package/tooltip/index.tsx
CHANGED
|
@@ -302,7 +302,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
|
|
|
302
302
|
} else {
|
|
303
303
|
willUpdateStates.visible = visible;
|
|
304
304
|
}
|
|
305
|
-
this.setState(willUpdateStates as TooltipState, () => {
|
|
305
|
+
this.mounted && this.setState(willUpdateStates as TooltipState, () => {
|
|
306
306
|
cb();
|
|
307
307
|
});
|
|
308
308
|
},
|
|
@@ -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(() => { });
|