5
5
* LICENSE file in the root directory of this source tree.
6
6
*/
7
7
8
- import React , { useState , useEffect , useRef } from 'react' ;
9
- import PropTypes from 'prop-types' ;
10
- import { CaretDown } from '@carbon/icons-react' ;
8
+ import { CarbonIconType , CaretDown } from '@carbon/icons-react' ;
11
9
import classNames from 'classnames' ;
10
+ import PropTypes from 'prop-types' ;
11
+ import React , { useEffect , useRef , useState } from 'react' ;
12
12
import { keys , match , matches } from '../../internal/keyboard' ;
13
- import uniqueId from '../../tools/uniqueId' ;
14
- import { usePrefix } from '../../internal/usePrefix' ;
15
13
import { useControllableState } from '../../internal/useControllableState' ;
14
+ import { usePrefix } from '../../internal/usePrefix' ;
15
+ import uniqueId from '../../tools/uniqueId' ;
16
16
import { useFeatureFlag } from '../FeatureFlags' ;
17
17
18
- const TreeNode = React . forwardRef (
18
+ export type TreeNodeProps = {
19
+ /**
20
+ * **Note:** this is controlled by the parent TreeView component, do not set manually.
21
+ * The ID of the active node in the tree
22
+ */
23
+ active ?: string | number ;
24
+ children ?: React . ReactNode ;
25
+ className ?: string ;
26
+ /**
27
+ * **[Experimental]** The default expansion state of the node.
28
+ * *This is only supported with the `enable-treeview-controllable` feature flag!*
29
+ */
30
+ defaultIsExpanded ?: boolean ;
31
+ /**
32
+ * **Note:** this is controlled by the parent TreeView component, do not set manually.
33
+ * TreeNode depth to determine spacing
34
+ */
35
+ depth ?: number ;
36
+ disabled ?: boolean ;
37
+ id ?: string ;
38
+ isExpanded ?: boolean ;
39
+ label : React . ReactNode ;
40
+ onNodeFocusEvent ?: ( event : React . FocusEvent < HTMLLIElement > ) => void ;
41
+ onSelect ?: ( event : React . MouseEvent , node ?: TreeNodeProps ) => void ;
42
+ onToggle ?: ( event : React . MouseEvent , node ?: TreeNodeProps ) => void ;
43
+ onTreeSelect ?: ( event : React . MouseEvent , node ?: TreeNodeProps ) => void ;
44
+ renderIcon ?: CarbonIconType ;
45
+ /**
46
+ * **Note:** this is controlled by the parent TreeView component, do not set manually.
47
+ * Array containing all selected node IDs in the tree
48
+ */
49
+ selected ?: Array < string | number > ;
50
+ value ?: string ;
51
+ } & Omit < React . LiHTMLAttributes < HTMLLIElement > , 'onSelect' > ;
52
+
53
+ const TreeNode = React . forwardRef < HTMLLIElement , TreeNodeProps > (
19
54
(
20
55
{
21
56
active,
22
57
children,
23
58
className,
24
- depth,
59
+ depth : propDepth ,
25
60
disabled,
26
61
id : nodeId ,
27
62
isExpanded,
@@ -32,12 +67,14 @@ const TreeNode = React.forwardRef(
32
67
onToggle,
33
68
onTreeSelect,
34
69
renderIcon : Icon ,
35
- selected,
70
+ selected : propSelected ,
36
71
value,
37
72
...rest
38
73
} ,
39
74
ref
40
75
) => {
76
+ const depth = propDepth as number ;
77
+ const selected = propSelected as ( string | number ) [ ] ;
41
78
const enableTreeviewControllable = useFeatureFlag (
42
79
'enable-treeview-controllable'
43
80
) ;
@@ -54,19 +91,20 @@ const TreeNode = React.forwardRef(
54
91
? controllableExpandedState
55
92
: uncontrollableExpandedState ;
56
93
57
- const currentNode = useRef ( null ) ;
58
- const currentNodeLabel = useRef ( null ) ;
94
+ const currentNode = useRef < HTMLLIElement > ( null ) ;
95
+ const currentNodeLabel = useRef < HTMLDivElement > ( null ) ;
59
96
const prefix = usePrefix ( ) ;
60
97
const nodesWithProps = React . Children . map ( children , ( node ) => {
61
98
if ( React . isValidElement ( node ) ) {
62
99
return React . cloneElement ( node , {
63
100
active,
101
+ // @ts -ignore
64
102
depth : depth + 1 ,
65
103
disabled : disabled || node . props . disabled ,
66
104
onTreeSelect,
67
105
selected,
68
106
tabIndex : ( ! node . props . disabled && - 1 ) || null ,
69
- } ) ;
107
+ } as TreeNodeProps ) ;
70
108
}
71
109
} ) ;
72
110
const isActive = active === id ;
@@ -85,7 +123,7 @@ const TreeNode = React.forwardRef(
85
123
[ `${ prefix } --tree-parent-node__toggle-icon--expanded` ] : expanded ,
86
124
}
87
125
) ;
88
- function handleToggleClick ( event ) {
126
+ function handleToggleClick ( event : React . MouseEvent < HTMLSpanElement > ) {
89
127
if ( disabled ) {
90
128
return ;
91
129
}
@@ -98,12 +136,12 @@ const TreeNode = React.forwardRef(
98
136
}
99
137
setExpanded ( ! expanded ) ;
100
138
}
101
- function handleClick ( event ) {
139
+ function handleClick ( event : React . MouseEvent ) {
102
140
event . stopPropagation ( ) ;
103
141
if ( ! disabled ) {
104
142
onTreeSelect ?.( event , { id, label, value } ) ;
105
143
onNodeSelect ?.( event , { id, label, value } ) ;
106
- rest ?. onClick ?. ( event ) ;
144
+ rest ?. onClick ?.( event as React . MouseEvent < HTMLLIElement > ) ;
107
145
}
108
146
}
109
147
function handleKeyDown ( event ) {
@@ -133,7 +171,7 @@ const TreeNode = React.forwardRef(
133
171
* When focus is on a leaf node or a closed parent node, move focus to
134
172
* its parent node (unless its depth is level 1)
135
173
*/
136
- findParentTreeNode ( currentNode . current . parentNode ) ?. focus ( ) ;
174
+ findParentTreeNode ( currentNode . current ? .parentNode ) ?. focus ( ) ;
137
175
}
138
176
}
139
177
if ( children && match ( event , keys . ArrowRight ) ) {
@@ -142,7 +180,7 @@ const TreeNode = React.forwardRef(
142
180
* When focus is on an expanded parent node, move focus to the first
143
181
* child node
144
182
*/
145
- currentNode . current . lastChild . firstChild . focus ( ) ;
183
+ ( currentNode . current ? .lastChild ? .firstChild as HTMLElement ) . focus ( ) ;
146
184
} else {
147
185
if ( ! enableTreeviewControllable ) {
148
186
onToggle ?.( event , { id, isExpanded : true , label, value } ) ;
@@ -213,19 +251,20 @@ const TreeNode = React.forwardRef(
213
251
setExpanded ,
214
252
] ) ;
215
253
216
- const treeNodeProps = {
254
+ const treeNodeProps : React . LiHTMLAttributes < HTMLLIElement > = {
217
255
...rest ,
218
- [ 'aria-current' ] : isActive || null ,
219
- [ 'aria-selected' ] : disabled ? null : isSelected ,
256
+ [ 'aria-current' ] : isActive || undefined ,
257
+ [ 'aria-selected' ] : disabled ? undefined : isSelected ,
220
258
[ 'aria-disabled' ] : disabled ,
221
259
className : treeNodeClasses ,
222
260
id,
223
261
onBlur : handleFocusEvent ,
224
262
onClick : handleClick ,
225
263
onFocus : handleFocusEvent ,
226
264
onKeyDown : handleKeyDown ,
227
- ref : currentNode ,
228
265
role : 'treeitem' ,
266
+ // @ts -ignore
267
+ ref : currentNode ,
229
268
} ;
230
269
231
270
if ( ! children ) {
@@ -246,6 +285,7 @@ const TreeNode = React.forwardRef(
246
285
{ /* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */ }
247
286
< span
248
287
className = { `${ prefix } --tree-parent-node__toggle` }
288
+ // @ts -ignore
249
289
disabled = { disabled }
250
290
onClick = { handleToggleClick } >
251
291
< CaretDown className = { toggleClasses } />
@@ -338,12 +378,14 @@ TreeNode.propTypes = {
338
378
* Optional prop to allow each node to have an associated icon.
339
379
* Can be a React component class
340
380
*/
381
+ // @ts -ignore
341
382
renderIcon : PropTypes . oneOfType ( [ PropTypes . func , PropTypes . object ] ) ,
342
383
343
384
/**
344
385
* **Note:** this is controlled by the parent TreeView component, do not set manually.
345
386
* Array containing all selected node IDs in the tree
346
387
*/
388
+ // @ts -ignore
347
389
selected : PropTypes . arrayOf (
348
390
PropTypes . oneOfType ( [ PropTypes . string , PropTypes . number ] )
349
391
) ,
0 commit comments