Skip to content

Commit

Permalink
Merge pull request #143 from Sappharad/master
Browse files Browse the repository at this point in the history
Prevent tree from stealing focus from the page (for Issue #142)
  • Loading branch information
yhy-1 authored Jan 12, 2024
2 parents f4dc71d + 71e408a commit 4e82bb3
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 9 deletions.
26 changes: 17 additions & 9 deletions src/TreeView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ interface IUseTreeProps {
onExpand?: (props: ITreeViewOnExpandProps) => void;
multiSelect?: boolean;
propagateSelectUpwards?: boolean;
treeRef?: React.MutableRefObject<HTMLUListElement | null>;
propagateSelect?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onLoadData?: (props: ITreeViewOnLoadDataProps) => Promise<any>;
Expand All @@ -80,6 +81,7 @@ const useTree = ({
multiSelect,
propagateSelect,
propagateSelectUpwards,
treeRef,
}: IUseTreeProps) => {
const treeParentNode = getTreeParent(data);
const [state, dispatch] = useReducer(treeReducer, {
Expand Down Expand Up @@ -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]);

Expand Down Expand Up @@ -543,6 +551,10 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
validateTreeViewData(data);
const nodeRefs = useRef({});
const leafRefs = useRef({});
let innerRef = useRef<HTMLUListElement | null>(null);
if (ref != null) {
innerRef = ref as React.MutableRefObject<HTMLUListElement>;
}
const [state, dispatch] = useTree({
data,
controlledSelectedIds: selectedIds,
Expand All @@ -560,14 +572,10 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
multiSelect,
propagateSelect,
propagateSelectUpwards,
treeRef: innerRef,
});
propagateSelect = propagateSelect && multiSelect;

let innerRef = useRef<HTMLUListElement | null>(null);
if (ref != null) {
innerRef = ref as React.MutableRefObject<HTMLUListElement>;
}

return (
<ul
className={cx(baseClassNames.root, className)}
Expand Down
18 changes: 18 additions & 0 deletions src/__tests__/ControlledTree.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -753,4 +753,22 @@ describe("Data with ids", () => {
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 = (<div>
<input type="text" id="editor"/>
<MultiSelectCheckboxControlled
selectedIds={selectedIds}
data={dataWithIds}
/>
</div>);
const { rerender } = render(renderContent);

const editorElement = document?.getElementById("editor");
editorElement?.focus();
selectedIds = [4];
rerender(renderContent);
expect(document.activeElement).toEqual(editorElement);
});
});

0 comments on commit 4e82bb3

Please sign in to comment.