@@ -14,6 +14,7 @@ import React, {
14
14
useEffect ,
15
15
useRef ,
16
16
useState ,
17
+ MutableRefObject ,
17
18
} from 'react' ;
18
19
import { keys , match , matches } from '../../internal/keyboard' ;
19
20
import { useControllableState } from '../../internal/useControllableState' ;
@@ -114,7 +115,7 @@ const TreeNode = React.forwardRef<HTMLLIElement, TreeNodeProps>(
114
115
value,
115
116
...rest
116
117
} ,
117
- ref
118
+ forwardedRef
118
119
) => {
119
120
// These are provided by the parent TreeView component
120
121
const depth = propDepth as number ;
@@ -136,9 +137,20 @@ const TreeNode = React.forwardRef<HTMLLIElement, TreeNodeProps>(
136
137
? controllableExpandedState
137
138
: uncontrollableExpandedState ;
138
139
139
- const currentNode = useRef < HTMLLIElement > ( null ) ;
140
+ const currentNode = useRef < HTMLLIElement | null > ( null ) ;
140
141
const currentNodeLabel = useRef < HTMLDivElement > ( null ) ;
141
142
const prefix = usePrefix ( ) ;
143
+
144
+ const setRefs = ( element : HTMLLIElement | null ) => {
145
+ currentNode . current = element ;
146
+ if ( typeof forwardedRef === 'function' ) {
147
+ forwardedRef ( element ) ;
148
+ } else if ( forwardedRef ) {
149
+ ( forwardedRef as MutableRefObject < HTMLLIElement | null > ) . current =
150
+ element ;
151
+ }
152
+ } ;
153
+
142
154
const nodesWithProps = React . Children . map ( children , ( node ) => {
143
155
if ( React . isValidElement ( node ) ) {
144
156
return React . cloneElement ( node , {
@@ -196,35 +208,38 @@ const TreeNode = React.forwardRef<HTMLLIElement, TreeNodeProps>(
196
208
event . stopPropagation ( ) ;
197
209
}
198
210
if ( match ( event , keys . ArrowLeft ) ) {
199
- const findParentTreeNode = ( node ) => {
211
+ const findParentTreeNode = ( node : Element | null ) : Element | null => {
212
+ if ( ! node ) return null ;
200
213
if ( node . classList . contains ( `${ prefix } --tree-parent-node` ) ) {
201
214
return node ;
202
215
}
203
216
if ( node . classList . contains ( `${ prefix } --tree` ) ) {
204
217
return null ;
205
218
}
206
- return findParentTreeNode ( node . parentNode ) ;
219
+ return findParentTreeNode ( node . parentElement ) ;
207
220
} ;
208
221
if ( children && expanded ) {
209
222
if ( ! enableTreeviewControllable ) {
210
223
onToggle ?.( event , { id, isExpanded : false , label, value } ) ;
211
224
}
212
225
setExpanded ( false ) ;
213
226
} else {
214
- /**
215
- * When focus is on a leaf node or a closed parent node, move focus to
216
- * its parent node (unless its depth is level 1)
217
- */
218
- findParentTreeNode ( currentNode . current ?. parentNode ) ?. focus ( ) ;
227
+ const parentNode = findParentTreeNode (
228
+ currentNode . current ?. parentElement || null
229
+ ) ;
230
+ if ( parentNode instanceof HTMLElement ) {
231
+ parentNode . focus ( ) ;
232
+ }
219
233
}
220
234
}
221
235
if ( children && match ( event , keys . ArrowRight ) ) {
222
236
if ( expanded ) {
223
- /**
224
- * When focus is on an expanded parent node, move focus to the first
225
- * child node
226
- */
227
- ( currentNode . current ?. lastChild ?. firstChild as HTMLElement ) . focus ( ) ;
237
+ const firstChildElement = currentNode . current ?. querySelector (
238
+ '[role="treeitem"]'
239
+ ) as HTMLElement | null ;
240
+ if ( firstChildElement ) {
241
+ firstChildElement . focus ( ) ;
242
+ }
228
243
} else {
229
244
if ( ! enableTreeviewControllable ) {
230
245
onToggle ?.( event , { id, isExpanded : true , label, value } ) ;
@@ -307,13 +322,11 @@ const TreeNode = React.forwardRef<HTMLLIElement, TreeNodeProps>(
307
322
onFocus : handleFocusEvent ,
308
323
onKeyDown : handleKeyDown ,
309
324
role : 'treeitem' ,
310
- // @ts -ignore
311
- ref : currentNode ,
312
325
} ;
313
326
314
327
if ( ! children ) {
315
328
return (
316
- < li { ...treeNodeProps } >
329
+ < li { ...treeNodeProps } ref = { setRefs } >
317
330
< div className = { `${ prefix } --tree-node__label` } ref = { currentNodeLabel } >
318
331
{ /* @ts -ignore - TS cannot be sure `className` exists on Icon props */ }
319
332
{ Icon && < Icon className = { `${ prefix } --tree-node__icon` } /> }
@@ -323,8 +336,7 @@ const TreeNode = React.forwardRef<HTMLLIElement, TreeNodeProps>(
323
336
) ;
324
337
}
325
338
return (
326
- // eslint-disable-next-line jsx-a11y/role-supports-aria-props
327
- < li { ...treeNodeProps } aria-expanded = { ! ! expanded } ref = { ref } >
339
+ < li { ...treeNodeProps } aria-expanded = { ! ! expanded } ref = { setRefs } >
328
340
< div className = { `${ prefix } --tree-node__label` } ref = { currentNodeLabel } >
329
341
{ /* https://github.com/carbon-design-system/carbon/pull/6008#issuecomment-675738670 */ }
330
342
{ /* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */ }
0 commit comments