Skip to content

Commit

Permalink
feat(ui5-combobox): enable handling of arrow down/up keys
Browse files Browse the repository at this point in the history
Now, selected item can be changed with arrow down / arrow up keys.

Fixes: #1939

In addition some issues are resolved:

Change event could be fired on Enter key press.
Change event could be fired on focusout.
  • Loading branch information
nnaydenow committed Aug 12, 2020
1 parent 13f1d2a commit 974401b
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 4 deletions.
89 changes: 86 additions & 3 deletions packages/main/src/ComboBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import getEffectiveAriaLabelText from "@ui5/webcomponents-base/dist/util/getEffe
import "@ui5/webcomponents-icons/dist/icons/slim-arrow-down.js";
import "@ui5/webcomponents-icons/dist/icons/decline.js";
import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import { isBackSpace, isDelete, isShow } from "@ui5/webcomponents-base/dist/Keys.js";
import {
isBackSpace,
isDelete,
isShow,
isUp,
isDown,
isEnter,
} from "@ui5/webcomponents-base/dist/Keys.js";
import * as Filters from "./ComboBoxFilters.js";

import {
Expand Down Expand Up @@ -324,6 +331,7 @@ class ComboBox extends UI5Element {
this._filteredItems = [];
this._initialRendering = true;
this._itemFocused = false;
this._tempFilterValue = "";
this.i18nBundle = getI18nBundle("@ui5/webcomponents");
}

Expand All @@ -348,7 +356,15 @@ class ComboBox extends UI5Element {
}

this._selectMatchingItem();

if (this._isKeyNavigation && this.responsivePopover && this.responsivePopover.opened) {
this.focused = false;
} else {
this.focused = this === document.activeElement;
}

this._initialRendering = false;
this._isKeyNavigation = false;
}

async onAfterRendering() {
Expand Down Expand Up @@ -381,14 +397,19 @@ class ComboBox extends UI5Element {

_focusout() {
this.focused = false;

this._inputChange();
}

_afterOpenPopover() {
this._iconPressed = true;
this._clearFocus();
}

_afterClosePopover() {
this._iconPressed = false;
this._filteredItems = this.items;
this._tempFilterValue = "";

// close device's keyboard and prevent further typing
if (isPhone()) {
Expand Down Expand Up @@ -424,6 +445,8 @@ class ComboBox extends UI5Element {
event.stopImmediatePropagation();
}

this._clearFocus();
this._tempFilterValue = value;
this.filterValue = value;
this.fireEvent("input");

Expand All @@ -440,9 +463,56 @@ class ComboBox extends UI5Element {
return Filters.StartsWith(str, this._filteredItems);
}

_clearFocus() {
this._filteredItems.map(item => {
item.focused = false;

return item;
});
}

handleArrowKeyPress(event) {
if (this.readonly || !this._filteredItems.length) {
return;
}

const isArrowDown = isDown(event);
const isArrowUp = isUp(event);
const currentItem = this._filteredItems.find(item => {
return this.responsivePopover.opened ? item.focused : item.selected;
});
let indexOfItem = this._filteredItems.indexOf(currentItem);

event.preventDefault();

if ((indexOfItem === 0 && isArrowUp) || (this._filteredItems.length - 1 === indexOfItem && isArrowDown)) {
return;
}

this._clearFocus();

indexOfItem += isArrowDown ? 1 : -1;
indexOfItem = indexOfItem < 0 ? 0 : indexOfItem;

this._filteredItems[indexOfItem].focused = true;
this.filterValue = this._filteredItems[indexOfItem].text;
this._isKeyNavigation = true;
this._itemFocused = true;
this.fireEvent("input");
}

_keydown(event) {
const isArrowKey = isDown(event) || isUp(event);
this._autocomplete = !(isBackSpace(event) || isDelete(event));

if (isArrowKey) {
this.handleArrowKeyPress(event);
}

if (isEnter(event)) {
this._inputChange();
}

if (isShow(event) && !this.readonly && !this.disabled) {
event.preventDefault();
this._resetFilter();
Expand Down Expand Up @@ -472,16 +542,21 @@ class ComboBox extends UI5Element {
_autoCompleteValue(current) {
const currentValue = current;
const matchingItems = this._startsWithMatchingItems(currentValue);
const selectionValue = this._tempFilterValue ? this._tempFilterValue : currentValue;

if (matchingItems.length) {
this._tempValue = matchingItems[0] ? matchingItems[0].text : current;
} else {
this._tempValue = current;
}

if (matchingItems.length && (currentValue !== this._tempValue)) {
if (matchingItems.length && (selectionValue !== this._tempValue)) {
setTimeout(() => {
this.inner.setSelectionRange(selectionValue.length, this._tempValue.length);
}, 0);
} else if (this._isKeyNavigation) {
setTimeout(() => {
this.inner.setSelectionRange(currentValue.length, this._tempValue.length);
this.inner.setSelectionRange(0, this._tempValue.length);
}, 0);
}
}
Expand All @@ -504,6 +579,10 @@ class ComboBox extends UI5Element {
this._closeRespPopover();
}

_itemMousedown(event) {
event.preventDefault();
}

_selectItem(event) {
const listItem = event.detail.item;

Expand Down Expand Up @@ -569,6 +648,10 @@ class ComboBox extends UI5Element {
return this.responsivePopover ? this.responsivePopover.opened : false;
}

get itemTabIndex() {
return undefined;
}

get ariaLabelText() {
return getEffectiveAriaLabelText(this);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/main/src/ComboBoxPopover.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,16 @@
separators="None"
@ui5-item-click={{_selectItem}}
@ui5-item-focused={{_onItemFocus}}
@mousedown={{_itemMousedown}}
mode="SingleSelect"
>
{{#each _filteredItems}}
<ui5-li
type="Active"
._tabIndex={{itemTabIndex}}
.mappedItem={{this}}
?selected={{this.selected}}
?focused={{this.focused}}
>
{{this.text}}
</ui5-li>
Expand Down
19 changes: 19 additions & 0 deletions packages/main/test/pages/ComboBox.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,20 @@
Change call count: <span id="change-count">0</span><br>
</div>

<div class="demo-section">
<span>Input event testing</span>

<ui5-combobox id="input-cb" style="width: 360px;">
<ui5-cb-item text="Argentina"></ui5-cb-item>
<ui5-cb-item text="Germany"></ui5-cb-item>
</ui5-combobox>

<br>

Input value: <span id="input-placeholder"></span><br>
Input call count: <span id="input-count">0</span><br>
</div>


<br>
<br>
Expand Down Expand Up @@ -182,6 +196,11 @@ <h3>ComboBox in Compact</h3>
document.getElementById("change-placeholder").innerHTML = event.target.value;
document.getElementById("change-count").innerHTML = parseInt(document.getElementById("change-count").innerHTML) + 1;
});

document.getElementById("input-cb").addEventListener("ui5-input", function(event) {
document.getElementById("input-placeholder").innerHTML = event.target.filterValue;
document.getElementById("input-count").innerHTML = parseInt(document.getElementById("input-count").innerHTML) + 1;
});
</script>

</body>
Expand Down
32 changes: 31 additions & 1 deletion packages/main/test/specs/ComboBox.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,36 @@ describe("General interaction", () => {
assert.strictEqual(counter.getText(), "1", "Call count should be 1");
});

it ("Tests input event", () => {
browser.url("http://localhost:8080/test-resources/pages/ComboBox.html");

const counter = $("#input-count");
const combo = $("#input-cb");
const placeholder = $("#input-placeholder");
const input = combo.shadow$("#ui5-combobox-input");

input.click();
input.keys("ArrowDown");

assert.strictEqual(placeholder.getText(), "Argentina", "First items is selected");
assert.strictEqual(counter.getText(), "1", "Call count should be 1");

input.keys("ArrowUp");

assert.strictEqual(placeholder.getText(), "Argentina", "Selection not changed");
assert.strictEqual(counter.getText(), "1", "Input event is not fired when first item is selected and navigating with arrow up");

input.keys("ArrowDown");

assert.strictEqual(placeholder.getText(), "Germany", "Last item is selected");
assert.strictEqual(counter.getText(), "2", "Call count should be 2");

input.keys("ArrowDown");

assert.strictEqual(placeholder.getText(), "Germany", "Selection not changed");
assert.strictEqual(counter.getText(), "2", "Input event is not fired when last item is selected and navigating with arrow down");
});

it ("Tests Combo with contains filter", () => {
const combo = $("#contains-cb");
const input = combo.shadow$("#ui5-combobox-input");
Expand Down Expand Up @@ -240,6 +270,6 @@ describe("General interaction", () => {

input.keys("a");
listItems = popover.$("ui5-list").$$("ui5-li");
assert.strictEqual(listItems.length, 0, "Items should be 0");
assert.notOk(popover.opened, "Popover should be closed when no match");
});
});

0 comments on commit 974401b

Please sign in to comment.