Skip to content

Commit 4e83bd3

Browse files
authored
Merge branch 'angular:main' into chips-overflow
2 parents f9ac3dd + 29f0bb2 commit 4e83bd3

File tree

27 files changed

+335
-132
lines changed

27 files changed

+335
-132
lines changed

goldens/material/chips/index.api.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ export class MatChipGrid extends MatChipSet implements AfterContentInit, AfterVi
189189
_blur(): void;
190190
readonly change: EventEmitter<MatChipGridChange>;
191191
get chipBlurChanges(): Observable<MatChipEvent>;
192-
protected _chipInput: MatChipTextControl;
192+
protected _chipInput?: MatChipTextControl;
193193
// (undocumented)
194194
_chips: QueryList<MatChipRow>;
195195
readonly controlType: string;
@@ -216,8 +216,6 @@ export class MatChipGrid extends MatChipSet implements AfterContentInit, AfterVi
216216
// (undocumented)
217217
ngAfterContentInit(): void;
218218
// (undocumented)
219-
ngAfterViewInit(): void;
220-
// (undocumented)
221219
ngControl: NgControl;
222220
// (undocumented)
223221
ngDoCheck(): void;

src/cdk-experimental/ui-patterns/tree/tree.spec.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,74 @@ describe('Tree Pattern', () => {
12161216
tree.onPointerdown(createClickEvent(item0.element()));
12171217
expect(item0.expanded()).toBe(false);
12181218
});
1219+
1220+
describe('follows focus & single select', () => {
1221+
beforeEach(() => {
1222+
treeInputs.selectionMode.set('follow');
1223+
treeInputs.multi.set(false);
1224+
});
1225+
1226+
it('should navigate and select the first child on expandKey if expanded and has children (vertical)', () => {
1227+
treeInputs.orientation.set('vertical');
1228+
const {tree, allItems} = createTree(treeExample, treeInputs);
1229+
const item0 = getItemByValue(allItems(), 'Item 0');
1230+
const item0_0 = getItemByValue(allItems(), 'Item 0-0');
1231+
tree.listBehavior.goto(item0);
1232+
item0.expansion.open();
1233+
1234+
tree.onKeydown(right());
1235+
expect(tree.activeItem()).toBe(item0_0);
1236+
expect(tree.inputs.value()).toEqual(['Item 0-0']);
1237+
});
1238+
1239+
it('should navigate and select the parent on collapseKey if collapsed (vertical)', () => {
1240+
treeInputs.orientation.set('vertical');
1241+
const {tree, allItems} = createTree(treeExample, treeInputs);
1242+
const item0 = getItemByValue(allItems(), 'Item 0');
1243+
const item0_0 = getItemByValue(allItems(), 'Item 0-0');
1244+
item0.expansion.open();
1245+
tree.listBehavior.goto(item0_0);
1246+
1247+
tree.onKeydown(left());
1248+
expect(tree.activeItem()).toBe(item0);
1249+
expect(tree.inputs.value()).toEqual(['Item 0']);
1250+
});
1251+
});
1252+
1253+
describe('follows focus & multi select', () => {
1254+
beforeEach(() => {
1255+
treeInputs.selectionMode.set('follow');
1256+
treeInputs.multi.set(true);
1257+
});
1258+
1259+
it('should navigate without select the first child on Ctrl + expandKey if expanded and has children (vertical)', () => {
1260+
treeInputs.orientation.set('vertical');
1261+
const {tree, allItems} = createTree(treeExample, treeInputs);
1262+
const item0 = getItemByValue(allItems(), 'Item 0');
1263+
const item0_0 = getItemByValue(allItems(), 'Item 0-0');
1264+
tree.listBehavior.goto(item0);
1265+
item0.expansion.open();
1266+
tree.inputs.value.set(['Item 1']); // pre-select something else
1267+
1268+
tree.onKeydown(right({control: true}));
1269+
expect(tree.activeItem()).toBe(item0_0);
1270+
expect(tree.inputs.value()).toEqual(['Item 1']);
1271+
});
1272+
1273+
it('should navigate without select the parent on Ctrl + collapseKey if collapsed (vertical)', () => {
1274+
treeInputs.orientation.set('vertical');
1275+
const {tree, allItems} = createTree(treeExample, treeInputs);
1276+
const item0 = getItemByValue(allItems(), 'Item 0');
1277+
const item0_0 = getItemByValue(allItems(), 'Item 0-0');
1278+
item0.expansion.open();
1279+
tree.listBehavior.goto(item0_0);
1280+
tree.inputs.value.set(['Item 1']); // pre-select something else
1281+
1282+
tree.onKeydown(left({control: true}));
1283+
expect(tree.activeItem()).toBe(item0);
1284+
expect(tree.inputs.value()).toEqual(['Item 1']);
1285+
});
1286+
});
12191287
});
12201288

12211289
describe('#setDefaultState', () => {

src/cdk-experimental/ui-patterns/tree/tree.ts

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -207,23 +207,15 @@ export class TreePattern<V> {
207207
const manager = new KeyboardEventManager();
208208
const list = this.listBehavior;
209209

210-
if (!this.followFocus()) {
211-
manager
212-
.on(this.prevKey, () => list.prev())
213-
.on(this.nextKey, () => list.next())
214-
.on('Home', () => list.first())
215-
.on('End', () => list.last())
216-
.on(this.typeaheadRegexp, e => list.search(e.key));
217-
}
218-
219-
if (this.followFocus()) {
220-
manager
221-
.on(this.prevKey, () => list.prev({selectOne: true}))
222-
.on(this.nextKey, () => list.next({selectOne: true}))
223-
.on('Home', () => list.first({selectOne: true}))
224-
.on('End', () => list.last({selectOne: true}))
225-
.on(this.typeaheadRegexp, e => list.search(e.key, {selectOne: true}));
226-
}
210+
manager
211+
.on(this.prevKey, () => list.prev({selectOne: this.followFocus()}))
212+
.on(this.nextKey, () => list.next({selectOne: this.followFocus()}))
213+
.on('Home', () => list.first({selectOne: this.followFocus()}))
214+
.on('End', () => list.last({selectOne: this.followFocus()}))
215+
.on(this.typeaheadRegexp, e => list.search(e.key, {selectOne: this.followFocus()}))
216+
.on(this.expandKey, () => this.expand({selectOne: this.followFocus()}))
217+
.on(this.collapseKey, () => this.collapse({selectOne: this.followFocus()}))
218+
.on(Modifier.Shift, '*', () => this.expandSiblings());
227219

228220
if (this.inputs.multi()) {
229221
manager
@@ -260,6 +252,8 @@ export class TreePattern<V> {
260252
manager
261253
.on([Modifier.Ctrl, Modifier.Meta], this.prevKey, () => list.prev())
262254
.on([Modifier.Ctrl, Modifier.Meta], this.nextKey, () => list.next())
255+
.on([Modifier.Ctrl, Modifier.Meta], this.expandKey, () => this.expand())
256+
.on([Modifier.Ctrl, Modifier.Meta], this.collapseKey, () => this.collapse())
263257
.on([Modifier.Ctrl, Modifier.Meta], ' ', () => list.toggle())
264258
.on([Modifier.Ctrl, Modifier.Meta], 'Enter', () => list.toggle())
265259
.on([Modifier.Ctrl, Modifier.Meta], 'Home', () => list.first())
@@ -270,11 +264,6 @@ export class TreePattern<V> {
270264
});
271265
}
272266

273-
manager
274-
.on(this.expandKey, () => this.expand())
275-
.on(this.collapseKey, () => this.collapse())
276-
.on(Modifier.Shift, '*', () => this.expandSiblings());
277-
278267
return manager;
279268
});
280269

@@ -403,38 +392,38 @@ export class TreePattern<V> {
403392
}
404393

405394
/** Expands a tree item. */
406-
expand(item?: TreeItemPattern<V>) {
407-
item ??= this.activeItem();
395+
expand(opts?: SelectOptions) {
396+
const item = this.activeItem();
408397
if (!item || !this.listBehavior.isFocusable(item)) return;
409398

410399
if (item.expandable() && !item.expanded()) {
411400
item.expansion.open();
412-
} else if (item.expanded() && item.children().length > 0) {
413-
const firstChild = item.children()[0];
414-
if (this.listBehavior.isFocusable(firstChild)) {
415-
this.listBehavior.goto(firstChild);
416-
}
401+
} else if (
402+
item.expanded() &&
403+
item.children().some(item => this.listBehavior.isFocusable(item))
404+
) {
405+
this.listBehavior.next(opts);
417406
}
418407
}
419408

420409
/** Expands all sibling tree items including itself. */
421410
expandSiblings(item?: TreeItemPattern<V>) {
422411
item ??= this.activeItem();
423412
const siblings = item?.parent()?.children();
424-
siblings?.forEach(item => this.expand(item));
413+
siblings?.forEach(item => item.expansion.open());
425414
}
426415

427416
/** Collapses a tree item. */
428-
collapse(item?: TreeItemPattern<V>) {
429-
item ??= this.activeItem();
417+
collapse(opts?: SelectOptions) {
418+
const item = this.activeItem();
430419
if (!item || !this.listBehavior.isFocusable(item)) return;
431420

432421
if (item.expandable() && item.expanded()) {
433422
item.expansion.close();
434423
} else if (item.parent() && item.parent() !== this) {
435424
const parentItem = item.parent();
436425
if (parentItem instanceof TreeItemPattern && this.listBehavior.isFocusable(parentItem)) {
437-
this.listBehavior.goto(parentItem);
426+
this.listBehavior.goto(parentItem, opts);
438427
}
439428
}
440429
}

src/components-examples/cdk-experimental/listbox/cdk-listbox-configurable/cdk-listbox-configurable-example.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.example-listbox-controls {
22
display: flex;
3+
flex-wrap: wrap;
34
align-items: center;
45
gap: 16px;
56
padding-bottom: 16px;

src/components-examples/cdk-experimental/radio-group/radio-common.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.example-radio-controls {
22
display: flex;
3+
flex-wrap: wrap;
34
align-items: center;
45
gap: 16px;
56
padding-bottom: 16px;

src/components-examples/cdk-experimental/tabs/cdk-tabs-common.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.example-tablist-controls {
22
display: flex;
3+
flex-wrap: wrap;
34
align-items: center;
45
gap: 16px;
56
padding-bottom: 16px;

src/components-examples/cdk-experimental/toolbar/toolbar-common.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
.example-toolbar-controls {
1010
display: flex;
11+
flex-wrap: wrap;
1112
align-items: center;
1213
gap: 16px;
1314
padding-bottom: 4px;

src/components-examples/cdk-experimental/tree/tree-common.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.example-tree-controls {
22
display: flex;
3+
flex-wrap: wrap;
34
align-items: center;
45
gap: 16px;
56
margin-bottom: 16px;
@@ -16,6 +17,8 @@
1617
padding: 10px;
1718
border: 1px solid var(--mat-sys-outline);
1819
border-radius: var(--mat-sys-corner-extra-small);
20+
overflow-x: scroll;
21+
min-width: 24rem;
1922
}
2023

2124
.example-tree-item {
@@ -46,6 +49,7 @@
4649
margin-left: auto;
4750
}
4851

52+
.example-tree-item[aria-current] .example-selected-icon,
4953
.example-tree-item[aria-selected='true'] .example-selected-icon {
5054
visibility: visible;
5155
}

src/dev-app/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ ng_project(
8585
"//src/dev-app/snack-bar",
8686
"//src/dev-app/stepper",
8787
"//src/dev-app/system",
88+
"//src/dev-app/system-classes-demo",
8889
"//src/dev-app/table",
8990
"//src/dev-app/table-scroll-container",
9091
"//src/dev-app/tabs",
@@ -94,7 +95,6 @@ ng_project(
9495
"//src/dev-app/tooltip",
9596
"//src/dev-app/tree",
9697
"//src/dev-app/typography",
97-
"//src/dev-app/utility-classes-demo",
9898
"//src/dev-app/virtual-scroll",
9999
"//src/dev-app/youtube-player",
100100
"//src/material/core",

src/dev-app/cdk-experimental-tree/cdk-tree-demo.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
.example-tree-grid {
22
display: grid;
3-
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
3+
grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr));
44
gap: 20px;
55
}
66

77
.example-tree-container {
8-
width: 500px;
98
display: flex;
109
flex-direction: column;
1110
justify-content: flex-start;

0 commit comments

Comments
 (0)