Skip to content

Commit 53cf5ab

Browse files
committed
fix: fixed focus flow issue on right arrow press
1 parent bc4acdc commit 53cf5ab

File tree

1 file changed

+31
-19
lines changed

1 file changed

+31
-19
lines changed

packages/react/src/components/TreeView/TreeNode.tsx

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import React, {
1414
useEffect,
1515
useRef,
1616
useState,
17+
MutableRefObject,
1718
} from 'react';
1819
import { keys, match, matches } from '../../internal/keyboard';
1920
import { useControllableState } from '../../internal/useControllableState';
@@ -114,7 +115,7 @@ const TreeNode = React.forwardRef<HTMLLIElement, TreeNodeProps>(
114115
value,
115116
...rest
116117
},
117-
ref
118+
forwardedRef
118119
) => {
119120
// These are provided by the parent TreeView component
120121
const depth = propDepth as number;
@@ -136,9 +137,20 @@ const TreeNode = React.forwardRef<HTMLLIElement, TreeNodeProps>(
136137
? controllableExpandedState
137138
: uncontrollableExpandedState;
138139

139-
const currentNode = useRef<HTMLLIElement>(null);
140+
const currentNode = useRef<HTMLLIElement | null>(null);
140141
const currentNodeLabel = useRef<HTMLDivElement>(null);
141142
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+
142154
const nodesWithProps = React.Children.map(children, (node) => {
143155
if (React.isValidElement(node)) {
144156
return React.cloneElement(node, {
@@ -196,35 +208,38 @@ const TreeNode = React.forwardRef<HTMLLIElement, TreeNodeProps>(
196208
event.stopPropagation();
197209
}
198210
if (match(event, keys.ArrowLeft)) {
199-
const findParentTreeNode = (node) => {
211+
const findParentTreeNode = (node: Element | null): Element | null => {
212+
if (!node) return null;
200213
if (node.classList.contains(`${prefix}--tree-parent-node`)) {
201214
return node;
202215
}
203216
if (node.classList.contains(`${prefix}--tree`)) {
204217
return null;
205218
}
206-
return findParentTreeNode(node.parentNode);
219+
return findParentTreeNode(node.parentElement);
207220
};
208221
if (children && expanded) {
209222
if (!enableTreeviewControllable) {
210223
onToggle?.(event, { id, isExpanded: false, label, value });
211224
}
212225
setExpanded(false);
213226
} 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+
}
219233
}
220234
}
221235
if (children && match(event, keys.ArrowRight)) {
222236
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+
}
228243
} else {
229244
if (!enableTreeviewControllable) {
230245
onToggle?.(event, { id, isExpanded: true, label, value });
@@ -307,13 +322,11 @@ const TreeNode = React.forwardRef<HTMLLIElement, TreeNodeProps>(
307322
onFocus: handleFocusEvent,
308323
onKeyDown: handleKeyDown,
309324
role: 'treeitem',
310-
// @ts-ignore
311-
ref: currentNode,
312325
};
313326

314327
if (!children) {
315328
return (
316-
<li {...treeNodeProps}>
329+
<li {...treeNodeProps} ref={setRefs}>
317330
<div className={`${prefix}--tree-node__label`} ref={currentNodeLabel}>
318331
{/* @ts-ignore - TS cannot be sure `className` exists on Icon props */}
319332
{Icon && <Icon className={`${prefix}--tree-node__icon`} />}
@@ -323,8 +336,7 @@ const TreeNode = React.forwardRef<HTMLLIElement, TreeNodeProps>(
323336
);
324337
}
325338
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}>
328340
<div className={`${prefix}--tree-node__label`} ref={currentNodeLabel}>
329341
{/* https://github.com/carbon-design-system/carbon/pull/6008#issuecomment-675738670 */}
330342
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}

0 commit comments

Comments
 (0)