Skip to content

Commit

Permalink
fix(ui5-input): cancel suggestion selection with ESC (#2289)
Browse files Browse the repository at this point in the history
When the users navigates between the items with UP/DOWN, the value is updated and shows the currently previewed item,
Now, the user is able to "cancel" this selection by pressing ESC.

FIXES: #2254
  • Loading branch information
ilhan007 committed Nov 5, 2020
1 parent 0918ab2 commit 4494037
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 6 deletions.
48 changes: 42 additions & 6 deletions packages/main/src/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
isSpace,
isEnter,
isBackSpace,
isEscape,
} from "@ui5/webcomponents-base/dist/Keys.js";
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
Expand Down Expand Up @@ -494,11 +495,20 @@ class Input extends UI5Element {
// Indicates if there is selected suggestionItem.
this.hasSuggestionItemSelected = false;

// Represents the value before user moves selection between the suggestion items.
// Used to register and fire "input" event upon [SPACE] or [ENTER].
// Note: the property "value" is updated upon selection move and can`t be used.
// Represents the value before user moves selection from suggestion item to another
// and its value is updated after each move.
// Note: Used to register and fire "input" event upon [SPACE] or [ENTER].
// Note: The property "value" is updated upon selection move and can`t be used.
this.valueBeforeItemSelection = "";

// Represents the value before user moves selection between the suggestion items
// and its value remains the same when the user navigates up or down the list.
// Note: Used to cancel selection upon [ESC].
this.valueBeforeItemPreview = "";

// Indicates if the user selection has been canceled with [ESC].
this.suggestionSelectionCanceled = false;

// tracks the value between focus in and focus out to detect that change event should be fired.
this.previousValue = undefined;

Expand Down Expand Up @@ -591,6 +601,10 @@ class Input extends UI5Element {
return this._handleEnter(event);
}

if (isEscape(event)) {
return this._handleEscape(event);
}

this._keyDown = true;
}

Expand Down Expand Up @@ -624,6 +638,20 @@ class Input extends UI5Element {
}
}

_handleEscape() {
if (this.showSuggestions && this.Suggestions && this.Suggestions._isItemOnTarget()) {
// Restore the value.
this.value = this.valueBeforeItemPreview;

// Mark that the selection has been canceled, so the popover can close
// and not reopen, due to receiving focus.
this.suggestionSelectionCanceled = true;

// Close suggestion popover
this._closeRespPopover(true);
}
}

async _onfocusin(event) {
const inputDomRef = await this.getInputDOMRef();

Expand All @@ -633,6 +661,7 @@ class Input extends UI5Element {

this.focused = true; // invalidating property
this.previousValue = this.value;
this.valueBeforeItemPreview = this.value;

this._inputIconFocused = event.target && event.target === this.querySelector("[ui5-icon]");
}
Expand Down Expand Up @@ -682,6 +711,8 @@ class Input extends UI5Element {
async _handleInput(event) {
const inputDomRef = await this.getInputDOMRef();

this.suggestionSelectionCanceled = false;

if (this.value && this.type === InputType.Number && !isBackSpace(event) && !inputDomRef.value) {
// For input with type="Number", if the delimiter is entered second time, the inner input is firing event with empty value
return;
Expand Down Expand Up @@ -711,8 +742,8 @@ class Input extends UI5Element {
this._inputWidth = this.offsetWidth;
}

_closeRespPopover() {
this.Suggestions.close();
_closeRespPopover(preventFocusRestore) {
this.Suggestions.close(preventFocusRestore);
}

async _afterOpenPopover() {
Expand Down Expand Up @@ -786,7 +817,8 @@ class Input extends UI5Element {
return !!(this.suggestionItems.length
&& this.focused
&& this.showSuggestions
&& !this.hasSuggestionItemSelected);
&& !this.hasSuggestionItemSelected
&& !this.suggestionSelectionCanceled);
}

selectSuggestion(item, keyboardUsed) {
Expand All @@ -807,6 +839,9 @@ class Input extends UI5Element {
this.fireEvent(this.EVENT_CHANGE);
}

this.valueBeforeItemPreview = "";
this.suggestionSelectionCanceled = false;

this.fireEvent(this.EVENT_SUGGESTION_ITEM_SELECT, { item });
}

Expand Down Expand Up @@ -857,6 +892,7 @@ class Input extends UI5Element {

this.value = inputValue;
this.highlightValue = inputValue;
this.valueBeforeItemPreview = inputValue;

if (isSafari()) {
// When setting the value by hand, Safari moves the cursor when typing in the middle of the text (See #1761)
Expand Down
9 changes: 9 additions & 0 deletions packages/main/test/pages/Input.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ <h3> Input test change</h3>
<h3> Input test change result</h3>
<ui5-input id="inputChangeResult"></ui5-input>

<h3>Input test ESC</h3>
<ui5-input id="myInputEsc"show-suggestions style="width: 100%">
<ui5-suggestion-item text="Chromium"></ui5-suggestion-item>
<ui5-suggestion-item text="Titanium"></ui5-suggestion-item>
<ui5-suggestion-item text="Iron"></ui5-suggestion-item>
<ui5-suggestion-item text="Gold"></ui5-suggestion-item>
<ui5-suggestion-item text="Silver"></ui5-suggestion-item>
</ui5-input>

<h3> Input readonly</h3>
<ui5-input id="input-readonly" value="Value can`t be changed" readonly></ui5-input>
<ui5-input id="input-readonly" placeholder="Value can`t be changed" readonly></ui5-input>
Expand Down
20 changes: 20 additions & 0 deletions packages/main/test/specs/Input.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,26 @@ describe("Input general interaction", () => {
assert.strictEqual(inputResult.getValue(), "1", "suggestionItemSelect is not fired as item is 'Inactive'");
});

it("handles suggestions selection cancel with ESC", () => {
const suggestionsInput = $("#myInputEsc").shadow$("input");

// act
suggestionsInput.click();
suggestionsInput.keys("ch");
suggestionsInput.keys("ArrowDown");

// assert
assert.strictEqual(suggestionsInput.getValue(), "Chromium",
"The value is updated as the item has been previewed.");

// act
suggestionsInput.keys("Escape");

// assert
assert.strictEqual(suggestionsInput.getValue(), "ch",
"The value is restored as ESC has been pressed.");
});

it("handles group suggestion item via keyboard", () => {
const suggestionsInput = $("#myInputGrouping").shadow$("input");
const inputResult = $("#inputResultGrouping").shadow$("input");
Expand Down

0 comments on commit 4494037

Please sign in to comment.