diff --git a/change/@fluentui-react-tree-e2a6aab0-e13f-4c01-9e0b-8f2b591adbd9.json b/change/@fluentui-react-tree-e2a6aab0-e13f-4c01-9e0b-8f2b591adbd9.json new file mode 100644 index 0000000000000..12368fe2f89c4 --- /dev/null +++ b/change/@fluentui-react-tree-e2a6aab0-e13f-4c01-9e0b-8f2b591adbd9.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: default selection behavior is accessible, with warnings", + "packageName": "@fluentui/react-tree", + "email": "sarah.higley@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.tsx b/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.tsx index 02d204fdb4ce5..d80b7d3eb976a 100644 --- a/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.tsx +++ b/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.tsx @@ -120,7 +120,14 @@ export function useTreeItem_unstable(props: TreeItemProps, ref: React.Ref ⚠️ Although \`actions\` are easy to navigate, they're not an expected pattern according to [WAI-ARIA](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/).providing a context menu with the same functionalities as the actions is recommended to ensure your tree item is accessible. +> ⚠️ Although \`aria-actions\` will improve accessibility in the future, the current state of actions is that they are inaccessible across multiple platforms and input modalities, such as voice control. They are also not allowed children of a treeitem per the ARIA spec. Providing a context menu with the same functionalities as the actions is recommended to improve the accessibility of actions. In the example below, we compose on top of \`TreeItem\` component to include both a context menu and actions that provide the same amount of functionalities. We also provide an \`aria-description\` to the tree item to indicate that it has actions. This is a new behavior that the user might not be aware of, so you might need to explain somewhere else in the UI what does having actions refers to. diff --git a/packages/react-components/react-tree/stories/src/Tree/TreeCustomizingInteraction.stories.tsx b/packages/react-components/react-tree/stories/src/Tree/TreeCustomizingInteraction.stories.tsx deleted file mode 100644 index 94b1f58aebdc7..0000000000000 --- a/packages/react-components/react-tree/stories/src/Tree/TreeCustomizingInteraction.stories.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from 'react'; -import type { JSXElement, TreeItemValue, TreeOpenChangeData, TreeOpenChangeEvent } from '@fluentui/react-components'; -import { Tree, TreeItem, TreeItemLayout } from '@fluentui/react-components'; - -export const CustomizingInteraction = (): JSXElement => { - const [openItems, setOpenItems] = React.useState>([]); - const onOpenChange = (_e: TreeOpenChangeEvent, data: TreeOpenChangeData) => { - if (data.type === 'Click' || data.type === 'Enter') { - alert('click on item'); - return; - } - setOpenItems(data.openItems); - }; - return ( - - - level 1, item 1 - - - level 2, item 1 - - - level 2, item 2 - - - - - level 1, item 2 - - - level 2, item 1 - - - level 3, item 1 - - - - - - - ); -}; - -CustomizingInteraction.parameters = { - docs: { - description: { - story: ` -By default, every expandable TreeItem responds to clicks on both content and the expand/collapse icon. To handle these separately, listen for the \`onOpenChange\` event in the \`Tree\` component. You can check the event type to determine whether the content or the icon was clicked, allowing you to override the default behavior. - `, - }, - }, -}; diff --git a/packages/react-components/react-tree/stories/src/Tree/TreeSelection.stories.tsx b/packages/react-components/react-tree/stories/src/Tree/TreeSelection.stories.tsx index 8e8ca089d06ed..b9f653050a218 100644 --- a/packages/react-components/react-tree/stories/src/Tree/TreeSelection.stories.tsx +++ b/packages/react-components/react-tree/stories/src/Tree/TreeSelection.stories.tsx @@ -46,6 +46,10 @@ Selection.parameters = { story: ` The tree component offers selectable functionality in both single and multi-selection modes. You can enable this feature by passing the \`selectionMode\` prop with either \`single\` or \`multiselect\` value. +⚠️ WARNING: trees that have multiple possible interactions on the same tree item (e.g. selection + a primary action, or selection + expand/collapse) are fundamentally inaccessible across multiple modalities, specifically mobile screen readers, VoiceOver on macOS, and voice control. Trees generally are not broadly accessible, and should be used very infrequently if at all. If you create a selectable tree, the best practice is to not also define an onClick for TreeItems. + +Controlling selection state: + - \`Tree\`: In nested tree, you are responsible for controlling the selection state, as it would be difficult to manage the state in an uncontrolled manner without knowing the items upfront. - \`FlatTree\`: In flat tree, you can take advantage of an uncontrolled state for easier management, as the items are known upfront. It is also possible to use a controlled state if you need to manage the selection state externally. diff --git a/packages/react-components/react-tree/stories/src/Tree/index.stories.tsx b/packages/react-components/react-tree/stories/src/Tree/index.stories.tsx index e4e869454b393..500eb9a159b26 100644 --- a/packages/react-components/react-tree/stories/src/Tree/index.stories.tsx +++ b/packages/react-components/react-tree/stories/src/Tree/index.stories.tsx @@ -26,7 +26,6 @@ export { NavigationModeTreeGrid } from './TreeNavigationModeTreeGrid.stories'; export { DefaultOpen } from './TreeDefaultOpen.stories'; export { OpenItemsControlled } from './OpenItemsControlled.stories'; export { OpenItemControlled } from './OpenItemControlled.stories'; -export { CustomizingInteraction } from './TreeCustomizingInteraction.stories'; export { InlineStylingTreeItemLevel } from './TreeInlineStylingTreeItemLevel.stories'; export { FlatTreeStory as FlatTree } from './FlatTree.stories';