Skip to content

Commit bad571b

Browse files
committed
fix(cdk/tree): fix level for flat and nested tree nodes
1 parent df7156e commit bad571b

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

src/cdk/tree/tree.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,21 @@ describe('CdkTree', () => {
595595
[_, _, `topping_6 - cheese_6 + base_6`],
596596
[`topping_3 - cheese_3 + base_3`]);
597597
});
598+
599+
it('with correct aria-level', () => {
600+
expect(getNodes(treeElement).every(node => {
601+
return node.getAttribute('aria-level') === '1';
602+
})).toBe(true);
603+
604+
let data = dataSource.data;
605+
const child = dataSource.addChild(data[1], false);
606+
dataSource.addChild(child, false);
607+
fixture.detectChanges();
608+
609+
const nodes = getNodes(treeElement);
610+
const levels = nodes.map(n => n.getAttribute('aria-level'));
611+
expect(levels).toEqual(['1', '1', '2', '3', '1']);
612+
});
598613
});
599614

600615
describe('with static children', () => {

src/cdk/tree/tree.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {FocusableOption} from '@angular/cdk/a11y';
99
import {CollectionViewer, DataSource, isDataSource} from '@angular/cdk/collections';
1010
import {
1111
AfterContentChecked,
12+
AfterViewInit,
1213
ChangeDetectionStrategy,
1314
ChangeDetectorRef,
1415
Component,
@@ -299,12 +300,11 @@ export class CdkTree<T> implements AfterContentChecked, CollectionViewer, OnDest
299300
exportAs: 'cdkTreeNode',
300301
host: {
301302
'[attr.aria-expanded]': 'isExpanded',
302-
'[attr.aria-level]': 'level + 1',
303303
'[attr.role]': 'role',
304304
'class': 'cdk-tree-node',
305305
},
306306
})
307-
export class CdkTreeNode<T> implements FocusableOption, OnDestroy {
307+
export class CdkTreeNode<T> implements FocusableOption, OnDestroy, AfterViewInit {
308308
/**
309309
* The most recently created `CdkTreeNode`. We save it in static variable so we can retrieve it
310310
* in `CdkTree` and set the data to it.
@@ -333,8 +333,11 @@ export class CdkTreeNode<T> implements FocusableOption, OnDestroy {
333333
}
334334

335335
get level(): number {
336-
return this._tree.treeControl.getLevel ? this._tree.treeControl.getLevel(this._data) : 0;
337-
}
336+
// Retrieve the aria-level of the parent node because level from treeControl is 0 indexed and
337+
// aria-level is 1 indexed
338+
return this._tree.treeControl.getLevel ?
339+
this._tree.treeControl.getLevel(this._data) : this._parentNodeAriaLevel();
340+
}
338341

339342
/**
340343
* The role of the node should always be 'treeitem'.
@@ -347,6 +350,10 @@ export class CdkTreeNode<T> implements FocusableOption, OnDestroy {
347350
CdkTreeNode.mostRecentTreeNode = this as CdkTreeNode<T>;
348351
}
349352

353+
ngAfterViewInit(): void {
354+
this._elementRef.nativeElement.setAttribute('aria-level', String(this.level + 1));
355+
}
356+
350357
ngOnDestroy() {
351358
// If this is the last tree node being destroyed,
352359
// clear out the reference to avoid leaking memory.
@@ -371,4 +378,22 @@ export class CdkTreeNode<T> implements FocusableOption, OnDestroy {
371378
}
372379
this.role = 'treeitem';
373380
}
381+
382+
private _parentNodeAriaLevel(): number {
383+
let parent = this._elementRef.nativeElement.parentElement;
384+
while (parent &&
385+
!(parent.classList.contains('cdk-nested-tree-node') || parent.classList.contains('cdk-tree'))) {
386+
parent = parent.parentElement;
387+
}
388+
if (!parent) {
389+
throw Error('Incorrect tree structure containing detached node.');
390+
}
391+
if (parent.classList.contains('cdk-nested-tree-node')) {
392+
return parseInt(parent.getAttribute('aria-level')!);
393+
} else if (parent.classList.contains('cdk-tree')) {
394+
return 0;
395+
} else {
396+
throw Error(`Incorrect tree structure containing ${parent.className}.`);
397+
}
398+
}
374399
}

0 commit comments

Comments
 (0)