Skip to content

feat: add TreeView Component#2191

Open
JoshMK wants to merge 26 commits intodevelopfrom
feat-tree
Open

feat: add TreeView Component#2191
JoshMK wants to merge 26 commits intodevelopfrom
feat-tree

Conversation

@JoshMK
Copy link
Copy Markdown
Contributor

@JoshMK JoshMK commented Jan 12, 2026

@JoshMK JoshMK changed the title Feat tree feat: add TreeView Component Jan 12, 2026
@aws-amplify-us-east-1
Copy link
Copy Markdown

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-2191.d15792l1n26ww3.amplifyapp.com

@JoshMK JoshMK marked this pull request as ready for review January 13, 2026 23:56
@JoshMK JoshMK requested review from a team as code owners January 13, 2026 23:56
Copilot AI review requested due to automatic review settings January 13, 2026 23:56
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new TreeView component to the Cauldron design system, enabling users to display and interact with hierarchical tree structures with support for single/multiple selection modes and custom actions.

Changes:

  • Added TreeView component with TreeViewItem subcomponent supporting single/multiple selection modes
  • Integrated react-aria-components library for accessibility features
  • Added CSS styling for tree view with theme support
  • Created comprehensive test coverage for TreeView functionality

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/styles/tree-view.css CSS styles for TreeView component with light/dark theme support
packages/styles/index.css Import statement for tree-view.css
packages/react/src/index.ts Export statement for TreeView component
packages/react/src/components/TreeView/index.tsx Main TreeView component definition
packages/react/src/components/TreeView/TreeViewItem.tsx TreeViewItem subcomponent with selection handling
packages/react/src/components/TreeView/TreeView.test.tsx Test suite for TreeView component
packages/react/src/components/Checkbox/index.tsx Added onChangeToggle prop to Checkbox component
packages/react/package.json Added react-aria-components dependency
package.json Added packageManager field
docs/pages/components/TreeView.mdx Documentation for TreeView component

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dbjorge
Copy link
Copy Markdown
Contributor

dbjorge commented Jan 14, 2026

It'd be good to go back to design and clarify the expected behavior of the checkboxes - the figma doesn't clarify explicitly, but some of the examples it cites (eg, Fluent 2) use a pattern where selecting a parent would implicitly select all children, and unselecting a child would implicitly set the parent to either an indeterminate or unselected state (depending on if other siblings were still selected).

adobe/react-spectrum#6589 suggests that it might be challenging to actually implement that ourselves without patching the spectrum tree view and that we'd likely prefer to wait for adobe/react-spectrum#8800 rather than implement it ourselves, but we should make sure that stakeholders/design are ok with the current behavior in the meantime.

@JoshMK
Copy link
Copy Markdown
Contributor Author

JoshMK commented Jan 14, 2026

It'd be good to go back to design and clarify the expected behavior of the checkboxes - the figma doesn't clarify explicitly, but some of the examples it cites (eg, Fluent 2) use a pattern where selecting a parent would implicitly select all children, and unselecting a child would implicitly set the parent to either an indeterminate or unselected state (depending on if other siblings were still selected).

adobe/react-spectrum#6589 suggests that it might be challenging to actually implement that ourselves without patching the spectrum tree view and that we'd likely prefer to wait for adobe/react-spectrum#8800 rather than implement it ourselves, but we should make sure that stakeholders/design are ok with the current behavior in the meantime.

Good points, Dan. Also a quick technical note for anyone reviewing this:

Adobe actually has two component libraries available that we can use - Spectrum and React Aria Components. I ended up choosing React Aria Components since unlike Spectrum, it doesn't make style assumptions and allows us to incorporate our existing Deque theming into new components.

@github-actions
Copy link
Copy Markdown
Contributor

This pull request has been open for 30 days with no activity. If no further activity occurs, it will be closed in 14 days.

@github-actions github-actions bot added the stale When a PR or Issue has been flagged due to inactivity label Feb 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 1, 2026

This pull request was closed because it has been inactive for 14 days since being marked as stale.

@github-actions github-actions bot closed this Mar 1, 2026
@kate-spalla kate-spalla reopened this Mar 17, 2026
@github-actions github-actions bot removed the stale When a PR or Issue has been flagged due to inactivity label Mar 17, 2026
import { Tree } from 'react-aria-components';
import TreeViewItem from './TreeViewItem';

export interface TreeViewFileType {
Copy link
Copy Markdown
Collaborator

@anastasialanz anastasialanz Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be named "file"? Could it be more generic like TreeViewNode?

Comment on lines +1 to +12
:root {
--treeview-padding: 16px;
--treeview-focus-ring-color: var(--focus-light);
--treeview-selected-color: var(--focus-light);
--treeview-highlight-background: var(--accent-light);
}

.cauldron--theme-dark {
--treeview-focus-ring-color: var(--focus-dark);
--treeview-selected-color: var(--focus-dark);
--treeview-highlight-background: var(--accent-dark);
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variables should be named --tree-view-* to be consistent with the rest of the codebase.

Copy link
Copy Markdown
Collaborator

@anastasialanz anastasialanz Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In our contributing docs, there's a note about using jest-axe to catch a11y violations. Can we implement that in these tests?

cauldron/CONTRIBUTING.md

Lines 229 to 239 in 3d37eb6

#### Ensuring component returns no violations (in unit test)
We use [jest-axe](https://www.npmjs.com/package/jest-axe) to run axe in our unit tests. It is expected that you run axe in **all states** of your component.
```jsx
test('should return no axe violations', async () => {
render(<SomeCauldronComponent ref={ref} />);
const component = screen.getByRole('button');
expect(await axe(component)).toHaveNoViolations();
});
```

We should include screenshot tests for this component as well.

New components that have visual components or PRs that include visual changes to [component snapshot testing screenshots](https://github.com/dequelabs/cauldron/blob/develop/e2e/readme.md#snapshot-testing) should have an approval from [@dequelabs/design-team](https://github.com/orgs/dequelabs/teams/design-team).

<TreeView ariaLabel="Test TreeView" items={items} selectionMode="single" />
);
const child1 = getByText('TreeView');
fireEvent.click(child1);
Copy link
Copy Markdown
Collaborator

@anastasialanz anastasialanz Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should prefer userEvent over fireEvent to simulate how a user would interact with this component.

https://testing-library.com/docs/user-event/intro/#differences-from-fireevent

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we include tests for things like the handleOnAction workaround and checkbox rendering based on selectionMode?

<Icon type="chevron-right" />
</Button>
{selectionMode !== 'none' ? (
<Checkbox
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not able to select an individual checkbox with space. As soon as I let go of the space bar, it becomes unchecked again.

checkbox-selection-bug.mov

I'm able to use space on the checkbox on https://react-aria.adobe.com/Tree.

Copy link
Copy Markdown

@mateoviilla1 mateoviilla1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Critical

TreeViewItem.tsx:60 — Checkbox Space key bug

Pressing Space on a focused checkbox doesn't maintain the checked state — it resets immediately on keyup. The combination of onChangeToggle={false} on Cauldron's <Checkbox> and react-aria's selection state sync causes the visual state to reset on re-render. For an accessibility-first component this is a blocking issue. Consider using react-aria's own <Checkbox> from react-aria-components, or routing keyboard interaction through TreeItem's selection handler so the state flows through react-aria's unified event model.


TreeViewItem.tsx:30–43 — Direct DOM mutation in handleOnAction

handleOnAction calls setAttribute('aria-selected', ...) directly on the DOM node to work around react-aria not toggling selection on action. This bypasses React's render cycle, causing the virtual DOM and real DOM to diverge — any subsequent re-render will overwrite the mutation. The correct fix is to manage selection externally via selectedKeys/onSelectionChange props on <Tree>, keeping state in React rather than the DOM.


Important

index.tsx:14onAction type mismatch

onAction is typed as () => void but handleOnAction calls it as onAction(key) where key is a string. TypeScript silently accepts calling a () => void with arguments, so this compiles but the public API contract is wrong. Change to (key: string) => void.


tree-view.css:1–12 — CSS variable naming

Custom properties use --treeview-* (e.g. --treeview-padding, --treeview-focus-ring-color). The Cauldron codebase convention uses --tree-view-* with word separators. Rename all variables accordingly.


TreeView.test.tsx — Test quality

Three gaps per CONTRIBUTING.md:

  1. fireEvent should be replaced with userEvent from @testing-library/user-event to simulate real user interaction (focus/pointer/keyboard sequences).
  2. Missing jest-axe assertions — at least one expect(await axe(container)).toHaveNoViolations() per rendered variant.
  3. Missing screenshot tests.

index.tsx:5TreeViewFileType naming

The exported type is named TreeViewFileType, implying the tree is file-system specific. The component is generic. Rename to TreeViewItem or TreeViewNode.


Suggestions

TreeViewItem.tsx:73 — Redundant items prop on <Collection>

<Collection items={children}> passes items but the children are rendered manually as a JSX array. In react-aria, items is only consumed when children is a render function — here it's silently ignored. Either remove items={children}, or switch to the dynamic collection pattern: <Collection items={children}>{(child) => <TreeViewItem ... />}</Collection>.


TreeView.mdx:4 — Incorrect source link

The frontmatter source field points to TreeView.tsx which doesn't exist — the actual entry point is index.tsx. Update to .../TreeView/index.tsx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants