Skip to content

Commit

Permalink
fix(material/tree): add data model functions, improve a11y and update…
Browse files Browse the repository at this point in the history
… docs

Add functionality to the Tree data model, improve accessibility and update documentation.

Add levelAcessor and childrenAccessor functions. Given a data node, childrenAccessor determines the children of that node. Given a data node, levelAccessor determines level of the node. One of levelAccessor or childrenAccessor must be specified, not both.

CdkTreeNode#levelAcessor and CdkTreeNode#childrenAccessor replace CdkTreeNode#treeControl. See deprecations for updating apps using treeControl.

Implement keyboard navigation in Tree component. Keyboard commands match [WAI ARIA Tree View Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/).

Make changes directly to Cdk tree API, which also apply to Mat tree APIs. See “Breaking Change” and “Deprecated” sections below for specifics on adopting changes.

Recommend adopting levelAccessor or childrenAccessor to access all accessibility improvements.

Accessibility improvements
 * Implement keyboard navigation for CdkTree and MatTree
 * Improve keyboard usability for CdkTreeNodeToggle.
 * Improve ARIA semantics for CdkTree, CdkTreeNode, Tree and TreeNode components
 * Fix miscellaneous accessibility issues in tree and cdk*tree examples
 * Add accessibility instructions to documentation

Documentation updates
 * Add API and usage examples for TreeKeyManager
 * Update @angular/cdk/tree and @angular/material/tree to be more consistent
 * Update examples to use levelAccessor and childrenAccessor
 * Add example for (activation) on MatTreeNode and CdkTreeNode

API CHANGE: control expanded state of tree nodes using isExpandable and isExpanded
 * Add isExpandable function to NestedTreeControlOptions to determine if argument tree node can be expanded or collapsed.
 * Add isExpanded Input to CdkTreeNode to specify the expanded state. Has no effect if node is not expandable.

Note when upgrading: isExpandable works differently on Trees using treeControl than trees using childrenAccessor or levelAccessor. Nodes on trees that have a treeControl are expandable by default. Nodes on trees using childrenAccessor or levelAccessor are *not* expandable by default. Provide isExpandable to override default behavior.

For trees using treeControl, recommend providing isExpandable if not already provided.

API CHANGE: use CdkTree to manage expansion state
 * Add CdkTree#isExpanded method.
 * Add CdkTree#toggle, CdkTree#expand and CdkTree#collapse methods.
 * Add  CdkTree#toggleDescendants, CdkTree#expandDescendants, and CdkTree#collapseDescendants methods to CdkTree
 * Add CdkTree#expandAll and CdkTree#collapseAll methods
 * Add expandedChange Output to CdkTreeNode

BEHAVIOR CHANGE: MatTree and CdkTree components respond to keyboard navigation.
 * CdkTree and MatTree respond to arrow keys, page up, page down, etc.; Keyboard commands match [WAI ARIA Tree View Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/).
 * Can no longer set the tabindex on MatTreeNode. MatTreeNode ignores the passed value of tabindex. MatTreeNode always has a tabindex attribute of “-1”.
 * Add TreeKeyManager class
 * CdkTree consumes TreeKeyManager

DEPRECATED: Tree controller deprecated. Use one of levelAccessor or childrenAccessor instead. To be removed in a future version.
 * BaseTreeControl, TreeControl, FlatTreeControl, and NestedTreeControl deprecated
 * CdkTree#treeControl deprecated. Provide one of CdkTree#levelAccessor or CdkTree#childrenAccessor instead.
 * MatTreeFlattener deprecated. Use MatTree#childrenAccessor and MatTreeNode#isExpandable instead.
 * MatTreeFlatDataSource deprecated. Use one of levelAccessor or childrenAccessor instead of TreeControl.

DEPRECATED: Setting tabindex of tree nodes deprecated.
 * MatTreeNode#tabIndex deprecated. MatTreeNode ignores Input tabIndex and manages its own focus behavior.
 * MatTreeNode#defaultTabIndex deprecated. MatTreeNode ignores Input defaultTabIndex and manages its own focus behavior.
 * MatNestedTreeNode#tabIndex deprecated. MatTreeNode ignores Input defaultTabIndex and manages its own focus behavior.

DEPRECATED: disabled renamed to isDisabled.
 * CdkTreeNode#disabled deprecated and alias to CdkTreeNode#isDisabled
  • Loading branch information
mmalerba authored and zarend committed Oct 24, 2023
1 parent 382cc50 commit 732665d
Show file tree
Hide file tree
Showing 56 changed files with 7,762 additions and 748 deletions.
60 changes: 59 additions & 1 deletion src/cdk/a11y/a11y.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Navigation through options can be made to wrap via the `withWrap` method
this.keyManager = new FocusKeyManager(...).withWrap();
```

#### Types of key managers
#### Types of list key managers

There are two varieties of `ListKeyManager`, `FocusKeyManager` and `ActiveDescendantKeyManager`.

Expand Down Expand Up @@ -55,6 +55,64 @@ interface Highlightable extends ListKeyManagerOption {

Each item must also have an ID bound to the listbox's or menu's `aria-activedescendant`.

### TreeKeyManager

`TreeKeyManager` manages the active option in a tree view. This is intended to be used with
components that correspond to a `role="tree"` pattern.

#### Basic usage

Any component that uses a `TreeKeyManager` will generally do three things:
* Create a `@ViewChildren` query for the tree items being managed.
* Initialize the `TreeKeyManager`, passing in the options.
* Forward keyboard events from the managed component to the `TreeKeyManager` via `onKeydown`.

Each tree item should implement the `TreeKeyManagerItem` interface:
```ts
interface TreeKeyManagerItem {
/** Whether the item is disabled. */
isDisabled?: (() => boolean) | boolean;

/** The user-facing label for this item. */
getLabel?(): string;

/** Perform the main action (i.e. selection) for this item. */
activate(): void;

/** Retrieves the parent for this item. This is `null` if there is no parent. */
getParent(): TreeKeyManagerItem | null;

/** Retrieves the children for this item. */
getChildren(): TreeKeyManagerItem[] | Observable<TreeKeyManagerItem[]>;

/** Determines if the item is currently expanded. */
isExpanded: (() => boolean) | boolean;

/** Collapses the item, hiding its children. */
collapse(): void;

/** Expands the item, showing its children. */
expand(): void;

/**
* Focuses the item. This should provide some indication to the user that this item is focused.
*/
focus(): void;
}
```

#### Focus management

The `TreeKeyManager` will handle focusing the appropriate item on keyboard interactions. However,
the component should call `onInitialFocus` when the component is focused for the first time (i.e.
when there is no active item).

`tabindex` should also be set by the component when the active item changes. This can be listened to
via the `change` property on the `TreeKeyManager`. In particular, the tree should only have a
`tabindex` set if there is no active item, and should not have a `tabindex` set if there is an
active item. Only the HTML node corresponding to the active item should have a `tabindex` set to
`0`, with all other items set to `-1`.


### FocusTrap

Expand Down
Loading

0 comments on commit 732665d

Please sign in to comment.