Skip to content

Commit ada3638

Browse files
committed
feat(select): allow for letter key debounce interval to be customized
Allows the consumer to customize the letter key debounce interval. This can be useful for some cases where the values are harder to type quickly (e.g. ZIP codes). Fixes #16472.
1 parent d9977bd commit ada3638

File tree

3 files changed

+39
-7
lines changed

3 files changed

+39
-7
lines changed

src/material/select/select.spec.ts

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ import {
7171
} from './select-errors';
7272

7373

74-
/** The debounce interval when typing letters to select an option. */
75-
const LETTER_KEY_DEBOUNCE_INTERVAL = 200;
74+
/** Default debounce interval when typing letters to select an option. */
75+
const DEFAULT_LETTER_KEY_DEBOUNCE_INTERVAL = 200;
7676

7777
describe('MatSelect', () => {
7878
let overlayContainer: OverlayContainer;
@@ -469,20 +469,42 @@ describe('MatSelect', () => {
469469
expect(formControl.value).toBeFalsy('Expected no initial value.');
470470

471471
dispatchEvent(select, createKeyboardEvent('keydown', 80, undefined, 'p'));
472-
tick(200);
472+
tick(DEFAULT_LETTER_KEY_DEBOUNCE_INTERVAL);
473473

474474
expect(options[1].selected).toBe(true, 'Expected second option to be selected.');
475475
expect(formControl.value).toBe(options[1].value,
476476
'Expected value from second option to have been set on the model.');
477477

478478
dispatchEvent(select, createKeyboardEvent('keydown', 69, undefined, 'e'));
479-
tick(200);
479+
tick(DEFAULT_LETTER_KEY_DEBOUNCE_INTERVAL);
480480

481481
expect(options[5].selected).toBe(true, 'Expected sixth option to be selected.');
482482
expect(formControl.value).toBe(options[5].value,
483483
'Expected value from sixth option to have been set on the model.');
484484
}));
485485

486+
it('should be able to customize the typing debounce interval', fakeAsync(() => {
487+
const formControl = fixture.componentInstance.control;
488+
const options = fixture.componentInstance.options.toArray();
489+
490+
fixture.componentInstance.letterKeyDebounceInterval = 1337;
491+
fixture.detectChanges();
492+
493+
expect(formControl.value).toBeFalsy('Expected no initial value.');
494+
495+
dispatchEvent(select, createKeyboardEvent('keydown', 80, undefined, 'p'));
496+
tick(DEFAULT_LETTER_KEY_DEBOUNCE_INTERVAL);
497+
498+
expect(formControl.value).toBeFalsy('Expected no value after a bit of time has passed.');
499+
500+
tick(1337);
501+
502+
expect(options[1].selected)
503+
.toBe(true, 'Expected second option to be selected after all the time has passed.');
504+
expect(formControl.value).toBe(options[1].value,
505+
'Expected value from second option to have been set on the model.');
506+
}));
507+
486508
it('should open the panel when pressing a vertical arrow key on a closed multiple select',
487509
fakeAsync(() => {
488510
fixture.destroy();
@@ -1969,7 +1991,7 @@ describe('MatSelect', () => {
19691991
// Press the letter 'o' 15 times since all the options are named 'Option <index>'
19701992
dispatchEvent(host, createKeyboardEvent('keydown', 79, undefined, 'o'));
19711993
fixture.detectChanges();
1972-
tick(LETTER_KEY_DEBOUNCE_INTERVAL);
1994+
tick(DEFAULT_LETTER_KEY_DEBOUNCE_INTERVAL);
19731995
}
19741996
flush();
19751997

@@ -4275,7 +4297,8 @@ describe('MatSelect', () => {
42754297
<mat-form-field>
42764298
<mat-select placeholder="Food" [formControl]="control" [required]="isRequired"
42774299
[tabIndex]="tabIndexOverride" [aria-label]="ariaLabel" [aria-labelledby]="ariaLabelledby"
4278-
[panelClass]="panelClass" [disableRipple]="disableRipple">
4300+
[panelClass]="panelClass" [disableRipple]="disableRipple"
4301+
[letterKeyDebounceInterval]="letterKeyDebounceInterval">
42794302
<mat-option *ngFor="let food of foods" [value]="food.value" [disabled]="food.disabled">
42804303
{{ food.viewValue }}
42814304
</mat-option>
@@ -4304,6 +4327,7 @@ class BasicSelect {
43044327
ariaLabelledby: string;
43054328
panelClass = ['custom-one', 'custom-two'];
43064329
disableRipple: boolean;
4330+
letterKeyDebounceInterval: number;
43074331

43084332
@ViewChild(MatSelect, {static: true}) select: MatSelect;
43094333
@ViewChildren(MatOption) options: QueryList<MatOption>;

src/material/select/select.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,9 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
426426
/** Object used to control when error messages are shown. */
427427
@Input() errorStateMatcher: ErrorStateMatcher;
428428

429+
/** Time to wait in milliseconds after the last keystroke before moving focus to an item. */
430+
@Input() letterKeyDebounceInterval: number;
431+
429432
/**
430433
* Function used to sort the values in a select in multiple mode.
431434
* Follows the same logic as `Array.prototype.sort`.
@@ -570,6 +573,10 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
570573
if (changes['disabled']) {
571574
this.stateChanges.next();
572575
}
576+
577+
if (changes['letterKeyDebounceInterval'] && this._keyManager) {
578+
this._keyManager.withTypeAhead(this.letterKeyDebounceInterval);
579+
}
573580
}
574581

575582
ngOnDestroy() {
@@ -897,7 +904,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
897904
/** Sets up a key manager to listen to keyboard events on the overlay panel. */
898905
private _initKeyManager() {
899906
this._keyManager = new ActiveDescendantKeyManager<MatOption>(this.options)
900-
.withTypeAhead()
907+
.withTypeAhead(this.letterKeyDebounceInterval)
901908
.withVerticalOrientation()
902909
.withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr')
903910
.withAllowedModifierKeys(['shiftKey']);

tools/public_api_guard/material/select.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export declare class MatSelect extends _MatSelectMixinBase implements AfterConte
4141
errorStateMatcher: ErrorStateMatcher;
4242
focused: boolean;
4343
id: string;
44+
letterKeyDebounceInterval: number;
4445
multiple: boolean;
4546
ngControl: NgControl;
4647
readonly openedChange: EventEmitter<boolean>;

0 commit comments

Comments
 (0)