Skip to content

Commit 5d001ec

Browse files
zarendcrisbeto
authored andcommitted
fix(cdk/tree): implement typeahead label
Implement typeahead labels for Tree. Follow the established pattern that Menu and List use. - implement CdkTreeNode#cdkTreeNodeTypeaheadLabel - implement CdkTreeNode#getLabel
1 parent 38cfe94 commit 5d001ec

File tree

16 files changed

+174
-9
lines changed

16 files changed

+174
-9
lines changed

src/cdk/tree/tree.spec.ts

Lines changed: 144 additions & 2 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} from '@angular/core/testing';
8+
import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
99
import {
1010
Component,
1111
ErrorHandler,
@@ -15,14 +15,16 @@ import {
1515
EventEmitter,
1616
ViewChildren,
1717
QueryList,
18+
ElementRef,
1819
} from '@angular/core';
1920
import {CollectionViewer, DataSource} from '@angular/cdk/collections';
2021
import {Directionality, Direction} from '@angular/cdk/bidi';
21-
import {combineLatest, BehaviorSubject, Observable} from 'rxjs';
22+
import {combineLatest, BehaviorSubject, Observable, of} from 'rxjs';
2223
import {map} from 'rxjs/operators';
2324
import {CdkTreeModule, CdkTreeNodePadding} from './index';
2425
import {CdkTree, CdkTreeNode} from './tree';
2526
import {createKeyboardEvent} from '@angular/cdk/testing/testbed/fake-events';
27+
import {B, C, T} from '../keycodes';
2628

2729
/**
2830
* This is a cloned version of `tree.spec.ts` that contains all the same tests,
@@ -1286,6 +1288,91 @@ describe('CdkTree', () => {
12861288
});
12871289
});
12881290
});
1291+
1292+
describe('typeahead', () => {
1293+
describe('Tree with default configuration', () => {
1294+
let fixture: ComponentFixture<FlatTreeWithThreeNodes>;
1295+
let component: FlatTreeWithThreeNodes;
1296+
1297+
beforeEach(() => {
1298+
configureCdkTreeTestingModule([FlatTreeWithThreeNodes]);
1299+
fixture = TestBed.createComponent(FlatTreeWithThreeNodes);
1300+
fixture.detectChanges();
1301+
1302+
component = fixture.componentInstance;
1303+
});
1304+
describe(`when pressing 'b'`, () => {
1305+
beforeEach(fakeAsync(() => {
1306+
component.tree.nativeElement.dispatchEvent(createKeyboardEvent('keydown', B));
1307+
fixture.detectChanges();
1308+
tick(1000);
1309+
}));
1310+
1311+
it('focuses banana', () => {
1312+
expect(document.activeElement)
1313+
.withContext('expecting banana to be focused')
1314+
.toBe(component.treeNodes.get(1)?.nativeElement!);
1315+
});
1316+
});
1317+
});
1318+
1319+
describe('Tree with cdkTreeNodeTypeaheadlabel Input binding', () => {
1320+
let fixture: ComponentFixture<TypeaheadLabelFlatTreeWithThreeNodes>;
1321+
let component: TypeaheadLabelFlatTreeWithThreeNodes;
1322+
1323+
beforeEach(() => {
1324+
configureCdkTreeTestingModule([TypeaheadLabelFlatTreeWithThreeNodes]);
1325+
fixture = TestBed.createComponent(TypeaheadLabelFlatTreeWithThreeNodes);
1326+
fixture.detectChanges();
1327+
1328+
component = fixture.componentInstance;
1329+
});
1330+
1331+
describe(`when pressing 'b'`, () => {
1332+
beforeEach(fakeAsync(() => {
1333+
component.tree.nativeElement.dispatchEvent(createKeyboardEvent('keydown', B));
1334+
fixture.detectChanges();
1335+
tick(1000);
1336+
}));
1337+
1338+
it('focuses banana', fakeAsync(() => {
1339+
component.tree.nativeElement.dispatchEvent(createKeyboardEvent('keydown', B));
1340+
fixture.detectChanges();
1341+
tick(1000);
1342+
1343+
expect(document.activeElement)
1344+
.withContext('expecting banana to be focused')
1345+
.toBe(component.treeNodes.get(1)?.nativeElement!);
1346+
}));
1347+
});
1348+
1349+
describe(`when pressing 'c'`, () => {
1350+
beforeEach(fakeAsync(() => {
1351+
component.tree.nativeElement.dispatchEvent(createKeyboardEvent('keydown', C));
1352+
fixture.detectChanges();
1353+
tick(1000);
1354+
}));
1355+
it('does not move focus', () => {
1356+
expect(document.activeElement)
1357+
.withContext('expecting document body to be focused')
1358+
.toBe(document.body);
1359+
});
1360+
});
1361+
1362+
describe(`when pressing 't'`, () => {
1363+
beforeEach(fakeAsync(() => {
1364+
component.tree.nativeElement.dispatchEvent(createKeyboardEvent('keydown', T));
1365+
fixture.detectChanges();
1366+
tick(1000);
1367+
}));
1368+
it('focuses focuses cherry', () => {
1369+
expect(document.activeElement)
1370+
.withContext('expecting cherry to be focused')
1371+
.toBe(component.treeNodes.get(2)?.nativeElement!);
1372+
});
1373+
});
1374+
});
1375+
});
12891376
});
12901377

12911378
export class TestData {
@@ -1873,3 +1960,58 @@ class NestedCdkTreeAppWithTrackBy {
18731960

18741961
@ViewChild(CdkTree) tree: CdkTree<TestData>;
18751962
}
1963+
1964+
class MinimalTestData {
1965+
constructor(
1966+
public name: string,
1967+
public typeaheadLabel: string | null = null,
1968+
) {}
1969+
children: MinimalTestData[] = [];
1970+
}
1971+
1972+
@Component({
1973+
template: `
1974+
<cdk-tree #tree [dataSource]="dataSource" [childrenAccessor]="getChildren">
1975+
<cdk-tree-node #node *cdkTreeNodeDef="let node"
1976+
[cdkTreeNodeTypeaheadLabel]="node.typeaheadLabel">
1977+
{{node.name}}
1978+
</cdk-tree-node>
1979+
</cdk-tree>
1980+
`,
1981+
})
1982+
class TypeaheadLabelFlatTreeWithThreeNodes {
1983+
isExpandable = (node: MinimalTestData) => node.children.length > 0;
1984+
getChildren = (node: MinimalTestData) => node.children;
1985+
1986+
dataSource = of([
1987+
new MinimalTestData('apple'),
1988+
new MinimalTestData('banana'),
1989+
new MinimalTestData('cherry', 'typeahead'),
1990+
]);
1991+
1992+
@ViewChild('tree', {read: ElementRef}) tree: ElementRef<HTMLElement>;
1993+
@ViewChildren('node') treeNodes: QueryList<ElementRef<HTMLElement>>;
1994+
}
1995+
1996+
@Component({
1997+
template: `
1998+
<cdk-tree #tree [dataSource]="dataSource" [childrenAccessor]="getChildren">
1999+
<cdk-tree-node #node *cdkTreeNodeDef="let node">
2000+
{{node.name}}
2001+
</cdk-tree-node>
2002+
</cdk-tree>
2003+
`,
2004+
})
2005+
class FlatTreeWithThreeNodes {
2006+
isExpandable = (node: MinimalTestData) => node.children.length > 0;
2007+
getChildren = (node: MinimalTestData) => node.children;
2008+
2009+
dataSource = of([
2010+
new MinimalTestData('apple'),
2011+
new MinimalTestData('banana'),
2012+
new MinimalTestData('cherry'),
2013+
]);
2014+
2015+
@ViewChild('tree', {read: ElementRef}) tree: ElementRef<HTMLElement>;
2016+
@ViewChildren('node') treeNodes: QueryList<ElementRef<HTMLElement>>;
2017+
}

src/cdk/tree/tree.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,16 @@ export class CdkTreeNode<T, K = T> implements OnDestroy, OnInit, TreeKeyManagerI
11691169
*/
11701170
@Input({transform: booleanAttribute}) isDisabled: boolean;
11711171

1172+
/**
1173+
* The text used to locate this item during typeahead. If not specified, the `textContent` will
1174+
* will be used.
1175+
*/
1176+
@Input('cdkTreeNodeTypeaheadLabel') typeaheadLabel: string | null;
1177+
1178+
getLabel(): string {
1179+
return this.typeaheadLabel || this._elementRef.nativeElement.textContent?.trim() || '';
1180+
}
1181+
11721182
/** This emits when the node has been programatically activated or activated by keyboard. */
11731183
@Output()
11741184
readonly activation: EventEmitter<T> = new EventEmitter<T>();

src/components-examples/cdk/tree/cdk-tree-complex/cdk-tree-complex-example.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<cdk-tree-node
1111
*cdkTreeNodeDef="let node"
1212
cdkTreeNodePadding
13+
[cdkTreeNodeTypeaheadLabel]="node.raw.name"
1314
[isExpandable]="node.isExpandable()"
1415
(expandedChange)="onExpand(node, $event)">
1516
<!-- Spinner when node is loading children; this replaces the expand button. -->

src/components-examples/cdk/tree/cdk-tree-custom-key-manager/cdk-tree-custom-key-manager-example.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<!-- This is the tree node template for expandable nodes -->
1212
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
1313
cdkTreeNodeToggle
14+
[cdkTreeNodeTypeaheadLabel]="node.name"
1415
[style.display]="shouldRender(node) ? 'flex' : 'none'"
1516
[isDisabled]="!shouldRender(node)"
1617
(expandedChange)="node.isExpanded = $event"

src/components-examples/cdk/tree/cdk-tree-flat-children-accessor/cdk-tree-flat-children-accessor-example.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<!-- This is the tree node template for expandable nodes -->
1212
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
1313
cdkTreeNodeToggle
14+
[cdkTreeNodeTypeaheadLabel]="node.name"
1415
[style.display]="shouldRender(node) ? 'flex' : 'none'"
1516
[isDisabled]="!shouldRender(node)"
1617
[isExpandable]="true"

src/components-examples/cdk/tree/cdk-tree-flat-level-accessor/cdk-tree-flat-level-accessor-example.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<!-- This is the tree node template for expandable nodes -->
1212
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
1313
cdkTreeNodeToggle
14+
[cdkTreeNodeTypeaheadLabel]="node.name"
1415
[style.display]="shouldRender(node) ? 'flex' : 'none'"
1516
[isDisabled]="!shouldRender(node)"
1617
[isExpandable]="node.expandable"

src/components-examples/cdk/tree/cdk-tree-flat/cdk-tree-flat-example.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</cdk-tree-node>
1111
<!-- This is the tree node template for expandable nodes -->
1212
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
13-
cdkTreeNodeToggle
13+
cdkTreeNodeToggle [cdkTreeNodeTypeaheadLabel]="node.name"
1414
[style.display]="shouldRender(node) ? 'flex' : 'none'"
1515
[isDisabled]="!shouldRender(node)"
1616
(expandedChange)="node.isExpanded = $event"

src/components-examples/cdk/tree/cdk-tree-nested-children-accessor/cdk-tree-nested-children-accessor-example.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<cdk-nested-tree-node #treeNode="cdkNestedTreeNode"
1111
cdkTreeNodeToggle
1212
*cdkTreeNodeDef="let node; when: hasChild"
13+
[cdkTreeNodeTypeaheadLabel]="node.name"
1314
isExpandable
1415
class="example-tree-node">
1516
<button mat-icon-button [attr.aria-label]="'Toggle ' + node.name" cdkTreeNodeToggle>

src/components-examples/cdk/tree/cdk-tree-nested-level-accessor/cdk-tree-nested-level-accessor-example.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<!-- This is the tree node template for expandable nodes -->
1010
<cdk-nested-tree-node #treeNode="cdkNestedTreeNode"
1111
cdkTreeNodeToggle
12+
[cdkTreeNodeTypeaheadLabel]="node.name"
1213
*cdkTreeNodeDef="let node; when: hasChild"
1314
isExpandable
1415
class="example-tree-node">

src/components-examples/cdk/tree/cdk-tree-nested/cdk-tree-nested-example.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<!-- This is the tree node template for expandable nodes -->
1010
<cdk-nested-tree-node #treeNode="cdkNestedTreeNode"
1111
cdkTreeNodeToggle
12+
[cdkTreeNodeTypeaheadLabel]="node.name"
1213
*cdkTreeNodeDef="let node; when: hasChild"
1314
isExpandable
1415
class="example-tree-node">

0 commit comments

Comments
 (0)