Implements a tree view pattern using Accessible Rich Internet Applications (ARIA) and following the ARIA Authoring Practices Guide (APG).
Nodes can't be selected. Mainly adds keyboard interactions and focus management.
Almost conforming to the WAI-ARIA 1.2 specifications for trees. See conformance note for more information.
Check live demo at https://davidlj95.github.io/angular-accessible-tree/
Everything non-selectable except type-ahead from the authoring guides (mostly from first example):
- Focus is:
- On first visible tree node by default
- On last tree node explored after tree has been interacted with
- Home: Moves focus to first node without opening or closing a node.
- End: Moves focus to the last node that can be focused without expanding any nodes that are closed.
- Arrow down: Moves focus to the next node that is focusable without opening or closing a node. If focus is on the last node, does nothing.
- Arrow up: Moves focus to the previous node that is focusable without opening or closing a node. If focus is on the first node, does nothing.
- Right arrow: When focus is on a closed node, opens the node; focus does not move. When focus is on a open node, moves focus to the first child node. When focus is on an end node, does nothing.
- Left arrow: When focus is on an open node, closes the node. When focus is on a child node that is also either an end node or a closed node, moves focus to its parent node. When focus is on a root node that is also either an end node or a closed node, does nothing.
- Home: Moves focus to first node without opening or closing a node.
- End: Moves focus to the last node that can be focused without expanding any nodes that are closed.
- *: Expands all closed sibling nodes that are at the same level as the focused node. Focus does not move.
Plus a small quick win:
- Shift + *: Collapses all open sibling nodes that are at the same level as the focused node. Focus does not move.
ARIA tree roles are designed to select an option inside a tree. Spec says a widget with a tree
role is
A widget that allows the user to select one or more items from a hierarchically organized collection.
This implementation is missing feature to select something inside the tree. There's no clear way on how to implement a non-selectable tree (keep reading chapter below). Probably, disclosure pattern is the way to go in that scenario though. I switched to that for my website
There's an issue with trees in ARIA. Seems there's no agreement between ARIA practices and specifications on how to implement a non-selectable tree node.
If you check the ARIA Authoring Practices Guide (APG), seems that "Tree View" is the proper role for a hierarchical list:
"A tree view widget presents a hierarchical list" (source)
In the page regarding tree views, it is also added they can be expanded and collapsed:
"Any item in the hierarchy may have child items, and items that have children may be expanded or collapsed to show or hide the children" (source)
Later, it also points out what to do when nodes in the tree aren't selectable:
"If the tree contains nodes that are not selectable, neither
aria-selected
noraria-checked
is present on those nodes" (source)
Seems that's it, right? Started coding, until a wild linter warning appeared: aria-selected
must be present in a tree item (<li role="treeitem">
). But I don't want to make that tree node selectable 🤔 And the authoring practices guide says it's ok. Then why is that being raised? Going down the hole, found that
ARIA 1.2 specs actually don't allow a tree item to not have the aria-selected
attribute. Because aria-selected
attribute must be present on every tree node according to specs of a tree item. Given it inherits from the option
role
So seems there's a discrepancy about that between the ARIA Authoring Practices Guide (APG) and the ARIA-WAI 1.2 specs. Indeed, someone already reported that to the ARIA APG repo.
In summary, to be specs compliant, aria-selected
should be there for every tree item (node). Despite with a false
value as there's nothing to select. But if we do so, we tell a node can be selected, whilst we can't select anything here.
Sooo
TL;DR: there's no spec compliant way to implement a tree with non-selectable nodes
I eventually used the disclosure pattern for a non-selectable hierarchical list instead. Specifically, in my website. But if you want a tree with some selectable and some non-selectable nodes, you'll have to be a bit evil 😈 and be non-spec compliant.
In fact, there have also other places where they use tree item role, but no aria-selected
is there
<li id="apples" class="tree-parent" role="treeitem" tabindex="-1" aria-expanded="false">
aria-hidden
is not needed for collapsed items, givendisplay: none
hides it from accessibility tools already- For an example of
tabindex
usage in ARIA practices tree view example check the JS code. It's not in the inline HTML.
Run the app development server and play with it. There's instructions in the main app
component about what's implemented and what not.
This project was generated with Angular CLI version 16.2.3.
Run ng serve
for a dev server. Navigate to http://localhost:4200/
. The application will automatically reload if you change any of the source files.
Run ng generate component component-name
to generate a new component. You can also use ng generate directive|pipe|service|class|guard|interface|enum|module
.
Run ng build
to build the project. The build artifacts will be stored in the dist/
directory.
Run ng test
to execute the unit tests via Karma.
Run ng e2e
to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
To get more help on the Angular CLI use ng help
or go check out the Angular CLI Overview and Command Reference page.
- Add tests (got too excited with recursion and didn't TDD)