Skip to content

Commit cb05f21

Browse files
BobobUnicornandrewseguin
authored andcommitted
feat(cdk/tree): improve aria attributes (#24658)
* feat(cdk/tree): add demos to the dev-app * feat(cdk/tree): add flat-node with levelAccessor example to the demo page * feat(cdk/tree): move new demos to cdk-tree-redesign dir * fix(cdk/tree): fix unused error * feat(cdk/tree): move demos back to their own dirs * fix(cdk/tree): address review comments * feat(cdk/tree): use _getDirectChildren method in nested node * feat(cdk/tree): add cache of nodes to the tree * fix(cdk/tree): fix cherry-pick errors * feat(cdk/tree): add translation layer for nested nodes using levelAccessor * feat(cdk/tree): add example with nested nodes & level accessor * feat(cdk/tree): fix examples * feat(cdk/tree): add example with flat nodes & childrenAccessor * feat(cdk/tree): flatten data that uses childrenAccessor * fix(cdk/tree): fix padding not showing for `childrenAccessor` trees * fix(cdk/tree): fix flat tree demo * fix(cdk/tree): convert generator function to return a regular array in demo * fix(cdk/tree): fix build error * feat(cdk/tree): update API goldens * fix(cdk/tree): fix some failing tests, one remaining * fix(cdk/tree): fix test errors and children conversion; also make `renderNodeChanges` private * fix(cdk/tree): update api goldens * fix(cdk/tree): fix lint errors * feat(cdk/tree): deprecate the TreeControl APIs * fix(cdk/tree): make the toggle button not focusable by keyboard * fix(cdk/tree): add aria attributes for the flat nodes * feat(cdk/tree): maintain a cache of parents and node groups * feat(cdk/tree): implement aria-posinset and aria-setsize * feat(cdk/tree): update goldens, add descriptions to methods * feat(cdk/tree): fix lint errors * fix(cdk/tree): fix build errors * fix(cdk/tree): change explicit breaking-change version to PLACEHOLDER * fix(cdk/tree): also update flat level accessor example * fix(cdk/tree): update API goldens * fix(cdk/tree): update API goldens * feat(cdk/tree): add a translation function to the tree to get children * fix(cdk/tree): lint
1 parent f4846bf commit cb05f21

24 files changed

+682
-158
lines changed

src/cdk/tree/control/base-tree-control.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ import {SelectionModel} from '@angular/cdk/collections';
99
import {Observable} from 'rxjs';
1010
import {TreeControl} from './tree-control';
1111

12-
/** Base tree control. It has basic toggle/expand/collapse operations on a single data node. */
12+
/**
13+
* Base tree control. It has basic toggle/expand/collapse operations on a single data node.
14+
*
15+
* @deprecated Use one of levelAccessor or childrenAccessor
16+
* @breaking-change 0.0.0-PLACEHOLDER
17+
*/
1318
export abstract class BaseTreeControl<T, K = T> implements TreeControl<T, K> {
1419
/** Gets a list of descendent data nodes of a subtree rooted at given data node recursively. */
1520
abstract getDescendants(dataNode: T): T[];

src/cdk/tree/control/flat-tree-control.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ export interface FlatTreeControlOptions<T, K> {
1313
trackBy?: (dataNode: T) => K;
1414
}
1515

16-
/** Flat tree control. Able to expand/collapse a subtree recursively for flattened tree. */
16+
/**
17+
* Flat tree control. Able to expand/collapse a subtree recursively for flattened tree.
18+
*
19+
* @deprecated Use one of levelAccessor or childrenAccessor
20+
* @breaking-change 14.0.0
21+
*/
1722
export class FlatTreeControl<T, K = T> extends BaseTreeControl<T, K> {
1823
/** Construct with flat tree data node functions getLevel and isExpandable. */
1924
constructor(

src/cdk/tree/control/nested-tree-control.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ export interface NestedTreeControlOptions<T, K> {
1414
trackBy?: (dataNode: T) => K;
1515
}
1616

17-
/** Nested tree control. Able to expand/collapse a subtree recursively for NestedNode type. */
17+
/**
18+
* Nested tree control. Able to expand/collapse a subtree recursively for NestedNode type.
19+
*
20+
* @deprecated Use one of levelAccessor or childrenAccessor
21+
* @breaking-change 14.0.0
22+
*/
1823
export class NestedTreeControl<T, K = T> extends BaseTreeControl<T, K> {
1924
/** Construct with nested tree function getChildren. */
2025
constructor(

src/cdk/tree/control/tree-control.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import {Observable} from 'rxjs';
1212
* Tree control interface. User can implement TreeControl to expand/collapse dataNodes in the tree.
1313
* The CDKTree will use this TreeControl to expand/collapse a node.
1414
* User can also use it outside the `<cdk-tree>` to control the expansion status of the tree.
15+
*
16+
* @deprecated Use one of levelAccessor or childrenAccessor
17+
* @breaking-change 14.0.0
1518
*/
1619
export interface TreeControl<T, K = T> {
1720
/** The saved tree nodes data for `expandAll` action. */

src/cdk/tree/nested-node.ts

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@ import {
1616
OnInit,
1717
QueryList,
1818
} from '@angular/core';
19-
import {isObservable} from 'rxjs';
2019
import {takeUntil} from 'rxjs/operators';
2120

2221
import {CDK_TREE_NODE_OUTLET_NODE, CdkTreeNodeOutlet} from './outlet';
2322
import {CdkTree, CdkTreeNode} from './tree';
24-
import {getTreeControlFunctionsMissingError} from './tree-errors';
2523

2624
/**
2725
* Nested node is a child of `<cdk-tree>`. It works with nested tree.
@@ -69,22 +67,13 @@ export class CdkNestedTreeNode<T, K = T>
6967

7068
ngAfterContentInit() {
7169
this._dataDiffer = this._differs.find([]).create(this._tree.trackBy);
72-
const childrenAccessor = this._tree._getChildrenAccessor();
73-
if (!childrenAccessor && (typeof ngDevMode === 'undefined' || ngDevMode)) {
74-
throw getTreeControlFunctionsMissingError();
75-
} else if (childrenAccessor) {
76-
const childrenNodes = childrenAccessor(this.data);
77-
if (Array.isArray(childrenNodes)) {
78-
this.updateChildrenNodes(childrenNodes as T[]);
79-
} else if (isObservable(childrenNodes)) {
80-
childrenNodes
81-
.pipe(takeUntil(this._destroyed))
82-
.subscribe(result => this.updateChildrenNodes(result));
83-
}
84-
this.nodeOutlet.changes
85-
.pipe(takeUntil(this._destroyed))
86-
.subscribe(() => this.updateChildrenNodes());
87-
}
70+
this._tree
71+
._getDirectChildren(this.data)
72+
.pipe(takeUntil(this._destroyed))
73+
.subscribe(result => this.updateChildrenNodes(result));
74+
this.nodeOutlet.changes
75+
.pipe(takeUntil(this._destroyed))
76+
.subscribe(() => this.updateChildrenNodes());
8877
}
8978

9079
// This is a workaround for https://github.com/angular/angular/issues/23091
@@ -106,7 +95,7 @@ export class CdkNestedTreeNode<T, K = T>
10695
}
10796
if (outlet && this._children) {
10897
const viewContainer = outlet.viewContainer;
109-
this._tree.renderNodeChanges(this._children, this._dataDiffer, viewContainer, this._data);
98+
this._tree._renderNodeChanges(this._children, this._dataDiffer, viewContainer, this._data);
11099
} else {
111100
// Reset the data differ if there's no children nodes displayed
112101
this._dataDiffer.diff([]);

src/cdk/tree/padding.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,7 @@ export class CdkTreeNodePadding<T, K = T> implements OnDestroy {
8080

8181
/** The padding indent value for the tree node. Returns a string with px numbers if not null. */
8282
_paddingIndent(): string | null {
83-
const nodeLevel =
84-
(this._treeNode.data && this._tree._getLevelAccessor()?.(this._treeNode.data)) ?? null;
83+
const nodeLevel = (this._treeNode.data && this._tree._getLevel(this._treeNode.data)) ?? null;
8584
const level = this._level == null ? nodeLevel : this._level;
8685
return typeof level === 'number' ? `${level * this._indent}${this.indentUnits}` : null;
8786
}

src/cdk/tree/toggle.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {CdkTree, CdkTreeNode} from './tree';
1818
selector: '[cdkTreeNodeToggle]',
1919
host: {
2020
'(click)': '_toggle($event)',
21+
'tabindex': '0',
2122
},
2223
})
2324
export class CdkTreeNodeToggle<T, K = T> {

src/cdk/tree/tree-errors.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ export function getMultipleTreeControlsError() {
4848
}
4949

5050
/**
51-
* Returns an error to be thrown when tree control did not implement functions for flat/nested node.
51+
* Returns an error to be thrown when the node type is not specified.
5252
* @docs-private
5353
*/
54-
export function getTreeControlFunctionsMissingError() {
55-
return Error(`Could not find functions for nested/flat tree in tree control.`);
54+
export function getTreeControlNodeTypeUnspecifiedError() {
55+
return Error(`The nodeType was not specified for the tree.`);
5656
}

src/cdk/tree/tree-redesign.spec.ts

Lines changed: 31 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {ComponentFixture, TestBed, fakeAsync, flush} from '@angular/core/testing';
8+
import {ComponentFixture, TestBed} from '@angular/core/testing';
99
import {
1010
Component,
1111
ErrorHandler,
@@ -24,7 +24,6 @@ import {map} from 'rxjs/operators';
2424

2525
import {CdkTreeModule, CdkTreeNodePadding} from './index';
2626
import {CdkTree, CdkTreeNode} from './tree';
27-
import {getTreeControlFunctionsMissingError} from './tree-errors';
2827

2928
/**
3029
* This is a cloned version of `tree.spec.ts` that contains all the same tests,
@@ -1127,20 +1126,6 @@ describe('CdkTree redesign', () => {
11271126
expect(changedNodes[5].getAttribute('initialIndex')).toBe('2');
11281127
});
11291128
});
1130-
1131-
it('should throw an error when missing function in nested tree', fakeAsync(() => {
1132-
configureCdkTreeTestingModule([NestedCdkErrorTreeApp]);
1133-
expect(() => {
1134-
try {
1135-
TestBed.createComponent(NestedCdkErrorTreeApp).detectChanges();
1136-
flush();
1137-
} catch {
1138-
flush();
1139-
} finally {
1140-
flush();
1141-
}
1142-
}).toThrowError(getTreeControlFunctionsMissingError().message);
1143-
}));
11441129
});
11451130

11461131
describe('with depth', () => {
@@ -1356,7 +1341,8 @@ function expectNestedTreeToMatch(treeElement: Element, ...expectedTree: any[]) {
13561341

13571342
@Component({
13581343
template: `
1359-
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel">
1344+
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel"
1345+
nodeType="flat">
13601346
<cdk-tree-node *cdkTreeNodeDef="let node" class="customNodeClass"
13611347
cdkTreeNodePadding [cdkTreeNodePaddingIndent]="indent"
13621348
cdkTreeNodeToggle
@@ -1383,7 +1369,8 @@ class SimpleCdkTreeApp {
13831369

13841370
@Component({
13851371
template: `
1386-
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel">
1372+
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel"
1373+
nodeType="flat">
13871374
<ng-container [ngSwitch]="true">
13881375
<cdk-tree-node *cdkTreeNodeDef="let node" class="customNodeClass"
13891376
cdkTreeNodePadding [cdkTreeNodePaddingIndent]="indent"
@@ -1399,7 +1386,8 @@ class SimpleCdkTreeAppWithIndirectNodes extends SimpleCdkTreeApp {}
13991386

14001387
@Component({
14011388
template: `
1402-
<cdk-tree [dataSource]="dataSource" [childrenAccessor]="getChildren">
1389+
<cdk-tree [dataSource]="dataSource" [childrenAccessor]="getChildren"
1390+
nodeType="nested">
14031391
<cdk-nested-tree-node *cdkTreeNodeDef="let node" class="customNodeClass">
14041392
{{node.pizzaTopping}} - {{node.pizzaCheese}} + {{node.pizzaBase}}
14051393
<ng-template cdkTreeNodeOutlet></ng-template>
@@ -1417,7 +1405,8 @@ class NestedCdkTreeApp {
14171405

14181406
@Component({
14191407
template: `
1420-
<cdk-tree [dataSource]="dataSource" [childrenAccessor]="getChildren">
1408+
<cdk-tree [dataSource]="dataSource" [childrenAccessor]="getChildren"
1409+
nodeType="nested">
14211410
<cdk-nested-tree-node *cdkTreeNodeDef="let node" class="customNodeClass">
14221411
{{node.pizzaTopping}} - {{node.pizzaCheese}} + {{node.pizzaBase}}
14231412
<ng-template cdkTreeNodeOutlet></ng-template>
@@ -1445,7 +1434,8 @@ class StaticNestedCdkTreeApp {
14451434

14461435
@Component({
14471436
template: `
1448-
<cdk-tree [dataSource]="dataSource" [childrenAccessor]="getChildren">
1437+
<cdk-tree [dataSource]="dataSource" [childrenAccessor]="getChildren"
1438+
nodeType="nested">
14491439
<cdk-nested-tree-node *cdkTreeNodeDef="let node" class="customNodeClass">
14501440
{{node.pizzaTopping}} - {{node.pizzaCheese}} + {{node.pizzaBase}}
14511441
<ng-template cdkTreeNodeOutlet></ng-template>
@@ -1469,7 +1459,8 @@ class WhenNodeNestedCdkTreeApp {
14691459

14701460
@Component({
14711461
template: `
1472-
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel">
1462+
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel"
1463+
nodeType="flat">
14731464
<cdk-tree-node *cdkTreeNodeDef="let node" class="customNodeClass"
14741465
cdkTreeNodePadding
14751466
cdkTreeNodeToggle [cdkTreeNodeToggleRecursive]="toggleRecursively"
@@ -1492,7 +1483,8 @@ class CdkTreeAppWithToggle {
14921483

14931484
@Component({
14941485
template: `
1495-
<cdk-tree #tree [dataSource]="dataSource" [childrenAccessor]="getChildren">
1486+
<cdk-tree #tree [dataSource]="dataSource" [childrenAccessor]="getChildren"
1487+
nodeType="nested">
14961488
<cdk-nested-tree-node *cdkTreeNodeDef="let node" class="customNodeClass"
14971489
cdkTreeNodeToggle [cdkTreeNodeToggleRecursive]="toggleRecursively">
14981490
{{node.pizzaTopping}} - {{node.pizzaCheese}} + {{node.pizzaBase}}
@@ -1515,7 +1507,8 @@ class NestedCdkTreeAppWithToggle {
15151507

15161508
@Component({
15171509
template: `
1518-
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel">
1510+
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel"
1511+
nodeType="flat">
15191512
<cdk-tree-node *cdkTreeNodeDef="let node" class="customNodeClass"
15201513
cdkTreeNodePadding [cdkTreeNodePaddingIndent]="28"
15211514
cdkTreeNodeToggle
@@ -1543,7 +1536,8 @@ class WhenNodeCdkTreeApp {
15431536

15441537
@Component({
15451538
template: `
1546-
<cdk-tree [dataSource]="dataArray" [levelAccessor]="getLevel">
1539+
<cdk-tree [dataSource]="dataArray" [levelAccessor]="getLevel"
1540+
nodeType="flat">
15471541
<cdk-tree-node *cdkTreeNodeDef="let node"
15481542
cdkTreeNodePadding [cdkTreeNodePaddingIndent]="28"
15491543
cdkTreeNodeToggle
@@ -1568,7 +1562,8 @@ class ArrayDataSourceCdkTreeApp {
15681562

15691563
@Component({
15701564
template: `
1571-
<cdk-tree [dataSource]="dataObservable" [levelAccessor]="getLevel">
1565+
<cdk-tree [dataSource]="dataObservable" [levelAccessor]="getLevel"
1566+
nodeType="flat">
15721567
<cdk-tree-node *cdkTreeNodeDef="let node"
15731568
cdkTreeNodePadding [cdkTreeNodePaddingIndent]="28"
15741569
cdkTreeNodeToggle
@@ -1593,7 +1588,8 @@ class ObservableDataSourceCdkTreeApp {
15931588

15941589
@Component({
15951590
template: `
1596-
<cdk-tree [dataSource]="dataArray" [childrenAccessor]="getChildren">
1591+
<cdk-tree [dataSource]="dataArray" [childrenAccessor]="getChildren"
1592+
nodeType="nested">
15971593
<cdk-nested-tree-node *cdkTreeNodeDef="let node">
15981594
[{{node.pizzaTopping}}] - [{{node.pizzaCheese}}] + [{{node.pizzaBase}}]
15991595
<ng-template cdkTreeNodeOutlet></ng-template>
@@ -1615,7 +1611,8 @@ class ArrayDataSourceNestedCdkTreeApp {
16151611

16161612
@Component({
16171613
template: `
1618-
<cdk-tree [dataSource]="dataObservable" [childrenAccessor]="getChildren">
1614+
<cdk-tree [dataSource]="dataObservable" [childrenAccessor]="getChildren"
1615+
nodeType="nested">
16191616
<cdk-nested-tree-node *cdkTreeNodeDef="let node">
16201617
[{{node.pizzaTopping}}] - [{{node.pizzaCheese}}] + [{{node.pizzaBase}}]
16211618
<ng-template cdkTreeNodeOutlet></ng-template>
@@ -1637,28 +1634,8 @@ class ObservableDataSourceNestedCdkTreeApp {
16371634

16381635
@Component({
16391636
template: `
1640-
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel">
1641-
<cdk-nested-tree-node *cdkTreeNodeDef="let node" class="customNodeClass"
1642-
[isExpandable]="isExpandable(node)">
1643-
{{node.pizzaTopping}} - {{node.pizzaCheese}} + {{node.pizzaBase}}
1644-
<ng-template cdkTreeNodeOutlet></ng-template>
1645-
</cdk-nested-tree-node>
1646-
</cdk-tree>
1647-
`,
1648-
})
1649-
class NestedCdkErrorTreeApp {
1650-
getLevel = (node: TestData) => node.level;
1651-
1652-
isExpandable = (node: TestData) => node.children.length > 0;
1653-
1654-
dataSource: FakeDataSource | null = new FakeDataSource();
1655-
1656-
@ViewChild(CdkTree) tree: CdkTree<TestData>;
1657-
}
1658-
1659-
@Component({
1660-
template: `
1661-
<cdk-tree [dataSource]="dataArray" [childrenAccessor]="getChildren">
1637+
<cdk-tree [dataSource]="dataArray" [childrenAccessor]="getChildren"
1638+
nodeType="nested">
16621639
<cdk-nested-tree-node *cdkTreeNodeDef="let node; let level = level">
16631640
<span class="tree-test-level">{{level}}</span>
16641641
[{{node.pizzaTopping}}] - [{{node.pizzaCheese}}] + [{{node.pizzaBase}}]
@@ -1681,7 +1658,8 @@ class DepthNestedCdkTreeApp {
16811658

16821659
@Component({
16831660
template: `
1684-
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel" [trackBy]="trackByFn">
1661+
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel" [trackBy]="trackByFn"
1662+
nodeType="flat">
16851663
<cdk-tree-node *cdkTreeNodeDef="let node" class="customNodeClass" [isExpandable]="isExpandable(node)">
16861664
{{node.pizzaTopping}} - {{node.pizzaCheese}} + {{node.pizzaBase}}
16871665
</cdk-tree-node>
@@ -1712,7 +1690,8 @@ class CdkTreeAppWithTrackBy {
17121690

17131691
@Component({
17141692
template: `
1715-
<cdk-tree [dataSource]="dataArray" [childrenAccessor]="getChildren" [trackBy]="trackByFn">
1693+
<cdk-tree [dataSource]="dataArray" [childrenAccessor]="getChildren" [trackBy]="trackByFn"
1694+
nodeType="nested">
17161695
<cdk-nested-tree-node *cdkTreeNodeDef="let node">
17171696
[{{node.pizzaTopping}}] - [{{node.pizzaCheese}}] + [{{node.pizzaBase}}]
17181697
<ng-template cdkTreeNodeOutlet></ng-template>

0 commit comments

Comments
 (0)