diff --git a/.changeset/fix-initial-expanded-items-should-expand-all-parents.md b/.changeset/fix-initial-expanded-items-should-expand-all-parents.md new file mode 100644 index 000000000..7651644c6 --- /dev/null +++ b/.changeset/fix-initial-expanded-items-should-expand-all-parents.md @@ -0,0 +1,5 @@ +--- +'react-magma-dom': patch +--- + +fix(TreeView): fix initial expanded items should expand all parents \ No newline at end of file diff --git a/packages/react-magma-dom/src/components/TreeView/TreeView.stories.tsx b/packages/react-magma-dom/src/components/TreeView/TreeView.stories.tsx index 3259dd2ce..620117a06 100644 --- a/packages/react-magma-dom/src/components/TreeView/TreeView.stories.tsx +++ b/packages/react-magma-dom/src/components/TreeView/TreeView.stories.tsx @@ -408,7 +408,7 @@ export const Complex = args => { Complex.args = { selectable: TreeViewSelectable.multi, ariaLabel: 'Textbook tree', - initialExpandedItems: ['pt1', 'pt1ch1'], + initialExpandedItems: ['pt1', 'pt1ch1', 'pt2ch5.1'], preselectedItems: [ { itemId: 'pt1ch1', checkedStatus: IndeterminateCheckboxStatus.checked }, { itemId: 'pt1', checkedStatus: IndeterminateCheckboxStatus.indeterminate }, diff --git a/packages/react-magma-dom/src/components/TreeView/TreeView.test.js b/packages/react-magma-dom/src/components/TreeView/TreeView.test.js index 6f3b04996..41d876854 100644 --- a/packages/react-magma-dom/src/components/TreeView/TreeView.test.js +++ b/packages/react-magma-dom/src/components/TreeView/TreeView.test.js @@ -213,10 +213,10 @@ describe('TreeView', () => { expect(getByTestId('item3')).toHaveAttribute('aria-expanded', 'false'); }); - it('when child item is part of the array, that item is expanded', () => { + it('when child item is part of the array, that item is expanded including parents', () => { const { getByTestId } = render( getTreeItemsMultiLevel({ - initialExpandedItems: ['item2', 'item-child2.1'], + initialExpandedItems: ['item-child2.1'], }) ); diff --git a/packages/react-magma-dom/src/components/TreeView/useTreeView.ts b/packages/react-magma-dom/src/components/TreeView/useTreeView.ts index 3498a3ec3..3073d6f6c 100644 --- a/packages/react-magma-dom/src/components/TreeView/useTreeView.ts +++ b/packages/react-magma-dom/src/components/TreeView/useTreeView.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import { useDescendants } from '../../hooks/useDescendants'; import { TreeItemSelectedInterface, TreeViewItemInterface } from './TreeViewContext'; -import { getInitialItems, selectMulti, selectSingle } from './utils'; +import { getInitialExpandedIds, getInitialItems, selectMulti, selectSingle } from './utils'; import { TreeViewSelectable } from './types'; import { IndeterminateCheckboxStatus } from '../IndeterminateCheckbox'; @@ -86,7 +86,7 @@ export function useTreeView(props: UseTreeViewProps) { selectable = TreeViewSelectable.single, onSelectedItemChange, onExpandedChange, - initialExpandedItems, + initialExpandedItems: rawInitialExpandedItems, preselectedItems, checkChildren = selectable !== TreeViewSelectable.single, checkParents = selectable !== TreeViewSelectable.single, @@ -107,7 +107,11 @@ export function useTreeView(props: UseTreeViewProps) { const selectedItems = React.useMemo(() => { return items.filter((item) => item.checkedStatus === IndeterminateCheckboxStatus.checked) - }, [items]); + }, [items]); + + const initialExpandedItems = React.useMemo(() => { + return getInitialExpandedIds({ items, initialExpandedItems: rawInitialExpandedItems }) + }, [items, rawInitialExpandedItems]); const itemToFocus = React.useMemo(() => { const [firstItem] = items; diff --git a/packages/react-magma-dom/src/components/TreeView/utils.ts b/packages/react-magma-dom/src/components/TreeView/utils.ts index b5107f86f..586e7add9 100644 --- a/packages/react-magma-dom/src/components/TreeView/utils.ts +++ b/packages/react-magma-dom/src/components/TreeView/utils.ts @@ -278,3 +278,25 @@ export const selectMulti = ({items, itemId, checkedStatus, checkChildren, checkP const itemsWithProcessedChildrenSelection = checkChildren ? processChildrenSelection({ items: itemsWithProcessedItemSelection, itemId, checkedStatus }) : itemsWithProcessedItemSelection return checkParents ? processParentsSelection({ items: itemsWithProcessedChildrenSelection, itemId, checkedStatus }) : itemsWithProcessedChildrenSelection; } + +const getParentIds = ({ items, itemId, prevParentIds = []}: { items: TreeViewItemInterface[]; itemId: TreeViewItemInterface['itemId']; prevParentIds?: TreeViewItemInterface['itemId'][] }) => { + const item = items.find(item => item.itemId === itemId); + + if (!item) { + return prevParentIds; + } + + const { parentId } = item; + + return parentId ? getParentIds({ itemId: parentId, items, prevParentIds: [...prevParentIds, parentId]}) : prevParentIds; +} + +export const getInitialExpandedIds = ({ items, initialExpandedItems }: { items: TreeViewItemInterface[]; } & Pick) => { + if (!initialExpandedItems) { + return initialExpandedItems; + } + + return initialExpandedItems.reduce((result, itemId) => { + return [...result, itemId, ...getParentIds({ itemId, items })]; + }, []); +} \ No newline at end of file