Skip to content

Commit

Permalink
Fix issue with halfSelected parent not change its status to selected …
Browse files Browse the repository at this point in the history
…when propagateSelect=false and after children deselected not possible to select parent.
  • Loading branch information
kpustakhod committed Aug 24, 2023
1 parent 13c61cb commit e78d84e
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 7 deletions.
9 changes: 2 additions & 7 deletions src/TreeView/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import cx from "classnames";
import PropTypes from "prop-types";
import React, { useEffect, useReducer, useRef } from "react";
import {
ITreeViewState,
treeReducer,
TreeViewAction,
} from "./reducer";
import { ITreeViewState, treeReducer, TreeViewAction } from "./reducer";
import {
ClickActions,
INode,
Expand Down Expand Up @@ -101,7 +97,6 @@ const useTree = ({

const {
selectedIds,
controlledIds,
expandedIds,
disabledIds,
tabbableId,
Expand Down Expand Up @@ -330,7 +325,7 @@ const useTree = ({
//Update parent if a child changes
useEffect(() => {
if (propagateSelectUpwards) {
const idsToUpdate = new Set<NodeId>([...toggledIds, ...controlledIds]);
const idsToUpdate = new Set<NodeId>([...toggledIds]);
if (
lastInteractedWith &&
lastAction !== treeTypes.focus &&
Expand Down
147 changes: 147 additions & 0 deletions src/__tests__/CheckboxTree.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,15 @@ function CheckboxTree({
defaultSelectedIds = [],
defaultExpandedIds = [],
defaultDisabledIds = [],
selectedIds,
}: {
defaultSelectedIds?: NodeId[];
defaultExpandedIds?: NodeId[];
defaultDisabledIds?: NodeId[];
propagateSelect?: boolean;
multiSelect?: boolean;
data?: INode[];
selectedIds?: NodeId[];
}) {
return (
<div>
Expand All @@ -76,6 +78,7 @@ function CheckboxTree({
defaultSelectedIds={defaultSelectedIds}
defaultExpandedIds={defaultExpandedIds}
defaultDisabledIds={defaultDisabledIds}
selectedIds={selectedIds}
propagateSelectUpwards
togglableSelect
nodeAction="check"
Expand Down Expand Up @@ -478,3 +481,147 @@ test("should preserve focus on node if changed data contains previouslt focused
"Drinks"
);
});

describe("halfSelected parent should change status when propagateSelect={false}", () => {
const checkSelectionAndHalfSelection = (nodes: HTMLElement[]) => {
//Initial state
//[-] Fruits
//*[ ] Avocados
//*[+] Bananas
//*[ ] Berries
// ...

console.log(nodes[2].innerHTML);
expect(nodes[0]).toHaveAttribute("aria-checked", "mixed");
expect(nodes[2]).toHaveAttribute("aria-checked", "true");

nodes[0].focus();
if (document.activeElement == null)
throw new Error(
`Expected to find an active element on the document (after focusing the second element with role["treeitem"]), but did not.`
);
fireEvent.click(nodes[0].getElementsByClassName("checkbox-icon")[0]); //select Fruits
//Expected state 1
//[+] Fruits
//*[ ] Avocados
//*[+] Bananas
//*[ ] Berries
// ...

expect(nodes[0]).toHaveAttribute("aria-checked", "true");
expect(nodes[2]).toHaveAttribute("aria-checked", "true");

fireEvent.click(nodes[0].getElementsByClassName("checkbox-icon")[0]); //half-select Fruits
//Expected state 2
//[-] Fruits
//*[ ] Avocados
//*[+] Bananas
//*[ ] Berries
// ...
expect(nodes[0]).toHaveAttribute("aria-checked", "mixed");
expect(nodes[2]).toHaveAttribute("aria-checked", "true");
};

test("to selected when default selected child and selection changed manually", () => {
const { queryAllByRole } = render(
<CheckboxTree
propagateSelect={false}
defaultSelectedIds={[3]}
defaultExpandedIds={[1]}
/>
);
checkSelectionAndHalfSelection(queryAllByRole("treeitem"));
});

test("to selected when selection changed manually", () => {
const { queryAllByRole } = render(
<CheckboxTree propagateSelect={false} defaultExpandedIds={[1]} />
);
const nodes = queryAllByRole("treeitem");

nodes[0].focus();
if (document.activeElement == null)
throw new Error(
`Expected to find an active element on the document (after focusing the second element with role["treeitem"]), but did not.`
);

fireEvent.click(nodes[2].getElementsByClassName("checkbox-icon")[0]); //select Bananas

expect(nodes[0]).toHaveAttribute("aria-checked", "mixed");
expect(nodes[2]).toHaveAttribute("aria-checked", "true");

checkSelectionAndHalfSelection(nodes);
});

test("to selected when selection changed controlled and manually", () => {
const { queryAllByRole } = render(
<CheckboxTree
propagateSelect={false}
selectedIds={[3]}
defaultExpandedIds={[1]}
/>
);
checkSelectionAndHalfSelection(queryAllByRole("treeitem"));
});

test("to deselected and to selected when no selected children after children where selected manually initially", () => {
const { queryAllByRole } = render(
<CheckboxTree propagateSelect={false} defaultExpandedIds={[1]} />
);
const nodes = queryAllByRole("treeitem");

nodes[0].focus();
if (document.activeElement == null)
throw new Error(
`Expected to find an active element on the document (after focusing the second element with role["treeitem"]), but did not.`
);

fireEvent.click(nodes[2].getElementsByClassName("checkbox-icon")[0]); //select Bananas

expect(nodes[0]).toHaveAttribute("aria-checked", "mixed");
expect(nodes[2]).toHaveAttribute("aria-checked", "true");

checkSelectionAndHalfSelection(nodes);

fireEvent.click(nodes[2].getElementsByClassName("checkbox-icon")[0]); //deselect Bananas

expect(nodes[0]).toHaveAttribute("aria-checked", "false");
expect(nodes[2]).toHaveAttribute("aria-checked", "false");

fireEvent.click(nodes[0].getElementsByClassName("checkbox-icon")[0]); //select Fruits

expect(nodes[0]).toHaveAttribute("aria-checked", "true");
expect(nodes[2]).toHaveAttribute("aria-checked", "false");

fireEvent.click(nodes[0].getElementsByClassName("checkbox-icon")[0]); //deselect Fruits

expect(nodes[0]).toHaveAttribute("aria-checked", "false");
expect(nodes[2]).toHaveAttribute("aria-checked", "false");
});

test("to deselected and to selected when no selected children after children where selected controlled initially", () => {
const { queryAllByRole } = render(
<CheckboxTree
propagateSelect={false}
defaultSelectedIds={[3]}
defaultExpandedIds={[1]}
/>
);
const nodes = queryAllByRole("treeitem");
checkSelectionAndHalfSelection(nodes);
fireEvent.click(nodes[2].getElementsByClassName("checkbox-icon")[0]); //deselect Bananas

expect(nodes[0]).toHaveAttribute("aria-checked", "false");
expect(nodes[2]).toHaveAttribute("aria-checked", "false");

fireEvent.click(nodes[0].getElementsByClassName("checkbox-icon")[0]); //select Fruits

expect(nodes[0]).toHaveAttribute("aria-checked", "true");
expect(nodes[2]).toHaveAttribute("aria-checked", "false");

fireEvent.click(nodes[0].getElementsByClassName("checkbox-icon")[0]); //deselect Fruits

expect(nodes[0]).toHaveAttribute("aria-checked", "false");
expect(nodes[2]).toHaveAttribute("aria-checked", "false");
});
});

0 comments on commit e78d84e

Please sign in to comment.