# Tree View
> A tree view is a hierarchical structure that provides nested levels of navigation.

## Import

```js
import { TreeView } from '@gemini-suite/vera-react';

// you can also access TreeView's context via hook:
// import { useTreeViewContext } from '@gemini-suite/vera-react';
```

TreeView is a compound component that consists of multiple parts:

- `TreeView.Root`: The wrapper that contains all the parts of a tree view and provides context for its children.
- `TreeView.Item`: A single item of the tree.
- `TreeView.SubTree`: The wrapper for the items that should be nested under a parent item.

## Examples

### Basic

`TreeView` is best used to organize large amounts of information that can nest within multiple levels.
Tree items with sub trees create a parent-child relationship and automatically use a chevron to indicate the ability to expand and collapse child items.
As the tree hierarchy increases nesting levels, the size of the indentation is increased as well.

`TreeView` works in an uncontrolled way by default, meaning that each instance of `TreeView.Item` containing `TreeView.Subtree` is responsible for managing its own expanded state internally.

<Callout variant="tip">

By default every subtree is collapsed. You can set `defaultIsExpanded` to `true` on the parent `TreeView.Item` to make it expanded by default.

</Callout>

<Callout variant="tip">

If a user expands nested parent items and then collapses a parent item higher in the hierarchy, the expansion state of the parent items lower in the hierarchy is preserved.

</Callout>

```jsx
<TreeView.Root aria-label="Production unit tree">
  <TreeView.Item id="portfolio-one">{'Portfolio one'}</TreeView.Item>
  <TreeView.Item id="portfolio-two">
    {'Portfolio two'}
    <TreeView.SubTree>
      <TreeView.Item id="se-1">
        {'SE 1'}
        <TreeView.SubTree>
          <TreeView.Item id="imsdal-1">{'Imsdal 1'}</TreeView.Item>
          <TreeView.Item id="imsdal-2">{'Imsdal 2'}</TreeView.Item>
        </TreeView.SubTree>
      </TreeView.Item>
    </TreeView.SubTree>
  </TreeView.Item>
  <TreeView.Item id="portfolio-three" defaultIsExpanded>
    {'Portfolio three'}
    <TreeView.SubTree>
      <TreeView.Item id="no-1">{'NO 1'}</TreeView.Item>
      <TreeView.Item id="no-2" defaultIsExpanded>
        {'NO 2'}
        <TreeView.SubTree>
          <TreeView.Item id="aqua" defaultIsExpanded>
            {'Aqua station'}
            <TreeView.SubTree>
              <TreeView.Item id="aqua-generator-1">
                {'Aqua generator 1'}
              </TreeView.Item>
              <TreeView.Item id="aqua-generator-2">
                {'Aqua generator 2'}
              </TreeView.Item>
            </TreeView.SubTree>
          </TreeView.Item>
        </TreeView.SubTree>
      </TreeView.Item>
    </TreeView.SubTree>
  </TreeView.Item>
</TreeView.Root>
```

### Controlled

The expansion state of each subtree can be controlled via `isExpanded` prop on its parent `TreeView.Item` and state changes can be listened via `onIsExpandedChange` prop, allowing you to sync state.

```jsx
() => {
  const [state, setState] = React.useState(false);

  return (
    <Flex flow="column">
      <Box>
        <Button
          onClick={() => {
            setState(prev => !prev);
          }}
          variant="outline"
        >
          {state ? 'Collapse item three' : 'Expand item three'}
        </Button>
      </Box>
      <TreeView.Root aria-label="Controlled tree view example">
        <TreeView.Item id="one">{'Item one'}</TreeView.Item>
        <TreeView.Item id="two">
          {'Item two'}
          <TreeView.SubTree>
            <TreeView.Item id="two-nested-one">{'Nested one'}</TreeView.Item>
            <TreeView.Item id="two-nested-two">{'Nested two'}</TreeView.Item>
          </TreeView.SubTree>
        </TreeView.Item>
        <TreeView.Item
          id="three"
          isExpanded={state}
          onIsExpandedChange={setState}
        >
          {'Item three'}
          <TreeView.SubTree>
            <TreeView.Item id="three-nested-one">{'Nested one'}</TreeView.Item>
            <TreeView.Item id="three-nested-two">{'Nested two'}</TreeView.Item>
            <TreeView.Item id="three-nested-two">
              {'Nested three'}
            </TreeView.Item>
            <TreeView.Item id="three-nested-four">
              {'Nested four'}
            </TreeView.Item>
          </TreeView.SubTree>
        </TreeView.Item>
      </TreeView.Root>
    </Flex>
  );
};
```

### Sizes

`TreeView` component comes in two sizes: `medium` and `small`. By default, it uses `medium` size. Use the `size` prop to control the size.

```jsx
<AutoGrid>
  <Flex flow="column" gap="spacingS">
    <Text color="foregroundNeutralSubtle">Medium</Text>
    <TreeView.Root aria-label="Medium tree view example" size="medium">
      <TreeView.Item id="one">{'Item one'}</TreeView.Item>
      <TreeView.Item id="two">
        {'Item two'}
        <TreeView.SubTree>
          <TreeView.Item id="two-nested-one">{'Nested one'}</TreeView.Item>
          <TreeView.Item id="two-nested-two">{'Nested two'}</TreeView.Item>
        </TreeView.SubTree>
      </TreeView.Item>
      <TreeView.Item id="three" defaultIsExpanded>
        {'Item three'}
        <TreeView.SubTree>
          <TreeView.Item id="three-nested-one">{'Nested one'}</TreeView.Item>
          <TreeView.Item id="three-nested-two">{'Nested two'}</TreeView.Item>
        </TreeView.SubTree>
      </TreeView.Item>
    </TreeView.Root>
  </Flex>
  <Flex flow="column" gap="spacingS">
    <Text variant="zeta" color="foregroundNeutralSubtle">
      Small
    </Text>
    <TreeView.Root aria-label="Small tree view example" size="small">
      <TreeView.Item id="one">{'Item one'}</TreeView.Item>
      <TreeView.Item id="two">
        {'Item two'}
        <TreeView.SubTree>
          <TreeView.Item id="two-nested-one">{'Nested one'}</TreeView.Item>
          <TreeView.Item id="two-nested-two">{'Nested two'}</TreeView.Item>
        </TreeView.SubTree>
      </TreeView.Item>
      <TreeView.Item id="three" defaultIsExpanded>
        {'Item three'}
        <TreeView.SubTree>
          <TreeView.Item id="three-nested-one">{'Nested one'}</TreeView.Item>
          <TreeView.Item id="three-nested-two">{'Nested two'}</TreeView.Item>
        </TreeView.SubTree>
      </TreeView.Item>
    </TreeView.Root>
  </Flex>
</AutoGrid>
```

### Active item

`TreeView.Item` components can be in active state, that is indicated by a different background color. Use `isActive` prop to set the active state.

<Callout variant="important">

No more than one item should be active at once. When a child item is active, all of it's parent items should be expanded.

</Callout>

```jsx
<TreeView.Root aria-label="Active item tree view example">
  <TreeView.Item id="one">{'Item one'}</TreeView.Item>
  <TreeView.Item id="two" defaultIsExpanded>
    {'Item two'}
    <TreeView.SubTree>
      <TreeView.Item id="two-nested-one" isActive>
        {'Nested one'}
      </TreeView.Item>
      <TreeView.Item id="two-nested-two">{'Nested two'}</TreeView.Item>
    </TreeView.SubTree>
  </TreeView.Item>
  <TreeView.Item id="three" defaultIsExpanded>
    {'Item three'}
    <TreeView.SubTree>
      <TreeView.Item id="three-nested-one">{'Nested one'}</TreeView.Item>
      <TreeView.Item id="three-nested-two">{'Nested two'}</TreeView.Item>
      <TreeView.Item id="three-nested-two">{'Nested three'}</TreeView.Item>
    </TreeView.SubTree>
  </TreeView.Item>
</TreeView.Root>
```

### Selecting items

Use `onSelect` prop to enable item selection. `onSelect` handler of `TreeView.Item` is called when an item is selected using either the mouse or keyboard. It can be used to track currently selected item.

```jsx
() => {
  const [selectedItem, setSelectedItem] = React.useState('item-two-nested-two');

  const treeData = [
    {
      title: 'Item one',
      id: 'item-one',
      children: []
    },
    {
      title: 'Item two',
      id: 'item-two',
      children: [
        {
          title: 'Nested one',
          id: 'item-two-nested-one'
        },
        {
          title: 'Nested two',
          id: 'item-two-nested-two'
        },
        {
          title: 'Nested three',
          id: 'item-two-nested-three'
        }
      ]
    },
    {
      title: 'Item three',
      id: 'item-three',
      children: [
        {
          title: 'Nested one',
          id: 'item-three-nested-one'
        },
        {
          title: 'Nested two',
          id: 'item-three-nested-two'
        },
        {
          title: 'Nested three',
          id: 'item-three-nested-three'
        },
        {
          title: 'Nested four',
          id: 'item-three-nested-four'
        },
        {
          title: 'Nested five',
          id: 'item-three-nested-five'
        }
      ]
    },
    {
      title: 'Item four',
      id: 'item-four'
    },
    {
      title: 'Item five',
      id: 'item-five',
      children: [
        {
          title: 'Nested one',
          id: 'item-five-nested-one'
        },
        {
          title: 'Nested two',
          id: 'item-five-nested-two'
        },
        {
          title: 'Nested three',
          id: 'item-five-nested-three'
        }
      ]
    }
  ];
  return (
    <Flex flow="column">
      <Box>Selected item: {selectedItem}</Box>
      <Box css={{ maxHeight: 300, overflowY: 'auto' }}>
        <TreeView.Root aria-label="Selecting items tree view example">
          {treeData.map(item => {
            if (item.children && item.children.length > 0) {
              return (
                <TreeView.Item
                  key={item.id}
                  id={item.id}
                  isActive={selectedItem === item.id}
                  onSelect={() => {
                    setSelectedItem(item.id);
                  }}
                  defaultIsExpanded
                >
                  {item.title}
                  <TreeView.SubTree>
                    {item.children.map(child => {
                      return (
                        <TreeView.Item
                          key={child.id}
                          id={child.id}
                          isActive={selectedItem === child.id}
                          onSelect={() => {
                            setSelectedItem(child.id);
                          }}
                        >
                          {child.title}
                        </TreeView.Item>
                      );
                    })}
                  </TreeView.SubTree>
                </TreeView.Item>
              );
            }

            return (
              <TreeView.Item
                key={item.id}
                id={item.id}
                isActive={selectedItem === item.id}
                onSelect={() => {
                  setSelectedItem(item.id);
                }}
              >
                {item.title}
              </TreeView.Item>
            );
          })}
        </TreeView.Root>
      </Box>
    </Flex>
  );
};
```

### Decorators

Use `startElement` and `endElement` props to add custom elements to the start and end of the `TreeView.Item`.

These are useful for adding a [Label](/components/label) to the item or providing an [Icon](/components/svg-icon) that visually represents and supports the item.

<Callout variant="tip">

Using a folder icon for parent items and a document icon for leaf items is a commonly recognized pairing of icons used in tree view structures.

</Callout>

```jsx
<Box css={{ maxWidth: 400 }}>
  <TreeView.Root aria-label="Tree view with decorators example">
    <TreeView.Item id="one" startElement={<SvgIcon iconName="cases" />}>
      {'Item One really really really long item name with truncation'}
    </TreeView.Item>
    <TreeView.Item
      id="two"
      defaultIsExpanded
      startElement={<SvgIcon iconName="cases" />}
      endElement={<Label size="small">{'Label'}</Label>}
    >
      {'Item Two'}
      <TreeView.SubTree>
        <TreeView.Item
          id="two-nested-one"
          startElement={<SvgIcon iconName="file" />}
        >
          {'Nested one'}
        </TreeView.Item>
        <TreeView.Item
          id="two-nested-two"
          startElement={<SvgIcon iconName="file" />}
        >
          {'Nested two'}
        </TreeView.Item>
      </TreeView.SubTree>
    </TreeView.Item>
    <TreeView.Item
      id="three"
      defaultIsExpanded
      startElement={<SvgIcon iconName="cases" />}
    >
      {'Item three with subtree'}
      <TreeView.SubTree>
        <TreeView.Item
          id="three-nested-one"
          startElement={<SvgIcon iconName="file" />}
          endElement={<Label size="small">{'Label'}</Label>}
        >
          {'Nested one'}
        </TreeView.Item>
        <TreeView.Item
          id="three-nested-two"
          startElement={<SvgIcon iconName="file" />}
          endElement={<Label size="small">{'Label'}</Label>}
        >
          {'Nested two'}
        </TreeView.Item>
        <TreeView.Item
          id="three-nested-three"
          startElement={<SvgIcon iconName="file" />}
        >
          {'Nested three'}
        </TreeView.Item>
      </TreeView.SubTree>
    </TreeView.Item>
  </TreeView.Root>
</Box>
```

## Accessibility

**Tree View** implements the [WAI-ARIA Tree View
design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/).

<ul>
  <li iconName="check">

All relevant ARIA attributes are automatically managed.

  </li>
  <li iconName="check">

Tree view can be focused with <Kbd>Tab</Kbd> or <Kbd>Shift + Tab</Kbd>. <Kbd>Tab</Kbd> moves focus outside of the tree view to the next focusable node.

  </li>
  <li iconName="check">

After focusing the tree view with keyboard, focus is set on the previously active item or on the first item if none of the items were active.

  </li>
  <li iconName="check">

Focus movement is managed using [roving focus technique](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#x6-6-keyboard-navigation-inside-components). <Kbd>↓</Kbd> and <Kbd>↑</Kbd> keys move the focus among focusable tree view items
without opening or closing them.

  </li>
  <li iconName="check">

<Kbd>Home</Kbd> and <Kbd>Page Up</Kbd> moves focus to the first item without
opening or closing it.

  </li>
  <li iconName="check">

<Kbd>End</Kbd> and <Kbd>Page Down</Kbd> moves focus to the last item without
expanding any nodes that are closed.

  </li>
  <li iconName="check">

<Kbd>Enter</Kbd> performs the default action (triggering `onSelect` event when
specified or toggling expansion) for the focused item.

  </li>
  <li iconName="check">

Clicking the chevron only expands or collapses the node. Clicking anywhere else will perform the default action on the item.

  </li>
</ul>

---

## API Reference

### TreeView.Root

<Callout variant="tip">

In addition to the props below, you can pass [margin props](/components/layout/box#api-reference) to control the spacing.

</Callout>

| Name         | Type                  | Default    | Description                                                                                                                                                                                                                                   | Required |
| ------------ | --------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`        | `StitchesCss`         |            | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants. |          |
| `aria-label` | `string`              |            | Used to provide a better description of the tree view for users with assistive technologies.                                                                                                                                                  | Yes      |
| `size`       | `"medium" \| "small"` | `"medium"` | The size of the tree view.                                                                                                                                                                                                                    |          |

### TreeView.Item

<Callout variant="tip">

In addition to the props below, you can pass [margin props](/components/layout/box#api-reference) to control the spacing.

</Callout>

| Name                 | Type                                                                                 | Default | Description                                                                                                                                                                                                                                   | Required |
| -------------------- | ------------------------------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`                | `StitchesCss`                                                                        |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants. |          |
| `id`                 | `string`                                                                             |         | A unique identifier for the item.                                                                                                                                                                                                             | Yes      |
| `defaultIsExpanded`  | `boolean`                                                                            |         | The initial expanded state of the item's subtree when it's first rendered. Use when you do not need to control expanded state.                                                                                                                |          |
| `isExpanded`         | `boolean`                                                                            | `false` | The controlled expanded state of the item. Use in conjunction with `onIsExpandedChange`.                                                                                                                                                      |          |
| `onIsExpandedChange` | `(isExpanded: boolean) => void`                                                      |         | Event handler called when the expanded state of the tree item changes.                                                                                                                                                                        |          |
| `isActive`           | `boolean`                                                                            | `false` | When `true`, the tree view item is shown in active state. `aria-current` attribute is set to `item` to indicate that the selected item.                                                                                                       |          |
| `onSelect`           | `(event: React.MouseEvent<HTMLElement> \| React.KeyboardEvent<HTMLElement>) => void` |         | Event handler called when tree item is selected (via mouse or keyboard).                                                                                                                                                                      |          |
| `startElement`       | `React.ReactElement`                                                                 |         | `ReactElement` to render to the left of the item's children.                                                                                                                                                                                  |          |
| `endElement`         | `React.ReactElement`                                                                 |         | `ReactElement` to render to the right of the item's children.                                                                                                                                                                                 |          |

### TreeView.SubTree

<Callout variant="tip">

In addition to the props below, you can pass [margin props](/components/layout/box#api-reference) to control the spacing.

</Callout>

| Name  | Type          | Default | Description                                                                                                                                                                                                                                   | Required |
| ----- | ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css` | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants. |          |
