Skip to content

Commit f0df308

Browse files
crisbetommalerba
authored andcommitted
fix(autocomplete): not picking up indirect descendant option g… (#17510)
Fixes the `mat-autocomplete` not picking up `mat-optgroup` that aren't direct descendants.
1 parent 4301fb0 commit f0df308

File tree

2 files changed

+81
-10
lines changed

2 files changed

+81
-10
lines changed

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,26 +1253,24 @@ describe('MatAutocomplete', () => {
12531253
});
12541254

12551255
describe('option groups', () => {
1256-
let fixture: ComponentFixture<AutocompleteWithGroups>;
12571256
let DOWN_ARROW_EVENT: KeyboardEvent;
12581257
let UP_ARROW_EVENT: KeyboardEvent;
1259-
let container: HTMLElement;
1260-
1261-
beforeEach(fakeAsync(() => {
1262-
fixture = createComponent(AutocompleteWithGroups);
1263-
fixture.detectChanges();
12641258

1259+
beforeEach(() => {
12651260
DOWN_ARROW_EVENT = createKeyboardEvent('keydown', DOWN_ARROW);
12661261
UP_ARROW_EVENT = createKeyboardEvent('keydown', UP_ARROW);
1262+
});
1263+
1264+
it('should scroll to active options below the fold', fakeAsync(() => {
1265+
const fixture = createComponent(AutocompleteWithGroups);
1266+
fixture.detectChanges();
12671267

12681268
fixture.componentInstance.trigger.openPanel();
12691269
fixture.detectChanges();
12701270
tick();
12711271
fixture.detectChanges();
1272-
container = document.querySelector('.mat-autocomplete-panel') as HTMLElement;
1273-
}));
1272+
const container = document.querySelector('.mat-autocomplete-panel') as HTMLElement;
12741273

1275-
it('should scroll to active options below the fold', fakeAsync(() => {
12761274
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
12771275
tick();
12781276
fixture.detectChanges();
@@ -1291,6 +1289,15 @@ describe('MatAutocomplete', () => {
12911289
}));
12921290

12931291
it('should scroll to active options on UP arrow', fakeAsync(() => {
1292+
const fixture = createComponent(AutocompleteWithGroups);
1293+
fixture.detectChanges();
1294+
1295+
fixture.componentInstance.trigger.openPanel();
1296+
fixture.detectChanges();
1297+
tick();
1298+
fixture.detectChanges();
1299+
const container = document.querySelector('.mat-autocomplete-panel') as HTMLElement;
1300+
12941301
fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT);
12951302
tick();
12961303
fixture.detectChanges();
@@ -1301,6 +1308,15 @@ describe('MatAutocomplete', () => {
13011308
}));
13021309

13031310
it('should scroll to active options that are above the panel', fakeAsync(() => {
1311+
const fixture = createComponent(AutocompleteWithGroups);
1312+
fixture.detectChanges();
1313+
1314+
fixture.componentInstance.trigger.openPanel();
1315+
fixture.detectChanges();
1316+
tick();
1317+
fixture.detectChanges();
1318+
const container = document.querySelector('.mat-autocomplete-panel') as HTMLElement;
1319+
13041320
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
13051321
tick();
13061322
fixture.detectChanges();
@@ -1326,6 +1342,15 @@ describe('MatAutocomplete', () => {
13261342

13271343
it('should scroll back to the top when reaching the first option with preceding group label',
13281344
fakeAsync(() => {
1345+
const fixture = createComponent(AutocompleteWithGroups);
1346+
fixture.detectChanges();
1347+
1348+
fixture.componentInstance.trigger.openPanel();
1349+
fixture.detectChanges();
1350+
tick();
1351+
fixture.detectChanges();
1352+
const container = document.querySelector('.mat-autocomplete-panel') as HTMLElement;
1353+
13291354
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
13301355
tick();
13311356
fixture.detectChanges();
@@ -1345,6 +1370,33 @@ describe('MatAutocomplete', () => {
13451370

13461371
expect(container.scrollTop).toBe(0, 'Expected panel to be scrolled to the top.');
13471372
}));
1373+
1374+
it('should scroll to active option when group is indirect descendant', fakeAsync(() => {
1375+
const fixture = createComponent(AutocompleteWithIndirectGroups);
1376+
fixture.detectChanges();
1377+
1378+
fixture.componentInstance.trigger.openPanel();
1379+
fixture.detectChanges();
1380+
tick();
1381+
fixture.detectChanges();
1382+
const container = document.querySelector('.mat-autocomplete-panel') as HTMLElement;
1383+
1384+
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
1385+
tick();
1386+
fixture.detectChanges();
1387+
expect(container.scrollTop).toBe(0, 'Expected the panel not to scroll.');
1388+
1389+
// Press the down arrow five times.
1390+
[1, 2, 3, 4, 5].forEach(() => {
1391+
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
1392+
tick();
1393+
});
1394+
1395+
// <option bottom> - <panel height> + <2x group labels> = 128
1396+
// 288 - 256 + 96 = 128
1397+
expect(container.scrollTop)
1398+
.toBe(128, 'Expected panel to reveal the sixth option.');
1399+
}));
13481400
});
13491401

13501402
describe('aria', () => {
@@ -2808,6 +2860,25 @@ class AutocompleteWithGroups {
28082860
];
28092861
}
28102862

2863+
@Component({
2864+
template: `
2865+
<mat-form-field>
2866+
<input matInput placeholder="State" [matAutocomplete]="auto" [(ngModel)]="selectedState">
2867+
</mat-form-field>
2868+
2869+
<mat-autocomplete #auto="matAutocomplete">
2870+
<ng-container [ngSwitch]="true">
2871+
<mat-optgroup *ngFor="let group of stateGroups" [label]="group.label">
2872+
<mat-option *ngFor="let state of group.states" [value]="state">
2873+
<span>{{ state }}</span>
2874+
</mat-option>
2875+
</mat-optgroup>
2876+
</ng-container>
2877+
</mat-autocomplete>
2878+
`
2879+
})
2880+
class AutocompleteWithIndirectGroups extends AutocompleteWithGroups {}
2881+
28112882
@Component({
28122883
template: `
28132884
<mat-form-field>

src/material/autocomplete/autocomplete.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export class MatAutocomplete extends _MatAutocompleteMixinBase implements AfterC
118118
@ContentChildren(MatOption, {descendants: true}) options: QueryList<MatOption>;
119119

120120
/** @docs-private */
121-
@ContentChildren(MatOptgroup) optionGroups: QueryList<MatOptgroup>;
121+
@ContentChildren(MatOptgroup, {descendants: true}) optionGroups: QueryList<MatOptgroup>;
122122

123123
/** Function that maps an option's control value to its display value in the trigger. */
124124
@Input() displayWith: ((value: any) => string) | null = null;

0 commit comments

Comments
 (0)