Skip to content

Commit 5fe7629

Browse files
authored
Merge pull request #8749 from IgniteUI/iganchev/select-type-fast-10.2.x
Handle keydown and iterate items by first character, if no item matched typed input - 10.2.x
2 parents 67f17e2 + b06576d commit 5fe7629

File tree

2 files changed

+43
-29
lines changed

2 files changed

+43
-29
lines changed

projects/igniteui-angular/src/lib/select/select-navigation.directive.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,26 +62,26 @@ export class IgxSelectItemNavigationDirective extends IgxDropDownItemNavigationD
6262
}
6363

6464
super.handleKeyDown(event);
65+
this.captureKey(event);
6566
}
6667

6768
// tslint:disable:member-ordering
6869
private inputStream = '';
6970
private clearStream$ = Subscription.EMPTY;
7071

71-
/** Handle continuous letter typing navigation */
72-
@HostListener('keyup', ['$event'])
7372
public captureKey(event: KeyboardEvent) {
7473
// relying only on key, available on all major browsers:
7574
// https://caniuse.com/#feat=keyboardevent-key (IE/Edge quirk doesn't affect letter typing)
76-
if (!event || !event.key || event.key.length > 1) {
77-
// ignore longer keys ('Alt', 'ArrowDown', etc)
75+
if (!event || !event.key || event.key.length > 1 || event.key === ' ' || event.key === 'spacebar') {
76+
// ignore longer keys ('Alt', 'ArrowDown', etc) AND spacebar (used of open/close)
7877
return;
7978
}
8079

8180
this.clearStream$.unsubscribe();
8281
this.clearStream$ = timer(500).subscribe(() => {
8382
this.inputStream = '';
8483
});
84+
8585
this.inputStream += event.key;
8686
const focusedItem = this.target.focusedItem as IgxSelectItemComponent;
8787

@@ -94,14 +94,20 @@ export class IgxSelectItemNavigationDirective extends IgxDropDownItemNavigationD
9494

9595
public activateItemByText(text: string) {
9696
const items = this.target.items as IgxSelectItemComponent[];
97-
const activeItemIndex = items.indexOf(this.target.focusedItem as IgxSelectItemComponent) || 0;
97+
9898
// ^ this is focused OR selected if the dd is closed
99-
let nextItem = items.slice(activeItemIndex + 1).find(x => !x.disabled && (x.itemText.toLowerCase().startsWith(text.toLowerCase())));
10099

101-
if (!nextItem) {
102-
nextItem = items.slice(0, activeItemIndex).find(x => !x.disabled && (x.itemText.toLowerCase().startsWith(text.toLowerCase())));
100+
let nextItem = this.findNextItem(items, text);
101+
102+
// If there is no such an item starting with the current text input stream AND the last Char in the input stream
103+
// is the same as the first one, find next item starting with the same first Char.
104+
// Covers cases of holding down the same key Ex: "pppppp" that iterates trough list items starting with "p".
105+
if (!nextItem && text.charAt(0) === text.charAt(text.length - 1)) {
106+
text = text.slice(0, 1);
107+
nextItem = this.findNextItem(items, text);
103108
}
104109

110+
// If there is no other item to be found, do not change the active item.
105111
if (!nextItem) {
106112
return;
107113
}
@@ -112,6 +118,14 @@ export class IgxSelectItemNavigationDirective extends IgxDropDownItemNavigationD
112118
this.target.navigateItem(items.indexOf(nextItem));
113119
}
114120

121+
private findNextItem(items: IgxSelectItemComponent[], text: string) {
122+
const activeItemIndex = items.indexOf(this.target.focusedItem as IgxSelectItemComponent) || 0;
123+
124+
// Match next item in ddl items and wrap around if needed
125+
return items.slice(activeItemIndex + 1).find(x => !x.disabled && (x.itemText.toLowerCase().startsWith(text.toLowerCase()))) ||
126+
items.slice(0, activeItemIndex).find(x => !x.disabled && (x.itemText.toLowerCase().startsWith(text.toLowerCase())));
127+
}
128+
115129
ngOnDestroy(): void {
116130
this.clearStream$.unsubscribe();
117131
}

projects/igniteui-angular/src/lib/select/select.component.spec.ts

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1903,10 +1903,10 @@ describe('igxSelect', () => {
19031903

19041904
const filteredItemsInxs = fixture.componentInstance.filterCities('pa');
19051905
for (let index = 0; index < filteredItemsInxs.length; index++) {
1906-
inputElement.triggerEventHandler('keyup', { key: 'p' });
1906+
inputElement.triggerEventHandler('keydown', { key: 'p' });
19071907
tick();
19081908
fixture.detectChanges();
1909-
inputElement.triggerEventHandler('keyup', { key: 'a' });
1909+
inputElement.triggerEventHandler('keydown', { key: 'a' });
19101910
tick();
19111911
fixture.detectChanges();
19121912
verifyFocusedItem(filteredItemsInxs[index]);
@@ -1920,14 +1920,14 @@ describe('igxSelect', () => {
19201920
fixture.detectChanges();
19211921

19221922
const filteredItemsInxs = fixture.componentInstance.filterCities('l');
1923-
inputElement.triggerEventHandler('keyup', { key: 'l' });
1923+
inputElement.triggerEventHandler('keydown', { key: 'l' });
19241924
tick();
19251925
fixture.detectChanges();
19261926
verifyFocusedItem(filteredItemsInxs[0]);
19271927
tick(500);
19281928
fixture.detectChanges();
19291929

1930-
inputElement.triggerEventHandler('keyup', { key: 'L' });
1930+
inputElement.triggerEventHandler('keydown', { key: 'L' });
19311931
tick();
19321932
fixture.detectChanges();
19331933
verifyFocusedItem(filteredItemsInxs[1]);
@@ -1942,15 +1942,15 @@ describe('igxSelect', () => {
19421942

19431943
const filteredItemsInxs = fixture.componentInstance.filterCities('l');
19441944
for (let index = 0; index < filteredItemsInxs.length; index++) {
1945-
inputElement.triggerEventHandler('keyup', { key: 'l' });
1945+
inputElement.triggerEventHandler('keydown', { key: 'l' });
19461946
tick();
19471947
fixture.detectChanges();
19481948
verifyFocusedItem(filteredItemsInxs[index]);
19491949
tick(500);
19501950
fixture.detectChanges();
19511951
}
19521952
// Navigate back to the first filtered item to verify that selection is wrapped
1953-
inputElement.triggerEventHandler('keyup', { key: 'l' });
1953+
inputElement.triggerEventHandler('keydown', { key: 'l' });
19541954
tick();
19551955
fixture.detectChanges();
19561956
verifyFocusedItem(filteredItemsInxs[0]);
@@ -1978,7 +1978,7 @@ describe('igxSelect', () => {
19781978
// German characters
19791979
let filteredItemsInxs = fixture.componentInstance.filterCities('ü');
19801980
for (let index = 0; index < filteredItemsInxs.length; index++) {
1981-
inputElement.triggerEventHandler('keyup', { key: 'ü' });
1981+
inputElement.triggerEventHandler('keydown', { key: 'ü' });
19821982
tick();
19831983
fixture.detectChanges();
19841984
verifyFocusedItem(filteredItemsInxs[index]);
@@ -1989,7 +1989,7 @@ describe('igxSelect', () => {
19891989
// Ciryllic characters
19901990
filteredItemsInxs = fixture.componentInstance.filterCities('с');
19911991
for (let index = 0; index < filteredItemsInxs.length; index++) {
1992-
inputElement.triggerEventHandler('keyup', { key: 'с' });
1992+
inputElement.triggerEventHandler('keydown', { key: 'с' });
19931993
tick();
19941994
fixture.detectChanges();
19951995
verifyFocusedItem(filteredItemsInxs[index]);
@@ -2003,15 +2003,15 @@ describe('igxSelect', () => {
20032003
fixture.detectChanges();
20042004

20052005
const filteredItemsInxs = fixture.componentInstance.filterCities('l');
2006-
inputElement.triggerEventHandler('keyup', { key: 'l' });
2006+
inputElement.triggerEventHandler('keydown', { key: 'l' });
20072007
tick();
20082008
fixture.detectChanges();
20092009
verifyFocusedItem(filteredItemsInxs[0]);
20102010
tick(500);
20112011
fixture.detectChanges();
20122012

20132013
// Verify that focus is unchanged
2014-
inputElement.triggerEventHandler('keyup', { key: 'w' });
2014+
inputElement.triggerEventHandler('keydown', { key: 'w' });
20152015
tick();
20162016
fixture.detectChanges();
20172017
verifyFocusedItem(filteredItemsInxs[0]);
@@ -2022,10 +2022,10 @@ describe('igxSelect', () => {
20222022
fakeAsync(() => {
20232023
const filteredItemsInxs = fixture.componentInstance.filterCities('pa');
20242024
for (let index = 0; index < filteredItemsInxs.length; index++) {
2025-
inputElement.triggerEventHandler('keyup', { key: 'p' });
2025+
inputElement.triggerEventHandler('keydown', { key: 'p' });
20262026
tick();
20272027
fixture.detectChanges();
2028-
inputElement.triggerEventHandler('keyup', { key: 'a' });
2028+
inputElement.triggerEventHandler('keydown', { key: 'a' });
20292029
tick();
20302030
fixture.detectChanges();
20312031
verifySelectedItem(filteredItemsInxs[index]);
@@ -2035,14 +2035,14 @@ describe('igxSelect', () => {
20352035
}));
20362036
it('character key navigation when dropdown is closed should be case insensitive', fakeAsync(() => {
20372037
const filteredItemsInxs = fixture.componentInstance.filterCities('l');
2038-
inputElement.triggerEventHandler('keyup', { key: 'l' });
2038+
inputElement.triggerEventHandler('keydown', { key: 'l' });
20392039
tick();
20402040
fixture.detectChanges();
20412041
verifySelectedItem(filteredItemsInxs[0]);
20422042
tick(500);
20432043
fixture.detectChanges();
20442044

2045-
inputElement.triggerEventHandler('keyup', { key: 'L' });
2045+
inputElement.triggerEventHandler('keydown', { key: 'L' });
20462046
tick();
20472047
fixture.detectChanges();
20482048
verifySelectedItem(filteredItemsInxs[1]);
@@ -2053,15 +2053,15 @@ describe('igxSelect', () => {
20532053
fakeAsync(() => {
20542054
const filteredItemsInxs = fixture.componentInstance.filterCities('l');
20552055
for (let index = 0; index < filteredItemsInxs.length; index++) {
2056-
inputElement.triggerEventHandler('keyup', { key: 'l' });
2056+
inputElement.triggerEventHandler('keydown', { key: 'l' });
20572057
tick();
20582058
fixture.detectChanges();
20592059
verifySelectedItem(filteredItemsInxs[index]);
20602060
tick(500);
20612061
fixture.detectChanges();
20622062
}
20632063
// Navigate back to the first filtered item to verify that selection is wrapped
2064-
inputElement.triggerEventHandler('keyup', { key: 'l' });
2064+
inputElement.triggerEventHandler('keydown', { key: 'l' });
20652065
tick();
20662066
fixture.detectChanges();
20672067
verifySelectedItem(filteredItemsInxs[0]);
@@ -2086,7 +2086,7 @@ describe('igxSelect', () => {
20862086
// German characters
20872087
let filteredItemsInxs = fixture.componentInstance.filterCities('ü');
20882088
for (let index = 0; index < filteredItemsInxs.length; index++) {
2089-
inputElement.triggerEventHandler('keyup', { key: 'ü' });
2089+
inputElement.triggerEventHandler('keydown', { key: 'ü' });
20902090
tick();
20912091
fixture.detectChanges();
20922092
verifySelectedItem(filteredItemsInxs[index]);
@@ -2097,7 +2097,7 @@ describe('igxSelect', () => {
20972097
// Ciryllic characters
20982098
filteredItemsInxs = fixture.componentInstance.filterCities('с');
20992099
for (let index = 0; index < filteredItemsInxs.length; index++) {
2100-
inputElement.triggerEventHandler('keyup', { key: 'с' });
2100+
inputElement.triggerEventHandler('keydown', { key: 'с' });
21012101
tick();
21022102
fixture.detectChanges();
21032103
verifySelectedItem(filteredItemsInxs[index]);
@@ -2107,22 +2107,22 @@ describe('igxSelect', () => {
21072107
}));
21082108
it('should not change selection when pressing non-matching character and dropdown is closed', fakeAsync(() => {
21092109
const filteredItemsInxs = fixture.componentInstance.filterCities('l');
2110-
inputElement.triggerEventHandler('keyup', { key: 'l' });
2110+
inputElement.triggerEventHandler('keydown', { key: 'l' });
21112111
tick();
21122112
fixture.detectChanges();
21132113
verifyFocusedItem(filteredItemsInxs[0]);
21142114
tick(500);
21152115
fixture.detectChanges();
21162116

21172117
// Verify that selection is unchanged
2118-
inputElement.triggerEventHandler('keyup', { key: 'q' });
2118+
inputElement.triggerEventHandler('keydown', { key: 'q' });
21192119
tick();
21202120
fixture.detectChanges();
21212121
verifyFocusedItem(filteredItemsInxs[0]);
21222122
tick(500);
21232123
fixture.detectChanges();
21242124

2125-
inputElement.triggerEventHandler('keyup', { key: 'l' });
2125+
inputElement.triggerEventHandler('keydown', { key: 'l' });
21262126
tick();
21272127
fixture.detectChanges();
21282128
verifyFocusedItem(filteredItemsInxs[1]);

0 commit comments

Comments
 (0)