diff --git a/src/TreeView/index.tsx b/src/TreeView/index.tsx index cb622c3..e34ff25 100644 --- a/src/TreeView/index.tsx +++ b/src/TreeView/index.tsx @@ -57,6 +57,7 @@ interface IUseTreeProps { onExpand?: (props: ITreeViewOnExpandProps) => void; multiSelect?: boolean; propagateSelectUpwards?: boolean; + treeRef?: React.MutableRefObject; propagateSelect?: boolean; // eslint-disable-next-line @typescript-eslint/no-explicit-any onLoadData?: (props: ITreeViewOnLoadDataProps) => Promise; @@ -80,6 +81,7 @@ const useTree = ({ multiSelect, propagateSelect, propagateSelectUpwards, + treeRef, }: IUseTreeProps) => { const treeParentNode = getTreeParent(data); const [state, dispatch] = useReducer(treeReducer, { @@ -417,10 +419,16 @@ const useTree = ({ nodeRefs?.current != null && leafRefs?.current != null ) { - const tabbableNode = nodeRefs.current[tabbableId]; - const leafNode = leafRefs.current[lastInteractedWith]; - scrollToRef(leafNode); - focusRef(tabbableNode); + const isTreeActive = (treeRef?.current == null) || + (document.activeElement && treeRef.current.contains(document.activeElement)); + if (isTreeActive) { + // Only scroll and focus on the tree when it is the active element on the page. + // This prevents controlled updates from scrolling to the tree and giving it focus. + const tabbableNode = nodeRefs.current[tabbableId]; + const leafNode = leafRefs.current[lastInteractedWith]; + scrollToRef(leafNode); + focusRef(tabbableNode); + } } }, [tabbableId, nodeRefs, leafRefs, lastInteractedWith]); @@ -543,6 +551,10 @@ const TreeView = React.forwardRef( validateTreeViewData(data); const nodeRefs = useRef({}); const leafRefs = useRef({}); + let innerRef = useRef(null); + if (ref != null) { + innerRef = ref as React.MutableRefObject; + } const [state, dispatch] = useTree({ data, controlledSelectedIds: selectedIds, @@ -560,14 +572,10 @@ const TreeView = React.forwardRef( multiSelect, propagateSelect, propagateSelectUpwards, + treeRef: innerRef, }); propagateSelect = propagateSelect && multiSelect; - let innerRef = useRef(null); - if (ref != null) { - innerRef = ref as React.MutableRefObject; - } - return (
    { expect(newNodes[4]).toHaveAttribute("aria-checked", "false"); expect(newNodes[5]).toHaveAttribute("aria-checked", "false"); }); + + test("SelectedIds should not steal focus if another control has it", () => { + let selectedIds = [42]; + const renderContent = (
    + + +
    ); + const { rerender } = render(renderContent); + + const editorElement = document?.getElementById("editor"); + editorElement?.focus(); + selectedIds = [4]; + rerender(renderContent); + expect(document.activeElement).toEqual(editorElement); + }); });