Skip to content

Commit 4e82bb3

Browse files
authored
Merge pull request #143 from Sappharad/master
Prevent tree from stealing focus from the page (for Issue #142)
2 parents f4dc71d + 71e408a commit 4e82bb3

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed

src/TreeView/index.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ interface IUseTreeProps {
5757
onExpand?: (props: ITreeViewOnExpandProps) => void;
5858
multiSelect?: boolean;
5959
propagateSelectUpwards?: boolean;
60+
treeRef?: React.MutableRefObject<HTMLUListElement | null>;
6061
propagateSelect?: boolean;
6162
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6263
onLoadData?: (props: ITreeViewOnLoadDataProps) => Promise<any>;
@@ -80,6 +81,7 @@ const useTree = ({
8081
multiSelect,
8182
propagateSelect,
8283
propagateSelectUpwards,
84+
treeRef,
8385
}: IUseTreeProps) => {
8486
const treeParentNode = getTreeParent(data);
8587
const [state, dispatch] = useReducer(treeReducer, {
@@ -417,10 +419,16 @@ const useTree = ({
417419
nodeRefs?.current != null &&
418420
leafRefs?.current != null
419421
) {
420-
const tabbableNode = nodeRefs.current[tabbableId];
421-
const leafNode = leafRefs.current[lastInteractedWith];
422-
scrollToRef(leafNode);
423-
focusRef(tabbableNode);
422+
const isTreeActive = (treeRef?.current == null) ||
423+
(document.activeElement && treeRef.current.contains(document.activeElement));
424+
if (isTreeActive) {
425+
// Only scroll and focus on the tree when it is the active element on the page.
426+
// This prevents controlled updates from scrolling to the tree and giving it focus.
427+
const tabbableNode = nodeRefs.current[tabbableId];
428+
const leafNode = leafRefs.current[lastInteractedWith];
429+
scrollToRef(leafNode);
430+
focusRef(tabbableNode);
431+
}
424432
}
425433
}, [tabbableId, nodeRefs, leafRefs, lastInteractedWith]);
426434

@@ -543,6 +551,10 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
543551
validateTreeViewData(data);
544552
const nodeRefs = useRef({});
545553
const leafRefs = useRef({});
554+
let innerRef = useRef<HTMLUListElement | null>(null);
555+
if (ref != null) {
556+
innerRef = ref as React.MutableRefObject<HTMLUListElement>;
557+
}
546558
const [state, dispatch] = useTree({
547559
data,
548560
controlledSelectedIds: selectedIds,
@@ -560,14 +572,10 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
560572
multiSelect,
561573
propagateSelect,
562574
propagateSelectUpwards,
575+
treeRef: innerRef,
563576
});
564577
propagateSelect = propagateSelect && multiSelect;
565578

566-
let innerRef = useRef<HTMLUListElement | null>(null);
567-
if (ref != null) {
568-
innerRef = ref as React.MutableRefObject<HTMLUListElement>;
569-
}
570-
571579
return (
572580
<ul
573581
className={cx(baseClassNames.root, className)}

src/__tests__/ControlledTree.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,4 +753,22 @@ describe("Data with ids", () => {
753753
expect(newNodes[4]).toHaveAttribute("aria-checked", "false");
754754
expect(newNodes[5]).toHaveAttribute("aria-checked", "false");
755755
});
756+
757+
test("SelectedIds should not steal focus if another control has it", () => {
758+
let selectedIds = [42];
759+
const renderContent = (<div>
760+
<input type="text" id="editor"/>
761+
<MultiSelectCheckboxControlled
762+
selectedIds={selectedIds}
763+
data={dataWithIds}
764+
/>
765+
</div>);
766+
const { rerender } = render(renderContent);
767+
768+
const editorElement = document?.getElementById("editor");
769+
editorElement?.focus();
770+
selectedIds = [4];
771+
rerender(renderContent);
772+
expect(document.activeElement).toEqual(editorElement);
773+
});
756774
});

0 commit comments

Comments
 (0)