From 6b8cb7a0ea8b030e2c52eeab998b61fb3df3d124 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Wed, 21 Aug 2024 11:42:40 -0500 Subject: [PATCH] docs(RSP): add TreeView docs (#6744) * add RSP TreeView docs * fix types * address review comments * fix typo * add ActionGroup and ActionMenu examples * address review comments * remove comment * Update packages/@react-spectrum/tree/docs/TreeView.mdx --------- Co-authored-by: Robert Snow --- .../@react-spectrum/tree/docs/TreeView.mdx | 498 ++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100644 packages/@react-spectrum/tree/docs/TreeView.mdx diff --git a/packages/@react-spectrum/tree/docs/TreeView.mdx b/packages/@react-spectrum/tree/docs/TreeView.mdx new file mode 100644 index 00000000000..d8a01afbc44 --- /dev/null +++ b/packages/@react-spectrum/tree/docs/TreeView.mdx @@ -0,0 +1,498 @@ +{/* Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '@react-spectrum/docs'; +export default Layout; + +import docs from 'docs:@react-spectrum/tree'; +import {HeaderInfo, PropTable, PageDescription, TypeLink, VersionBadge} from '@react-spectrum/docs'; +import {Keyboard} from '@react-spectrum/text'; +import packageData from '@react-spectrum/tree/package.json'; +import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; + +```jsx import +import Folder from '@spectrum-icons/workflow/Folder'; +import {Flex} from '@react-spectrum/layout'; +import FileTxt from '@spectrum-icons/workflow/FileTxt'; +import GlobeOutline from '@spectrum-icons/workflow/GlobeOutline'; +import Image from '@spectrum-icons/workflow/Image'; +import Edit from '@spectrum-icons/workflow/Edit'; +import Delete from '@spectrum-icons/workflow/Delete'; +import {Text} from '@react-spectrum/text'; +import {TreeView, TreeViewItem} from '@react-spectrum/tree'; +import {JSX} from "react"; +import {Key} from "@react-types/shared"; +import {ActionGroup, Item} from '@react-spectrum/actiongroup'; +import {ActionMenu} from '@react-spectrum/menu'; +``` + +--- +category: Collections +keywords: [tree, grid] +--- + +# TreeView + +{docs.exports.TreeView.description} + + + +## Example + +```tsx example + + + Documents + + + Project A + + + Weekly Report + + + + + Document 1 + + + + Document 2 + + + + + Photos + + + Image 1 + + + + Image 2 + + + + Image 3 + + + + +``` + +## Content +TreeView is a [collection component](../react-aria/collections.html) that provides users with a way to navigate nested hierarchical information. + +Basic usage of TreeView, seen in the example above, shows the use of a static collection where the contents of the TreeView are hard coded. Dynamic collections, as shown below, can be used when the options come from an external data source, such as an API, or update over time. Providing the data in this way allows TreeView to automatically cache the rendering of each item, which dramatically improves performance. + +Items can be statically defined as children, or generated dynamically using a function based on the data passed to the `items` prop. + +Each item has a unique key defined by the data. The `key` of each item element is implicitly defined by the id property of the item object. See [collections](../react-aria/collections.html#unique-ids) to learn more about keys in dynamic collections. + +Note: Asynchronous tree loading (i.e, loading by level or infinite scrolling) is not yet supported in TreeView. + +```tsx example export=true +type MyItem = { + id: string, + name: string, + icon: JSX.Element, + childItems?: MyItem[] +}; + +let items: MyItem[] = [ + {id: 'projects', name: 'Projects', icon: , childItems: [ + {id: 'project-1', name: 'Project 1', icon: }, + {id: 'project-2', name: 'Project 2', icon: , childItems: [ + {id: 'document-a', name: 'Document A', icon: }, + {id: 'document-b', name: 'Document B', icon: }, + ]} + ]}, + {id: 'reports', name: 'Reports', icon: , childItems: [ + {id: 'report-1', name: 'Reports 1', icon: } + ]} +]; + +function ExampleTree(props) { + return ( + + {(item: MyItem) => ( + + {item.name} + {item.icon} + + )} + + ); +} +``` + +### Internationalization +To internationalize a TreeView, all text content within the TreeView should be localized. This includes the `aria-label` provided to the TreeView if any. +For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of TreeView is automatically flipped. + +## Labeling +### Accessibility +An `aria-label` must be provided to the TreeView for accessibility. If the TreeView is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead. + +## Expansion + +By default, TreeView items are initially collapsed. Use `defaultExpandedKeys` to provide a default set of expanded items. Note that the value of the expanded keys must match the `id` prop of the TreeViewItem. + +The example below uses `defaultExpandedKeys` to select the items with keys "documents" and "photos". + +```tsx example + +``` + +### Controlled expansion + +To programmatically control item expansion, use the `expandedKeys` prop paired with the `onExpandedChange` callback. The `key` prop from the expanded items will +be passed into the callback when the item is pressed, allowing you to update state accordingly. + +Here is how you would control expansion for the above example. + +```tsx example +function ControlledExpansion() { + let [expandedKeys, setExpandedKeys] = React.useState>(new Set(['projects', 'reports'])); + + return ( + + ); +} +``` + +## Selection + +By default, TreeView doesn't allow item selection but this can be enabled using the `selectionMode` prop. +Use `defaultSelectedKeys` to provide a default set of selected items. Note that the value of the selected keys must match the `id` prop of the TreeViewItem. + +The example below enables multiple selection mode, and uses `defaultSelectedKeys` to select the items with keys "document-a" and "document-b". + +```tsx example + +``` + +### Controlled selection + +To programmatically control item selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected items will +be passed into the callback when the item is pressed, allowing you to update state accordingly. + +Here is how you would control selection for the above example. + +```tsx example +import type {Selection} from '@adobe/react-spectrum'; + +function ControlledSelection() { + let [selectedKeys, setSelectedKeys] = React.useState(new Set(['document-a', 'document-b'])); + + return ( + + ); +} +``` + +### Single selection + +To limit users to selecting only a single item at a time, `selectionMode` can be set to `single`. + +```tsx example + +``` + +### Disallow empty selection + +TreeView also supports a `disallowEmptySelection` prop which forces the user to have at least one item in the TreeView selected at all times. +In this mode, if a single item is selected and the user presses it, it will not be deselected. + +```tsx example + +``` + +### Disabled items + +You can disable specific items by providing an array of keys to TreeView via the `disabledKeys` prop. This will prevent items from being selectable as shown in the example below. + +```tsx example + +``` + +### Highlight selection + +By default, TreeView uses the checkbox selection style, which includes a checkbox in each item for selection. When the selectionStyle prop is set to `"highlight"`, the checkboxes are hidden, +and the selected items are displayed with a highlighted background instead. + +In addition to changing the appearance, the selection behavior also changes depending on the `selectionStyle` prop. In the default checkbox selection style, clicking, tapping, or pressing +the Space or Enter keys toggles selection for the focused item. Using the arrow keys moves focus but does not change selection. + +In the highlight selection style, however, clicking a item with the mouse _replaces_ the selection with only that item. Using the arrow keys moves both focus and selection. To select multiple items, +modifier keys such as Ctrl, Cmd, and Shift can be used. On touch screen devices, selection always behaves as toggle since modifier +keys may not be available. + +These selection styles implement the behaviors defined in [Aria Practices](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction). + +```tsx example + +``` + +## Actions + +### Item actions + +TreeView supports item actions via the `onAction` prop, which is useful for functionality such as navigation. When nothing is selected, the TreeView performs actions by default when clicking or tapping a item. +Items may be selected using the checkbox, or by long pressing on touch devices. When at least one item is selected, the TreeView is in selection mode, +and clicking or tapping a item toggles the selection. Actions may also be triggered via the Enter key, and selection using the Space key. + +This behavior is slightly different in the highlight selection style, where single clicking selects the item and actions are performed via double click. Touch and keyboard behaviors are unaffected. + +```tsx example + + alert(`Opening item ${key}...`)} + /*- end highlight -*/ + /> + alert(`Opening item ${key}...`)} + /*- end highlight -*/ + /> + +``` + +### Links + +Tree items may also be links to another page or website. This can be achieved by passing the `href` prop to the `` component. Links behave the same way as described above for item actions depending on the `selectionMode` and `selectionStyle`. + +```tsx example + + + Bookmarks + + + Adobe + + + + Google + + + + New York Times + + + + +``` + +#### Client side routing + +The `` component works with frameworks and client side routers like [Next.js](https://nextjs.org/) and [React Router](https://reactrouter.com/en/main). As with other React Spectrum components that support links, this works via the [Provider](Provider.html) component at the root of your app. See the [client side routing guide](routing.html) to learn how to set this up. + +### With action groups + +```tsx example +type MyItem = { + id: string, + name: string, + icon: JSX.Element, + childItems?: MyItem[] +}; + +let items: MyItem[] = [ + {id: 'projects', name: 'Projects', icon: , childItems: [ + {id: 'project-1', name: 'Project 1', icon: }, + {id: 'project-2', name: 'Project 2', icon: , childItems: [ + {id: 'document-a', name: 'Document A', icon: }, + {id: 'document-b', name: 'Document B', icon: }, + ]} + ]}, + {id: 'reports', name: 'Reports', icon: , childItems: [ + {id: 'report-1', name: 'Reports 1', icon: } + ]} +]; + + + {(item: MyItem) => ( + + {item.name} + {item.icon} + alert(`Item: ${item.id}, Action: ${key}`)}> + + + Edit + + + + Delete + + + + )} + +``` + +### With action menus + +```tsx example +type MyItem = { + id: string, + name: string, + icon: JSX.Element, + childItems?: MyItem[] +}; + +let items: MyItem[] = [ + {id: 'projects', name: 'Projects', icon: , childItems: [ + {id: 'project-1', name: 'Project 1', icon: }, + {id: 'project-2', name: 'Project 2', icon: , childItems: [ + {id: 'document-a', name: 'Document A', icon: }, + {id: 'document-b', name: 'Document B', icon: }, + ]} + ]}, + {id: 'reports', name: 'Reports', icon: , childItems: [ + {id: 'report-1', name: 'Reports 1', icon: } + ]} +]; + + + {(item: MyItem) => ( + + {item.name} + {item.icon} + alert(`Item: ${item.id}, Action: ${key}`)}> + + + Edit + + + + Delete + + + + )} + +``` + +## Props + +### TreeView props + + + +### TreeViewItem props + + + +## Visual options +### Empty state +Use the `renderEmptyState` prop to customize what the TreeView will display if there are no items provided. + +```tsx example +import {Content} from '@react-spectrum/view'; +import {IllustratedMessage} from '@react-spectrum/illustratedmessage'; +import NotFound from '@spectrum-icons/illustrations/NotFound'; +import {Heading} from '@react-spectrum/text'; + +function renderEmptyState() { + return ( + + + No results + No results found + + ); +} + + + {[]} + +``` + +## Testing + +The TreeView features long press interactions on its items depending on the item actions provided and if user is interacting with the list on +a touch device. Please see the following sections in the testing docs for more information on how to handle these +behaviors in your test suite. + +[Timers](./testing.html#timers) + +[Desktop vs Mobile](./testing.html#desktop-vs-mobile) + +[Long press](./testing.html#simulating-user-long-press) + +Please also refer to [React Spectrum's test suite](https://github.com/adobe/react-spectrum/blob/main/packages/%40react-spectrum/tree/test/TreeView.test.js) if you find that the above +isn't sufficient when resolving issues in your own test cases.