Skip to content

Commit

Permalink
fix(cdk/tree): CdkTreeNodeToggle responds to Enter and Space keys
Browse files Browse the repository at this point in the history
Implement keydown event for CdkTreeNodeToggle to perform that same
action as click when receiving Enter or Space. Update examples to
expand/collapse nodes when pressing enter.

Fix a11y issue in demos where pressing Enter when focused on a tree node
seems to not perform any action. Use CdkTreeNodeToggle to perform the
action of expanding or collaping the node.

Align with instructions in [APG Tree View
Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/):

"Enter: activates a node, i.e., performs its default action. For parent
nodes, one possible default action is to open or close the node. In
single-select trees where selection does not follow focus (see note
below), the default action is typically to select the focused node."
  • Loading branch information
zarend committed Aug 29, 2023
1 parent 919dbb9 commit 3665278
Show file tree
Hide file tree
Showing 14 changed files with 33 additions and 9 deletions.
2 changes: 2 additions & 0 deletions src/cdk/tree/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ ng_test_library(
":tree",
"//src/cdk/bidi",
"//src/cdk/collections",
"//src/cdk/keycodes",
"//src/cdk/testing/testbed",
"@npm//rxjs",
],
)
Expand Down
12 changes: 12 additions & 0 deletions src/cdk/tree/toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@

import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';
import {Directive, Input} from '@angular/core';
import {ENTER, SPACE} from '@angular/cdk/keycodes';

import {CdkTree, CdkTreeNode} from './tree';

/**
* Node toggle to expand/collapse the node.
*
* CdkTreeNodeToggle is intended only to be used on native button elements, elements with button role,
* or elements with treeitem role.
*/
@Directive({
selector: '[cdkTreeNodeToggle]',
host: {
'(click)': '_toggle($event)',
'(keydown)': '_toggleOnEnterOrSpace($event)',
'tabindex': '-1',
},
})
Expand All @@ -41,4 +46,11 @@ export class CdkTreeNodeToggle<T, K = T> {

event.stopPropagation();
}

_toggleOnEnterOrSpace(event: KeyboardEvent) {
if (event.keyCode === ENTER || event.keyCode === SPACE) {
this._toggle(event);
event.preventDefault();
}
}
}
8 changes: 5 additions & 3 deletions src/cdk/tree/tree-redesign.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ import {
ViewChildren,
QueryList,
} from '@angular/core';

import {CollectionViewer, DataSource} from '@angular/cdk/collections';
import {Directionality, Direction} from '@angular/cdk/bidi';
import {combineLatest, BehaviorSubject, Observable} from 'rxjs';
import {map} from 'rxjs/operators';

import {CdkTreeModule, CdkTreeNodePadding} from './index';
import {CdkTree, CdkTreeNode} from './tree';
import {createKeyboardEvent} from '@angular/cdk/testing/testbed/fake-events';
import {ENTER} from '@angular/cdk/keycodes';

/**
* This is a cloned version of `tree.spec.ts` that contains all the same tests,
Expand Down Expand Up @@ -293,7 +293,9 @@ describe('CdkTree redesign', () => {
[_, `${data[3].pizzaTopping} - ${data[3].pizzaCheese} + ${data[3].pizzaBase}`],
);

(getNodes(treeElement)[2] as HTMLElement).click();
(getNodes(treeElement)[2] as HTMLElement)!.dispatchEvent(
createKeyboardEvent('keydown', ENTER),
);
fixture.detectChanges();

const expandedNodes = getExpandedNodes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</cdk-tree-node>
<!-- This is the tree node template for expandable nodes -->
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
cdkTreeNodeToggle
[style.display]="shouldRender(node) ? 'flex' : 'none'"
[isDisabled]="!shouldRender(node)"
[isExpandable]="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</cdk-tree-node>
<!-- This is the tree node template for expandable nodes -->
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
cdkTreeNodeToggle
[style.display]="shouldRender(node) ? 'flex' : 'none'"
[isDisabled]="!shouldRender(node)"
[isExpandable]="node.expandable"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</cdk-tree-node>
<!-- This is the tree node template for expandable nodes -->
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
cdkTreeNodeToggle
[style.display]="shouldRender(node) ? 'flex' : 'none'"
[isDisabled]="!shouldRender(node)"
(expandedChange)="node.isExpanded = $event"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</cdk-nested-tree-node>
<!-- This is the tree node template for expandable nodes -->
<cdk-nested-tree-node *cdkTreeNodeDef="let node; when: hasChild"
cdkTreeNodeToggle
[isExpandable]="node.expandable"
[isDisabled]="!shouldRender(node)"
class="example-tree-node example-expandable">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</cdk-nested-tree-node>
<!-- This is the tree node template for expandable nodes -->
<cdk-nested-tree-node #treeNode="cdkNestedTreeNode"
cdkTreeNodeToggle
*cdkTreeNodeDef="let node; when: hasChild"
isExpandable
class="example-tree-node">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<button mat-icon-button disabled></button>
{{node.item}}
</mat-tree-node>
<mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding>
<mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding matTreeNodeToggle>
<button mat-icon-button
[attr.aria-label]="'Toggle ' + node.item" matTreeNodeToggle>
<mat-icon class="mat-icon-rtl-mirror">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{{node.name}}
</mat-tree-node>
<!-- This is the tree node template for expandable nodes -->
<mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding>
<mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding matTreeNodeToggle>
<button mat-icon-button matTreeNodeToggle
[attr.aria-label]="'Toggle ' + node.name">
<mat-icon class="mat-icon-rtl-mirror">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{{node.name}}
</mat-tree-node>
<!-- This is the tree node template for expandable nodes -->
<mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding>
<mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding matTreeNodeToggle>
<button mat-icon-button matTreeNodeToggle
[attr.aria-label]="'Toggle ' + node.name">
<mat-icon class="mat-icon-rtl-mirror">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</mat-tree-node>

<!-- expandable node -->
<mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding>
<mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding matTreeNodeToggle>
<button mat-icon-button
[attr.aria-label]="'Toggle ' + node.item"
(click)="loadChildren(node)"
Expand All @@ -18,7 +18,7 @@
{{node.item}}
</mat-tree-node>

<mat-tree-node *matTreeNodeDef="let node; when: isLoadMore">
<mat-tree-node *matTreeNodeDef="let node; when: isLoadMore" matTreeNodeToggle>
<button mat-button (click)="loadMore(node.loadMoreParentItem)">
Load more...
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
<!-- This is the tree node template for leaf nodes -->
<!-- There is inline padding applied to this node using styles.
This padding value depends on the mat-icon-button width. -->
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
<mat-tree-node *matTreeNodeDef="let node">
{{node.name}}
</mat-tree-node>
<!-- This is the tree node template for expandable nodes -->
<mat-nested-tree-node
*matTreeNodeDef="let node; when: hasChild"
matTreeNodeToggle
isExpandable>
<div class="mat-tree-node">
<button mat-icon-button matTreeNodeToggle
Expand Down
2 changes: 2 additions & 0 deletions tools/public_api_guard/cdk/tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ export class CdkTreeNodeToggle<T, K = T> {
// (undocumented)
_toggle(event: Event): void;
// (undocumented)
_toggleOnEnterOrSpace(event: KeyboardEvent): void;
// (undocumented)
protected _tree: CdkTree<T, K>;
// (undocumented)
protected _treeNode: CdkTreeNode<T, K>;
Expand Down

0 comments on commit 3665278

Please sign in to comment.