Skip to content

Commit 4657395

Browse files
authored
feat(ui5-select): Change focus of options on keypress (#3538)
1 parent dbfa9ca commit 4657395

File tree

2 files changed

+95
-18
lines changed

2 files changed

+95
-18
lines changed

packages/main/src/Select.js

+68-16
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ class Select extends UI5Element {
316316
this._selectedIndexBeforeOpen = -1;
317317
this._escapePressed = false;
318318
this._lastSelectedOption = null;
319+
this._typedChars = "";
320+
this._typingTimeoutID = -1;
319321
this.i18nBundle = getI18nBundle("@ui5/webcomponents");
320322
}
321323

@@ -458,11 +460,64 @@ class Select extends UI5Element {
458460
this._handleEndKey(event);
459461
} else if (isEnter(event)) {
460462
this._handleSelectionChange();
461-
} else {
463+
} else if (isUp(event) || isDown(event)) {
462464
this._handleArrowNavigation(event);
465+
} else {
466+
this._handleKeyboardNavigation(event);
467+
}
468+
}
469+
470+
_handleKeyboardNavigation(event) {
471+
// Waiting for the actual symbol to trigger the keydown event
472+
if (event.shiftKey && event.key === "Shift") {
473+
return;
474+
}
475+
476+
const typedCharacter = event.key.toLowerCase();
477+
478+
this._typedChars += typedCharacter;
479+
480+
// We check if we have more than one characters and they are all duplicate, we set the
481+
// text to be the last input character (typedCharacter). If not, we set the text to be
482+
// the whole input string.
483+
484+
const text = (/^(.)\1+$/i).test(this._typedChars) ? typedCharacter : this._typedChars;
485+
486+
clearTimeout(this._typingTimeoutID);
487+
488+
this._typingTimeoutID = setTimeout(() => {
489+
this._typedChars = "";
490+
this._typingTimeoutID = -1;
491+
}, 1000);
492+
493+
this._selectTypedItem(text);
494+
}
495+
496+
_selectTypedItem(text) {
497+
const currentIndex = this._selectedIndex;
498+
const itemToSelect = this._searchNextItemByText(text);
499+
500+
if (itemToSelect) {
501+
const nextIndex = this._getSelectedItemIndex(itemToSelect);
502+
503+
this._changeSelectedItem(this._selectedIndex, nextIndex);
504+
505+
if (currentIndex !== this._selectedIndex) {
506+
this.itemSelectionAnnounce();
507+
}
463508
}
464509
}
465510

511+
_searchNextItemByText(text) {
512+
let orderedOptions = this.options.slice(0);
513+
const optionsAfterSelected = orderedOptions.splice(this._selectedIndex + 1, orderedOptions.length - this._selectedIndex);
514+
const optionsBeforeSelected = orderedOptions.splice(0, orderedOptions.length - 1);
515+
516+
orderedOptions = optionsAfterSelected.concat(optionsBeforeSelected);
517+
518+
return orderedOptions.find(option => option.textContent.toLowerCase().startsWith(text));
519+
}
520+
466521
_handleHomeKey(event) {
467522
event.preventDefault();
468523
this._changeSelectedItem(this._selectedIndex, 0);
@@ -530,24 +585,21 @@ class Select extends UI5Element {
530585
let nextIndex = -1;
531586
const currentIndex = this._selectedIndex;
532587
const isDownKey = isDown(event);
533-
const isUpKey = isUp(event);
534588

535-
if (isDownKey || isUpKey) {
536-
event.preventDefault();
537-
if (isDownKey) {
538-
nextIndex = this._getNextOptionIndex();
539-
} else {
540-
nextIndex = this._getPreviousOptionIndex();
541-
}
589+
event.preventDefault();
590+
if (isDownKey) {
591+
nextIndex = this._getNextOptionIndex();
592+
} else {
593+
nextIndex = this._getPreviousOptionIndex();
594+
}
542595

543-
this._changeSelectedItem(this._selectedIndex, nextIndex);
596+
this._changeSelectedItem(this._selectedIndex, nextIndex);
544597

545-
if (currentIndex !== this._selectedIndex) {
546-
// Announce new item even if picker is opened.
547-
// The aria-activedescendents attribute can't be used,
548-
// because listitem elements are in different shadow dom
549-
this.itemSelectionAnnounce();
550-
}
598+
if (currentIndex !== this._selectedIndex) {
599+
// Announce new item even if picker is opened.
600+
// The aria-activedescendents attribute can't be used,
601+
// because listitem elements are in different shadow dom
602+
this.itemSelectionAnnounce();
551603
}
552604
}
553605

packages/main/test/specs/Select.spec.js

+27-2
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,13 @@ describe("Select general interaction", () => {
111111
select.keys("ArrowDown");
112112
assert.ok(selectText.getHTML(false).indexOf(EXPECTED_SELECTION_TEXT2), "Arrow Down should change selected item");
113113
assert.strictEqual(selectionText.getHTML(false), EXPECTED_SELECTION_TEXT2, "Selection announcement text should be equalt to the current selected item's text");
114-
114+
115115
// change previewed item with picker opened
116116
select.click();
117117
select.keys("ArrowUp");
118118
assert.strictEqual(selectionText.getHTML(false), EXPECTED_SELECTION_TEXT1, "Selection announcement text should be equalt to the current selected item's text");
119119
select.keys("Escape");
120-
120+
121121
// change selection with picker opened
122122
select.click();
123123
select.keys("ArrowUp");
@@ -205,6 +205,31 @@ describe("Select general interaction", () => {
205205
select.keys("Escape");
206206
});
207207

208+
it("changes selection with typing single letter", () => {
209+
const select = browser.$("#keyboardHandling");
210+
const EXPECTED_SELECTION_TEXT = "Banana";
211+
212+
select.click(); // Open select
213+
select.keys("b");
214+
215+
const selectText = select.shadow$(".ui5-select-label-root");
216+
217+
assert.ok(selectText.getHTML(false).indexOf(EXPECTED_SELECTION_TEXT) > -1, "Typing letter should change selection");
218+
});
219+
220+
it("changes selection with typing more letters", () => {
221+
const select = browser.$("#mySelect3");
222+
const EXPECTED_SELECTION_TEXT = "Brazil";
223+
224+
select.click(); // Open select
225+
select.keys("b");
226+
select.keys("r");
227+
228+
const selectText = select.shadow$(".ui5-select-label-root");
229+
230+
assert.ok(selectText.getHTML(false).indexOf(EXPECTED_SELECTION_TEXT) > -1, "Typing text should change selection");
231+
});
232+
208233
it("opens upon space", () => {
209234
browser.url(`http://localhost:${PORT}/test-resources/pages/Select.html`);
210235

0 commit comments

Comments
 (0)