Skip to content

Commit

Permalink
feat(ui5-select): Change focus of options on keypress (#3538)
Browse files Browse the repository at this point in the history
  • Loading branch information
IlianaB authored Aug 2, 2021
1 parent dbfa9ca commit 4657395
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 18 deletions.
84 changes: 68 additions & 16 deletions packages/main/src/Select.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ class Select extends UI5Element {
this._selectedIndexBeforeOpen = -1;
this._escapePressed = false;
this._lastSelectedOption = null;
this._typedChars = "";
this._typingTimeoutID = -1;
this.i18nBundle = getI18nBundle("@ui5/webcomponents");
}

Expand Down Expand Up @@ -458,11 +460,64 @@ class Select extends UI5Element {
this._handleEndKey(event);
} else if (isEnter(event)) {
this._handleSelectionChange();
} else {
} else if (isUp(event) || isDown(event)) {
this._handleArrowNavigation(event);
} else {
this._handleKeyboardNavigation(event);
}
}

_handleKeyboardNavigation(event) {
// Waiting for the actual symbol to trigger the keydown event
if (event.shiftKey && event.key === "Shift") {
return;
}

const typedCharacter = event.key.toLowerCase();

this._typedChars += typedCharacter;

// We check if we have more than one characters and they are all duplicate, we set the
// text to be the last input character (typedCharacter). If not, we set the text to be
// the whole input string.

const text = (/^(.)\1+$/i).test(this._typedChars) ? typedCharacter : this._typedChars;

clearTimeout(this._typingTimeoutID);

this._typingTimeoutID = setTimeout(() => {
this._typedChars = "";
this._typingTimeoutID = -1;
}, 1000);

this._selectTypedItem(text);
}

_selectTypedItem(text) {
const currentIndex = this._selectedIndex;
const itemToSelect = this._searchNextItemByText(text);

if (itemToSelect) {
const nextIndex = this._getSelectedItemIndex(itemToSelect);

this._changeSelectedItem(this._selectedIndex, nextIndex);

if (currentIndex !== this._selectedIndex) {
this.itemSelectionAnnounce();
}
}
}

_searchNextItemByText(text) {
let orderedOptions = this.options.slice(0);
const optionsAfterSelected = orderedOptions.splice(this._selectedIndex + 1, orderedOptions.length - this._selectedIndex);
const optionsBeforeSelected = orderedOptions.splice(0, orderedOptions.length - 1);

orderedOptions = optionsAfterSelected.concat(optionsBeforeSelected);

return orderedOptions.find(option => option.textContent.toLowerCase().startsWith(text));
}

_handleHomeKey(event) {
event.preventDefault();
this._changeSelectedItem(this._selectedIndex, 0);
Expand Down Expand Up @@ -530,24 +585,21 @@ class Select extends UI5Element {
let nextIndex = -1;
const currentIndex = this._selectedIndex;
const isDownKey = isDown(event);
const isUpKey = isUp(event);

if (isDownKey || isUpKey) {
event.preventDefault();
if (isDownKey) {
nextIndex = this._getNextOptionIndex();
} else {
nextIndex = this._getPreviousOptionIndex();
}
event.preventDefault();
if (isDownKey) {
nextIndex = this._getNextOptionIndex();
} else {
nextIndex = this._getPreviousOptionIndex();
}

this._changeSelectedItem(this._selectedIndex, nextIndex);
this._changeSelectedItem(this._selectedIndex, nextIndex);

if (currentIndex !== this._selectedIndex) {
// Announce new item even if picker is opened.
// The aria-activedescendents attribute can't be used,
// because listitem elements are in different shadow dom
this.itemSelectionAnnounce();
}
if (currentIndex !== this._selectedIndex) {
// Announce new item even if picker is opened.
// The aria-activedescendents attribute can't be used,
// because listitem elements are in different shadow dom
this.itemSelectionAnnounce();
}
}

Expand Down
29 changes: 27 additions & 2 deletions packages/main/test/specs/Select.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ describe("Select general interaction", () => {
select.keys("ArrowDown");
assert.ok(selectText.getHTML(false).indexOf(EXPECTED_SELECTION_TEXT2), "Arrow Down should change selected item");
assert.strictEqual(selectionText.getHTML(false), EXPECTED_SELECTION_TEXT2, "Selection announcement text should be equalt to the current selected item's text");

// change previewed item with picker opened
select.click();
select.keys("ArrowUp");
assert.strictEqual(selectionText.getHTML(false), EXPECTED_SELECTION_TEXT1, "Selection announcement text should be equalt to the current selected item's text");
select.keys("Escape");

// change selection with picker opened
select.click();
select.keys("ArrowUp");
Expand Down Expand Up @@ -205,6 +205,31 @@ describe("Select general interaction", () => {
select.keys("Escape");
});

it("changes selection with typing single letter", () => {
const select = browser.$("#keyboardHandling");
const EXPECTED_SELECTION_TEXT = "Banana";

select.click(); // Open select
select.keys("b");

const selectText = select.shadow$(".ui5-select-label-root");

assert.ok(selectText.getHTML(false).indexOf(EXPECTED_SELECTION_TEXT) > -1, "Typing letter should change selection");
});

it("changes selection with typing more letters", () => {
const select = browser.$("#mySelect3");
const EXPECTED_SELECTION_TEXT = "Brazil";

select.click(); // Open select
select.keys("b");
select.keys("r");

const selectText = select.shadow$(".ui5-select-label-root");

assert.ok(selectText.getHTML(false).indexOf(EXPECTED_SELECTION_TEXT) > -1, "Typing text should change selection");
});

it("opens upon space", () => {
browser.url(`http://localhost:${PORT}/test-resources/pages/Select.html`);

Expand Down

0 comments on commit 4657395

Please sign in to comment.