Skip to content

Commit

Permalink
[TreeView] Fix usage of the aria-selected attribute (#14991)
Browse files Browse the repository at this point in the history
  • Loading branch information
flaviendelangle authored Oct 16, 2024
1 parent 78cb399 commit 19d2255
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 25 deletions.
22 changes: 11 additions & 11 deletions packages/x-tree-view/src/TreeItem/TreeItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export const TreeItem = React.forwardRef(function TreeItem(
icons: contextIcons,
runItemPlugins,
items: { disabledItemsFocusable, indentationAtItemLevel },
selection: { multiSelect },
selection: { disableSelection },
expansion: { expansionTrigger },
treeId,
instance,
Expand Down Expand Up @@ -358,17 +358,17 @@ export const TreeItem = React.forwardRef(function TreeItem(
});
const icon = Icon ? <Icon {...iconProps} /> : null;

let ariaSelected;
if (multiSelect) {
ariaSelected = selected;
} else if (selected) {
/* single-selection trees unset aria-selected on un-selected items.
*
* If the tree does not support multiple selection, aria-selected
* is set to true for the selected item and it is not present on any other item in the tree.
* Source: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/
*/
// https://www.w3.org/WAI/ARIA/apg/patterns/treeview/
let ariaSelected: boolean | undefined;
if (selected) {
// - each selected node has aria-selected set to true.
ariaSelected = true;
} else if (disableSelection || disabled) {
// - if the tree contains nodes that are not selectable, aria-selected is not present on those nodes.
ariaSelected = undefined;
} else {
// - all nodes that are selectable but not selected have aria-selected set to false.
ariaSelected = false;
}

function handleFocus(event: React.FocusEvent<HTMLLIElement>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -712,12 +712,12 @@ describeTreeView<[UseTreeViewSelectionSignature, UseTreeViewExpansionSignature]>
// This `describe` only tests basics scenarios, more complex scenarios are tested in this file's other `describe`.
describe('aria-selected item attribute', () => {
describe('single selection', () => {
it('should not have the attribute `aria-selected=false` if not selected', () => {
it('should have the attribute `aria-selected=false` if not selected', () => {
const view = render({
items: [{ id: '1' }, { id: '2' }],
});

expect(view.getItemRoot('1')).not.to.have.attribute('aria-selected');
expect(view.getItemRoot('1')).to.have.attribute('aria-selected', 'false');
});

it('should have the attribute `aria-selected=true` if selected', () => {
Expand Down Expand Up @@ -750,14 +750,23 @@ describeTreeView<[UseTreeViewSelectionSignature, UseTreeViewExpansionSignature]>
expect(view.getItemRoot('1')).to.have.attribute('aria-selected', 'true');
});

it('should have the attribute `aria-selected=false` if disabledSelection is true', () => {
it('should not have the attribute `aria-selected=false` if disabledSelection is true', () => {
const view = render({
multiSelect: true,
items: [{ id: '1' }, { id: '2' }],
disableSelection: true,
});

expect(view.getItemRoot('1')).to.have.attribute('aria-selected', 'false');
expect(view.getItemRoot('1')).not.to.have.attribute('aria-selected');
});

it('should not have the attribute `aria-selected=false` if the item is disabled', () => {
const view = render({
multiSelect: true,
items: [{ id: '1', disabled: true }, { id: '2' }],
});

expect(view.getItemRoot('1')).not.to.have.attribute('aria-selected');
});
});
});
Expand Down
20 changes: 10 additions & 10 deletions packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const useTreeItem2 = <
const {
runItemPlugins,
items: { onItemClick, disabledItemsFocusable, indentationAtItemLevel },
selection: { multiSelect, disableSelection, checkboxSelection },
selection: { disableSelection, checkboxSelection },
expansion: { expansionTrigger },
treeId,
instance,
Expand Down Expand Up @@ -194,17 +194,17 @@ export const useTreeItem2 = <
...extractEventHandlers(externalProps),
};

// https://www.w3.org/WAI/ARIA/apg/patterns/treeview/
let ariaSelected: boolean | undefined;
if (multiSelect) {
ariaSelected = status.selected;
} else if (status.selected) {
/* single-selection trees unset aria-selected on un-selected items.
*
* If the tree does not support multiple selection, aria-selected
* is set to true for the selected item and it is not present on any other item in the tree.
* Source: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/
*/
if (status.selected) {
// - each selected node has aria-selected set to true.
ariaSelected = true;
} else if (disableSelection || status.disabled) {
// - if the tree contains nodes that are not selectable, aria-selected is not present on those nodes.
ariaSelected = undefined;
} else {
// - all nodes that are selectable but not selected have aria-selected set to false.
ariaSelected = false;
}

const props: UseTreeItem2RootSlotPropsFromUseTreeItem = {
Expand Down

0 comments on commit 19d2255

Please sign in to comment.