@douyinfe/semi-ui 2.4.1 → 2.6.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.
Files changed (138) hide show
  1. package/calendar/monthCalendar.tsx +14 -13
  2. package/cascader/__test__/cascader.test.js +24 -0
  3. package/cascader/_story/cascader.stories.js +73 -0
  4. package/cascader/index.tsx +26 -5
  5. package/cascader/item.tsx +25 -5
  6. package/datePicker/_story/v2/FixInputRangeFocus.jsx +25 -0
  7. package/datePicker/_story/v2/index.js +2 -1
  8. package/datePicker/dateInput.tsx +8 -5
  9. package/datePicker/datePicker.tsx +9 -1
  10. package/datePicker/month.tsx +14 -7
  11. package/datePicker/monthsGrid.tsx +17 -5
  12. package/datePicker/navigation.tsx +8 -4
  13. package/datePicker/quickControl.tsx +1 -0
  14. package/datePicker/yearAndMonth.tsx +1 -1
  15. package/dist/css/semi.css +71 -35
  16. package/dist/css/semi.min.css +1 -1
  17. package/dist/umd/semi-ui.js +696 -263
  18. package/dist/umd/semi-ui.js.map +1 -1
  19. package/dist/umd/semi-ui.min.js +1 -1
  20. package/dist/umd/semi-ui.min.js.map +1 -1
  21. package/form/__test__/formApi.test.js +182 -0
  22. package/form/_story/FormApi/arrayDemo.jsx +4 -7
  23. package/form/_story/Layout/slotDemo.jsx +2 -2
  24. package/form/_story/demo.jsx +18 -1
  25. package/form/_story/form.stories.js +6 -6
  26. package/form/baseForm.tsx +2 -2
  27. package/form/hoc/withField.tsx +1 -1
  28. package/lib/cjs/_base/base.css +5 -5
  29. package/lib/cjs/autoComplete/index.d.ts +1 -1
  30. package/lib/cjs/calendar/monthCalendar.js +21 -5
  31. package/lib/cjs/cascader/index.d.ts +9 -2
  32. package/lib/cjs/cascader/index.js +20 -1
  33. package/lib/cjs/cascader/item.d.ts +6 -2
  34. package/lib/cjs/cascader/item.js +33 -4
  35. package/lib/cjs/datePicker/dateInput.d.ts +0 -2
  36. package/lib/cjs/datePicker/dateInput.js +17 -6
  37. package/lib/cjs/datePicker/datePicker.js +19 -12
  38. package/lib/cjs/datePicker/month.d.ts +1 -0
  39. package/lib/cjs/datePicker/month.js +18 -2
  40. package/lib/cjs/datePicker/monthsGrid.js +16 -4
  41. package/lib/cjs/datePicker/navigation.js +8 -0
  42. package/lib/cjs/datePicker/quickControl.js +1 -0
  43. package/lib/cjs/datePicker/yearAndMonth.js +1 -0
  44. package/lib/cjs/dropdown/index.d.ts +1 -1
  45. package/lib/cjs/form/baseForm.d.ts +1 -1
  46. package/lib/cjs/form/baseForm.js +2 -2
  47. package/lib/cjs/form/field.d.ts +1 -1
  48. package/lib/cjs/form/hoc/withField.js +1 -1
  49. package/lib/cjs/scrollList/scrollItem.d.ts +2 -1
  50. package/lib/cjs/scrollList/scrollItem.js +13 -3
  51. package/lib/cjs/select/index.d.ts +3 -3
  52. package/lib/cjs/select/index.js +32 -28
  53. package/lib/cjs/select/option.js +2 -2
  54. package/lib/cjs/select/virtualRow.js +2 -2
  55. package/lib/cjs/table/Table.d.ts +1 -1
  56. package/lib/cjs/table/Table.js +8 -2
  57. package/lib/cjs/table/interface.d.ts +1 -0
  58. package/lib/cjs/tabs/interface.d.ts +1 -1
  59. package/lib/cjs/tooltip/index.d.ts +1 -1
  60. package/lib/cjs/tooltip/index.js +6 -2
  61. package/lib/cjs/tree/index.d.ts +2 -0
  62. package/lib/cjs/tree/index.js +15 -8
  63. package/lib/cjs/treeSelect/index.d.ts +2 -0
  64. package/lib/cjs/treeSelect/index.js +64 -27
  65. package/lib/cjs/upload/fileCard.js +31 -22
  66. package/lib/cjs/upload/index.d.ts +6 -0
  67. package/lib/cjs/upload/index.js +15 -8
  68. package/lib/cjs/upload/interface.d.ts +8 -6
  69. package/lib/es/_base/base.css +5 -5
  70. package/lib/es/autoComplete/index.d.ts +1 -1
  71. package/lib/es/calendar/monthCalendar.js +22 -5
  72. package/lib/es/cascader/index.d.ts +9 -2
  73. package/lib/es/cascader/index.js +19 -1
  74. package/lib/es/cascader/item.d.ts +6 -2
  75. package/lib/es/cascader/item.js +31 -4
  76. package/lib/es/datePicker/dateInput.d.ts +0 -2
  77. package/lib/es/datePicker/dateInput.js +17 -6
  78. package/lib/es/datePicker/datePicker.js +19 -12
  79. package/lib/es/datePicker/month.d.ts +1 -0
  80. package/lib/es/datePicker/month.js +18 -2
  81. package/lib/es/datePicker/monthsGrid.js +16 -4
  82. package/lib/es/datePicker/navigation.js +8 -0
  83. package/lib/es/datePicker/quickControl.js +2 -0
  84. package/lib/es/datePicker/yearAndMonth.js +1 -0
  85. package/lib/es/dropdown/index.d.ts +1 -1
  86. package/lib/es/form/baseForm.d.ts +1 -1
  87. package/lib/es/form/baseForm.js +2 -2
  88. package/lib/es/form/field.d.ts +1 -1
  89. package/lib/es/form/hoc/withField.js +1 -1
  90. package/lib/es/scrollList/scrollItem.d.ts +2 -1
  91. package/lib/es/scrollList/scrollItem.js +13 -3
  92. package/lib/es/select/index.d.ts +3 -3
  93. package/lib/es/select/index.js +30 -26
  94. package/lib/es/select/option.js +2 -2
  95. package/lib/es/select/virtualRow.js +2 -2
  96. package/lib/es/table/Table.d.ts +1 -1
  97. package/lib/es/table/Table.js +10 -2
  98. package/lib/es/table/interface.d.ts +1 -0
  99. package/lib/es/tabs/interface.d.ts +1 -1
  100. package/lib/es/tooltip/index.d.ts +1 -1
  101. package/lib/es/tooltip/index.js +6 -2
  102. package/lib/es/tree/index.d.ts +2 -0
  103. package/lib/es/tree/index.js +15 -8
  104. package/lib/es/treeSelect/index.d.ts +2 -0
  105. package/lib/es/treeSelect/index.js +64 -27
  106. package/lib/es/upload/fileCard.js +31 -24
  107. package/lib/es/upload/index.d.ts +6 -0
  108. package/lib/es/upload/index.js +14 -8
  109. package/lib/es/upload/interface.d.ts +8 -6
  110. package/package.json +9 -9
  111. package/scrollList/_story/ScrollList/index.js +3 -0
  112. package/scrollList/_story/WheelList/index.js +3 -0
  113. package/scrollList/scrollItem.tsx +30 -9
  114. package/select/index.tsx +18 -19
  115. package/select/option.tsx +2 -2
  116. package/select/virtualRow.tsx +2 -2
  117. package/table/Table.tsx +7 -2
  118. package/table/_story/Perf/Virtualized/index.jsx +6 -0
  119. package/table/_story/table.stories.js +1 -2
  120. package/table/_story/v2/FixedHeaderMerge/index.jsx +98 -0
  121. package/table/_story/v2/FixedResizable/index.jsx +114 -0
  122. package/table/_story/v2/defaultFilteredValue.tsx +114 -0
  123. package/table/_story/v2/index.js +5 -0
  124. package/table/interface.ts +1 -0
  125. package/tabs/interface.ts +1 -1
  126. package/tooltip/__test__/tooltip.test.js +48 -4
  127. package/tooltip/_story/tooltip.stories.js +83 -1
  128. package/tooltip/index.tsx +4 -4
  129. package/tree/__test__/treeMultiple.test.js +94 -0
  130. package/tree/_story/tree.stories.js +169 -0
  131. package/tree/index.tsx +12 -5
  132. package/treeSelect/__test__/treeMultiple.test.js +94 -0
  133. package/treeSelect/_story/treeSelect.stories.js +242 -0
  134. package/treeSelect/index.tsx +72 -40
  135. package/upload/_story/upload.stories.js +22 -6
  136. package/upload/fileCard.tsx +23 -23
  137. package/upload/index.tsx +15 -6
  138. package/upload/interface.ts +7 -5
@@ -0,0 +1,114 @@
1
+ import React, { useState, useEffect, useMemo } from 'react';
2
+ import { Table, Avatar, Button } from '@douyinfe/semi-ui';
3
+ import * as dateFns from 'date-fns';
4
+ import { ColumnProps, ChangeInfoFilter } from '@douyinfe/semi-ui/table';
5
+
6
+ const DAY = 24 * 60 * 60 * 1000;
7
+ const figmaIconUrl = 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/figma-icon.png';
8
+
9
+ function App() {
10
+ const [dataSource, setData] = useState([]);
11
+ const [filteredValue, setFilteredValue] = useState(['Semi Pro 设计稿']);
12
+
13
+ const scroll = useMemo(() => ({ y: 300 }), []);
14
+
15
+ const columns: ColumnProps[] = [
16
+ {
17
+ title: '标题',
18
+ dataIndex: 'name',
19
+ width: 400,
20
+ render: (text, record, index) => {
21
+ return (
22
+ <div>
23
+ <Avatar size="small" shape="square" src={figmaIconUrl} style={{ marginRight: 12 }}></Avatar>
24
+ {text}
25
+ </div>
26
+ );
27
+ },
28
+ filters: [
29
+ {
30
+ text: 'Semi Design 设计稿',
31
+ value: 'Semi Design 设计稿',
32
+ },
33
+ {
34
+ text: 'Semi Pro 设计稿',
35
+ value: 'Semi Pro 设计稿',
36
+ },
37
+ ],
38
+ onFilter: (value, record) => record.name.includes(value),
39
+ sorter: (a, b) => a.name.length - b.name.length > 0 ? 1 : -1,
40
+ // filterMultiple: false,
41
+ // filteredValue: filteredValue,
42
+ defaultFilteredValue: filteredValue,
43
+ },
44
+ {
45
+ title: '大小',
46
+ dataIndex: 'size',
47
+ sorter: (a, b) => a.size - b.size > 0 ? 1 : -1,
48
+ render: (text) => `${text} KB`
49
+ },
50
+ {
51
+ title: '所有者',
52
+ dataIndex: 'owner',
53
+ render: (text, record, index) => {
54
+ return (
55
+ <div>
56
+ <Avatar size="small" color={record.avatarBg} style={{ marginRight: 4 }}>{typeof text === 'string' && text.slice(0, 1)}</Avatar>
57
+ {text}
58
+ </div>
59
+ );
60
+ }
61
+
62
+ }
63
+ ];
64
+
65
+ const getData = (total) => {
66
+ const data = [];
67
+ for (let i = 0; i < total; i++) {
68
+ const isSemiDesign = i % 2 === 0;
69
+ const randomNumber = (i * 1000) % 199;
70
+ data.push({
71
+ key: '' + i,
72
+ name: isSemiDesign ? `Semi Design 设计稿${i}.fig` : `Semi Pro 设计稿${i}.fig`,
73
+ owner: isSemiDesign ? '姜鹏志' : '郝宣',
74
+ size: randomNumber,
75
+ avatarBg: isSemiDesign ? 'grey' : 'red'
76
+ });
77
+ }
78
+ return data;
79
+ };
80
+
81
+ const handleFilterChange = (filters: ChangeInfoFilter<any>[]) => {
82
+ console.log('filters', filters);
83
+ if (Array.isArray(filters) && filters.length) {
84
+ const { filteredValue } = filters.find(filter => filter.dataIndex === 'name');
85
+ setFilteredValue(filteredValue);
86
+ }
87
+ };
88
+
89
+ const handleChange = (options) => {
90
+ const { filters } = options;
91
+ handleFilterChange(filters);
92
+ };
93
+
94
+ const toggleChangeData = () => {
95
+ const length = dataSource.length;
96
+ const newData = getData(length === 46 ? 25 : 46);
97
+ setData(newData);
98
+ };
99
+
100
+ useEffect(() => {
101
+ const data = getData(46);
102
+ setData(data);
103
+ }, []);
104
+
105
+ return (
106
+ <div>
107
+ <Button onClick={toggleChangeData}>toggle change dataSource (46/25)</Button>
108
+ <Table columns={columns} dataSource={dataSource} scroll={scroll} onChange={handleChange} />
109
+ </div>
110
+ );
111
+ }
112
+
113
+ App.storyName = 'defaultFilteredValue';
114
+ export default App;
@@ -0,0 +1,5 @@
1
+ export { default as DefaultFilteredValue } from './defaultFilteredValue';
2
+ export { default as FixedColumnsChange } from './FixedColumnsChange';
3
+ export { default as FixedZIndex } from './FixedZIndex';
4
+ export { default as FixedHeaderMerge } from './FixedHeaderMerge';
5
+ export { default as FixedResizable } from './FixedResizable';
@@ -79,6 +79,7 @@ export interface ColumnProps<RecordType extends Record<string, any> = any> {
79
79
  className?: string;
80
80
  colSpan?: number;
81
81
  dataIndex?: string;
82
+ defaultFilteredValue?: any[];
82
83
  defaultSortOrder?: SortOrder;
83
84
  filterChildrenRecord?: boolean;
84
85
  filterDropdown?: React.ReactNode;
package/tabs/interface.ts CHANGED
@@ -58,7 +58,7 @@ export interface TabBarProps {
58
58
  export interface TabPaneProps {
59
59
  className?: string;
60
60
  disabled?: boolean;
61
- icon?: string;
61
+ icon?: ReactNode;
62
62
  itemKey?: string;
63
63
  style?: CSSProperties;
64
64
  tab?: ReactNode;
@@ -72,8 +72,8 @@ describe(`Tooltip`, () => {
72
72
  expect(elem.state(`visible`)).toBe(true);
73
73
 
74
74
  // click outside
75
- document.body.click();
76
- // document.dispatchEvent(new Event('mousedown', { bubbles: true, cancelable: true }));
75
+ // document.body.click();
76
+ document.dispatchEvent(new Event('mousedown', { bubbles: true, cancelable: true }));
77
77
  // demo.find(`#${triggerId}`)
78
78
  // .at(0)
79
79
  // .simulate(`mouseDown`);
@@ -88,7 +88,8 @@ describe(`Tooltip`, () => {
88
88
  // unmount elem
89
89
  demo.unmount();
90
90
  await sleep(100);
91
- document.body.click();
91
+ // document.body.click();
92
+ document.dispatchEvent(new Event('mousedown', { bubbles: true, cancelable: true }));
92
93
  expect(document.getElementsByClassName(`${BASE_CLASS_PREFIX}-tooltip-wrapper`).length).toBe(0);
93
94
  });
94
95
 
@@ -165,7 +166,8 @@ describe(`Tooltip`, () => {
165
166
  expect(refFn.called).toBeTruthy();
166
167
 
167
168
  // click outside
168
- document.body.click();
169
+ // document.body.click();
170
+ document.dispatchEvent(new Event('mousedown', { bubbles: true, cancelable: true }));
169
171
  await sleep(100);
170
172
  expect(
171
173
  demo
@@ -290,6 +292,48 @@ describe(`Tooltip`, () => {
290
292
  expect(document.querySelector(`.${BASE_CLASS_PREFIX}-tooltip-wrapper`).getAttribute('x-placement')).toBe(position);
291
293
  }
292
294
  });
295
+
296
+ it(`test click outside handler`, async () => {
297
+ const containerId = `container`;
298
+
299
+ const demo = mount(
300
+ <div style={{ height: 480, width: 320 }}>
301
+ <div id={containerId}>Hello Semi</div>
302
+ <Tooltip
303
+ content='Content'
304
+ trigger='click'
305
+ >
306
+ <Button >Click here</Button>
307
+ </Tooltip>
308
+ </div>
309
+ );
310
+
311
+ const toolTipElem = demo.find(Tooltip);
312
+ const buttonElem = demo.find(Button);
313
+ // click inside
314
+ buttonElem.simulate('click');
315
+ toolTipElem.update();
316
+ await sleep(100);
317
+ expect(toolTipElem.state(`visible`)).toBe(true);
318
+
319
+ // click outside
320
+ // document.body.click();
321
+ document.dispatchEvent(new Event('mousedown', { bubbles: true, cancelable: true }));
322
+ toolTipElem.update();
323
+ await sleep(100);
324
+ expect(toolTipElem.state('visible')).toBe(false);
325
+
326
+ // click button to show tooltip
327
+ buttonElem.simulate('click');
328
+ toolTipElem.update();
329
+ await sleep(100);
330
+ expect(toolTipElem.state('visible')).toBe(true);
331
+
332
+ document.getElementById(containerId).dispatchEvent(new Event('mousedown', { bubbles: true, cancelable: true }));
333
+ toolTipElem.update();
334
+ await sleep(100);
335
+ expect(toolTipElem.state('visible')).toBe(false);
336
+ });
293
337
  });
294
338
 
295
339
  it('wrapperClassName', () => {
@@ -1,7 +1,7 @@
1
1
  import React, { useState, useMemo } from 'react';
2
2
  import Tooltip from '../index';
3
3
  import './story.scss';
4
- import { Tag, Icon, IconButton, Switch, Checkbox, Radio, Button, Select, InputNumber } from '@douyinfe/semi-ui';
4
+ import { Tag, Icon, IconButton, Switch, Checkbox, Radio, Button, Select, InputNumber, Space } from '@douyinfe/semi-ui';
5
5
 
6
6
  import InTableDemo from './InTable';
7
7
  import EdgeDemo from './Edge';
@@ -736,3 +736,85 @@ export const AutoAdjustWithSpacing = () => {
736
736
  AutoAdjustWithSpacing.story = {
737
737
  name: 'AutoAdjustWithSpacing',
738
738
  };
739
+
740
+ /**
741
+ * Chromatic UI test
742
+ */
743
+ export const leftTopOverDemo = () => {
744
+ const [visible, setVisible] = useState(true);
745
+ const content = (
746
+ <div style={{ height: 200, width: 200 }}>
747
+ Semi Design
748
+ </div>
749
+ );
750
+
751
+ const commonProps = {
752
+ content,
753
+ trigger: 'click',
754
+ showArrow: false,
755
+ visible,
756
+ trigger: 'custom',
757
+ motion: false,
758
+ };
759
+ const buttonStyle = {
760
+ width: 200,
761
+ };
762
+
763
+ return (
764
+ <div data-cy='wrapper'>
765
+ <Button onClick={() => setVisible(!visible)}>toggle visible</Button>
766
+ <div style={{ paddingTop: 200 }}>
767
+ <Space spacing={80}>
768
+ <Tooltip {...commonProps} position='leftTopOver' >
769
+ <Button data-cy='leftTopOver' style={buttonStyle}>leftTopOver</Button>
770
+ </Tooltip>
771
+ <Tooltip {...commonProps} position='leftBottomOver'>
772
+ <Button data-cy='leftBottomOver' style={buttonStyle}>leftBottomOver</Button>
773
+ </Tooltip>
774
+ <Tooltip {...commonProps} position='rightTopOver'>
775
+ <Button data-cy='rightTopOver' style={buttonStyle}>rightTopOver</Button>
776
+ </Tooltip>
777
+ <Tooltip {...commonProps} position='rightBottomOver'>
778
+ <Button data-cy='rightBottomOver' style={buttonStyle}>rightBottomOver</Button>
779
+ </Tooltip>
780
+ </Space>
781
+ </div>
782
+ </div>
783
+ )
784
+ };
785
+ leftTopOverDemo.storyName = `leftTopOver visible`;
786
+ leftTopOverDemo.parameters = {
787
+ chromatic: {
788
+ disableSnapshot: false,
789
+ delay: 3000,
790
+ viewports: [1200]
791
+ }
792
+ };
793
+
794
+ /**
795
+ * Cypress test
796
+ */
797
+ export const leftTopOverAutoAdjustOverflow = () => {
798
+ const content = (
799
+ <div style={{ height: 200, width: 200 }}>
800
+ Semi Design
801
+ </div>
802
+ );
803
+
804
+ const commonProps = {
805
+ content,
806
+ trigger: 'click',
807
+ showArrow: false,
808
+ };
809
+
810
+ return (
811
+ <div data-cy='wrapper' style={{ width: '200vw', height: '200vw', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
812
+ <div>
813
+ <Tooltip {...commonProps} position='leftTopOver' >
814
+ <Button data-cy='leftTopOver' style={{ width: 200 }}>leftTopOver</Button>
815
+ </Tooltip>
816
+ </div>
817
+ </div>
818
+ )
819
+ };
820
+ leftTopOverAutoAdjustOverflow.storyName = `leftTopOver autoAdjustOverflow`;
package/tooltip/index.tsx CHANGED
@@ -280,7 +280,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
280
280
  getDocumentElementBounding: () => document.documentElement.getBoundingClientRect(),
281
281
  setPosition: ({ position, ...style }: { position: Position }) => {
282
282
  this.setState(
283
- {
283
+ {
284
284
  containerStyle: { ...this.state.containerStyle, ...style },
285
285
  placement: position,
286
286
  isPositionUpdated: true
@@ -326,11 +326,11 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
326
326
  cb();
327
327
  }
328
328
  };
329
- document.addEventListener('click', this.clickOutsideHandler, false);
329
+ document.addEventListener('mousedown', this.clickOutsideHandler, { capture: true });
330
330
  },
331
331
  unregisterClickOutsideHandler: () => {
332
332
  if (this.clickOutsideHandler) {
333
- document.removeEventListener('click', this.clickOutsideHandler, false);
333
+ document.removeEventListener('mousedown', this.clickOutsideHandler, { capture: true });
334
334
  this.clickOutsideHandler = null;
335
335
  }
336
336
  },
@@ -661,4 +661,4 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
661
661
  }
662
662
  }
663
663
 
664
- export { Position };
664
+ export { Position };
@@ -357,6 +357,100 @@ describe('Tree', () => {
357
357
  ).toEqual(true);
358
358
  });
359
359
 
360
+ it('unRelated', () => {
361
+ const spyOnChange = sinon.spy(() => { });
362
+ const tree = getTree({
363
+ defaultExpandAll: true,
364
+ onChange: spyOnChange,
365
+ checkRelation: 'unRelated',
366
+ });
367
+ const nodelevel2 = tree.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`);
368
+ const selectedNode = nodelevel2.at(0);
369
+ selectedNode.simulate('click');
370
+ expect(spyOnChange.calledOnce).toBe(true);
371
+ expect(spyOnChange.calledWithMatch(['Zhongguo'])).toEqual(true);
372
+ // Note: selectedNode cannot be used directly here. selectedNode is the original node in the unselected state
373
+ expect(
374
+ tree
375
+ .find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`)
376
+ .at(0)
377
+ .exists(`.${BASE_CLASS_PREFIX}-checkbox-checked`)
378
+ ).toEqual(true);
379
+ const nodelevel3 = tree.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`);
380
+ expect(
381
+ nodelevel3
382
+ .exists(`.${BASE_CLASS_PREFIX}-checkbox-unChecked` )
383
+ ).toEqual(true);
384
+ expect(
385
+ nodelevel3
386
+ .at(1)
387
+ .exists(`.${BASE_CLASS_PREFIX}-checkbox-unChecked` )
388
+ ).toEqual(true);
389
+ });
390
+
391
+ it('unRelated + value', () => {
392
+ const tree = getTree({
393
+ defaultExpandAll: true,
394
+ checkRelation: 'unRelated',
395
+ value: 'Zhongguo'
396
+ });
397
+ expect(
398
+ tree
399
+ .find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`)
400
+ .at(0)
401
+ .exists(`.${BASE_CLASS_PREFIX}-checkbox-checked`)
402
+ ).toEqual(true);
403
+ const nodelevel3 = tree.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`);
404
+ expect(
405
+ nodelevel3
406
+ .exists(`.${BASE_CLASS_PREFIX}-checkbox-unChecked` )
407
+ ).toEqual(true);
408
+ expect(
409
+ nodelevel3
410
+ .at(1)
411
+ .exists(`.${BASE_CLASS_PREFIX}-checkbox-unChecked` )
412
+ ).toEqual(true);
413
+ });
414
+
415
+ it('unRelated + defaultValue', () => {
416
+ const tree = getTree({
417
+ defaultExpandAll: true,
418
+ checkRelation: 'unRelated',
419
+ defaultValue: 'Zhongguo'
420
+ });
421
+ expect(
422
+ tree
423
+ .find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`)
424
+ .at(0)
425
+ .exists(`.${BASE_CLASS_PREFIX}-checkbox-checked`)
426
+ ).toEqual(true);
427
+ const nodelevel3 = tree.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`);
428
+ expect(
429
+ nodelevel3
430
+ .exists(`.${BASE_CLASS_PREFIX}-checkbox-unChecked` )
431
+ ).toEqual(true);
432
+ expect(
433
+ nodelevel3
434
+ .at(1)
435
+ .exists(`.${BASE_CLASS_PREFIX}-checkbox-unChecked` )
436
+ ).toEqual(true);
437
+ });
438
+
439
+ it('unRelated + onSelect', () => {
440
+ const spyOnSelect = sinon.spy(() => { });
441
+ const tree = getTree({
442
+ defaultExpandAll: true,
443
+ onSelect: spyOnSelect,
444
+ checkRelation: 'unRelated',
445
+ });
446
+ const nodelevel2 = tree.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`);
447
+ const selectedNode = nodelevel2.at(0);
448
+ selectedNode.simulate('click');
449
+ expect(spyOnSelect.calledOnce).toBe(true);
450
+ // onSelect first args is key, not value
451
+ expect(spyOnSelect.calledWithMatch('zhongguo')).toEqual(true);
452
+ });
453
+
360
454
  it('controlled: leaf values show correct', () => {
361
455
  let tree = getTree({
362
456
  value: 'Beijing'
@@ -2170,3 +2170,172 @@ export const RenderFullLabelWithDraggable = () => {
2170
2170
  RenderFullLabelWithDraggable.story = {
2171
2171
  name: 'renderFullLabel with draggable',
2172
2172
  };
2173
+
2174
+ export const CheckRelationDemo = () => {
2175
+ const treeData = [
2176
+ {
2177
+ label: 'Asia',
2178
+ value: 'Asia',
2179
+ key: '0',
2180
+ children: [
2181
+ {
2182
+ label: 'China',
2183
+ value: 'China',
2184
+ key: '0-0',
2185
+ children: [
2186
+ {
2187
+ label: 'Beijing',
2188
+ value: 'Beijing',
2189
+ key: '0-0-0',
2190
+ },
2191
+ {
2192
+ label: 'Shanghai',
2193
+ value: 'Shanghai',
2194
+ key: '0-0-1',
2195
+ },
2196
+ {
2197
+ label: 'Chengdu',
2198
+ value: 'Chengdu',
2199
+ key: '0-0-2',
2200
+ },
2201
+ ],
2202
+ },
2203
+ {
2204
+ label: 'Japan',
2205
+ value: 'Japan',
2206
+ key: '0-1',
2207
+ children: [
2208
+ {
2209
+ label: 'Osaka',
2210
+ value: 'Osaka',
2211
+ key: '0-1-0'
2212
+ }
2213
+ ]
2214
+ },
2215
+ ],
2216
+ },
2217
+ {
2218
+ label: 'North America',
2219
+ value: 'North America',
2220
+ key: '1',
2221
+ children: [
2222
+ {
2223
+ label: 'United States',
2224
+ value: 'United States',
2225
+ key: '1-0'
2226
+ },
2227
+ {
2228
+ label: 'Canada',
2229
+ value: 'Canada',
2230
+ key: '1-1'
2231
+ }
2232
+ ]
2233
+ }
2234
+ ];
2235
+ const [value, setValue] = useState('China');
2236
+ const [value2, setValue2] = useState();
2237
+ const [value3, setValue3] = useState();
2238
+ const style = {
2239
+ width: 260,
2240
+ height: 420,
2241
+ border: '1px solid var(--semi-color-border)'
2242
+ };
2243
+ const handleChange = value => {
2244
+ console.log(value);
2245
+ setValue(value);
2246
+ };
2247
+ const handleChange2 = value => {
2248
+ console.log(value);
2249
+ setValue2(value);
2250
+ };
2251
+ const handleChange3 = value => {
2252
+ console.log(value);
2253
+ setValue3(value);
2254
+ };
2255
+ return (
2256
+ <>
2257
+ <div>checkRelation='unRelated'</div>
2258
+ <Tree
2259
+ treeData={treeData}
2260
+ multiple
2261
+ checkRelation='unRelated'
2262
+ defaultExpandAll
2263
+ style={style}
2264
+ />
2265
+ <br /><br />
2266
+ <div>checkRelation='unRelated' + 中国节点为 disabled</div>
2267
+ <Tree
2268
+ treeData={treeData1}
2269
+ multiple
2270
+ checkRelation='unRelated'
2271
+ defaultExpandAll
2272
+ style={style}
2273
+ />
2274
+ <br /><br />
2275
+ <div>checkRelation='unRelated' + 中国节点为 disabled + 严格禁用</div>
2276
+ <Tree
2277
+ treeData={treeData1}
2278
+ multiple
2279
+ checkRelation='unRelated'
2280
+ defaultExpandAll
2281
+ disableStrictly
2282
+ style={style}
2283
+ />
2284
+ <br /><br />
2285
+ <div>checkRelation='unRelated' + defaultValue 为 China</div>
2286
+ <Tree
2287
+ treeData={treeData}
2288
+ multiple
2289
+ checkRelation='unRelated'
2290
+ defaultExpandAll
2291
+ style={style}
2292
+ defaultValue='China'
2293
+ />
2294
+ <br /><br />
2295
+ <div>checkRelation='unRelated' + 受控 + value 初始为 China</div>
2296
+ <Tree
2297
+ treeData={treeData}
2298
+ multiple
2299
+ checkRelation='unRelated'
2300
+ defaultExpandAll
2301
+ style={style}
2302
+ value={value}
2303
+ onChange={handleChange}
2304
+ />
2305
+ <br /><br />
2306
+ <div>checkRelation='unRelated' + 受控 + onChangeWithObject</div>
2307
+ <Tree
2308
+ treeData={treeData}
2309
+ multiple
2310
+ checkRelation='unRelated'
2311
+ defaultExpandAll
2312
+ style={style}
2313
+ value={value2}
2314
+ onChangeWithObject
2315
+ onChange={handleChange2}
2316
+ />
2317
+ <br /><br />
2318
+ <div>checkRelation='unRelated' + 受控 + leafOnly,此时 leafOnly 失效</div>
2319
+ <Tree
2320
+ leafOnly
2321
+ treeData={treeData}
2322
+ multiple
2323
+ checkRelation='unRelated'
2324
+ defaultExpandAll
2325
+ style={style}
2326
+ value={value3}
2327
+ onChange={handleChange3}
2328
+ />
2329
+ <br /><br />
2330
+ <div>checkRelation='unRelated' + onSelect </div>
2331
+ <Tree
2332
+ treeData={treeData}
2333
+ multiple
2334
+ checkRelation='unRelated'
2335
+ defaultExpandAll
2336
+ style={style}
2337
+ onSelect={(value,status,node)=>console.log('select', value, status, node)}
2338
+ />
2339
+ </>
2340
+ );
2341
+ };
package/tree/index.tsx CHANGED
@@ -112,6 +112,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
112
112
  onDragStart: PropTypes.func,
113
113
  onDrop: PropTypes.func,
114
114
  labelEllipsis: PropTypes.bool,
115
+ checkRelation: PropTypes.string,
115
116
  'aria-label': PropTypes.string,
116
117
  };
117
118
 
@@ -133,6 +134,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
133
134
  disableStrictly: false,
134
135
  draggable: false,
135
136
  autoExpandWhenDragEnter: true,
137
+ checkRelation: 'related',
136
138
  };
137
139
 
138
140
  static TreeNode: typeof TreeNode;
@@ -152,6 +154,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
152
154
  selectedKeys: [],
153
155
  checkedKeys: new Set(),
154
156
  halfCheckedKeys: new Set(),
157
+ realCheckedKeys: new Set([]),
155
158
  motionKeys: new Set([]),
156
159
  motionType: 'hide',
157
160
  expandedKeys: new Set(props.expandedKeys),
@@ -409,10 +412,14 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
409
412
  }
410
413
 
411
414
  if (checkedKeyValues) {
412
- const { checkedKeys, halfCheckedKeys } = calcCheckedKeys(checkedKeyValues, keyEntities);
415
+ if (props.checkRelation === 'unRelated') {
416
+ newState.realCheckedKeys = new Set(checkedKeyValues);
417
+ } else if (props.checkRelation === 'related') {
418
+ const { checkedKeys, halfCheckedKeys } = calcCheckedKeys(checkedKeyValues, keyEntities);
413
419
 
414
- newState.checkedKeys = checkedKeys;
415
- newState.halfCheckedKeys = halfCheckedKeys;
420
+ newState.checkedKeys = checkedKeys;
421
+ newState.halfCheckedKeys = halfCheckedKeys;
422
+ }
416
423
  }
417
424
  }
418
425
 
@@ -422,7 +429,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
422
429
  }
423
430
 
424
431
  // update disableStrictly
425
- if (treeData && props.disableStrictly) {
432
+ if (treeData && props.disableStrictly && props.checkRelation === 'related') {
426
433
  newState.disabledKeys = calcDisabledKeys(keyEntities);
427
434
  }
428
435
 
@@ -706,7 +713,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
706
713
  const ariaAttr = {
707
714
  role: noData ? 'none' : 'tree'
708
715
  };
709
- if (ariaAttr.role === 'tree'){
716
+ if (ariaAttr.role === 'tree') {
710
717
  ariaAttr['aria-multiselectable'] = multiple ? true : false;
711
718
  }
712
719
  return (