Skip to content

Commit ef1fb40

Browse files
authored
fix(ui5-input): cancel suggestion selection with ESC (#2289)
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
1 parent 0742854 commit ef1fb40

File tree

3 files changed

+71
-6
lines changed

3 files changed

+71
-6
lines changed

packages/main/src/Input.js

+42-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
isSpace,
1212
isEnter,
1313
isBackSpace,
14+
isEscape,
1415
} from "@ui5/webcomponents-base/dist/Keys.js";
1516
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
1617
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
@@ -494,11 +495,20 @@ class Input extends UI5Element {
494495
// Indicates if there is selected suggestionItem.
495496
this.hasSuggestionItemSelected = false;
496497

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

504+
// Represents the value before user moves selection between the suggestion items
505+
// and its value remains the same when the user navigates up or down the list.
506+
// Note: Used to cancel selection upon [ESC].
507+
this.valueBeforeItemPreview = "";
508+
509+
// Indicates if the user selection has been canceled with [ESC].
510+
this.suggestionSelectionCanceled = false;
511+
502512
// tracks the value between focus in and focus out to detect that change event should be fired.
503513
this.previousValue = undefined;
504514

@@ -591,6 +601,10 @@ class Input extends UI5Element {
591601
return this._handleEnter(event);
592602
}
593603

604+
if (isEscape(event)) {
605+
return this._handleEscape(event);
606+
}
607+
594608
this._keyDown = true;
595609
}
596610

@@ -624,6 +638,20 @@ class Input extends UI5Element {
624638
}
625639
}
626640

641+
_handleEscape() {
642+
if (this.showSuggestions && this.Suggestions && this.Suggestions._isItemOnTarget()) {
643+
// Restore the value.
644+
this.value = this.valueBeforeItemPreview;
645+
646+
// Mark that the selection has been canceled, so the popover can close
647+
// and not reopen, due to receiving focus.
648+
this.suggestionSelectionCanceled = true;
649+
650+
// Close suggestion popover
651+
this._closeRespPopover(true);
652+
}
653+
}
654+
627655
async _onfocusin(event) {
628656
const inputDomRef = await this.getInputDOMRef();
629657

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

634662
this.focused = true; // invalidating property
635663
this.previousValue = this.value;
664+
this.valueBeforeItemPreview = this.value;
636665

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

714+
this.suggestionSelectionCanceled = false;
715+
685716
if (this.value && this.type === InputType.Number && !isBackSpace(event) && !inputDomRef.value) {
686717
// For input with type="Number", if the delimiter is entered second time, the inner input is firing event with empty value
687718
return;
@@ -711,8 +742,8 @@ class Input extends UI5Element {
711742
this._inputWidth = this.offsetWidth;
712743
}
713744

714-
_closeRespPopover() {
715-
this.Suggestions.close();
745+
_closeRespPopover(preventFocusRestore) {
746+
this.Suggestions.close(preventFocusRestore);
716747
}
717748

718749
async _afterOpenPopover() {
@@ -786,7 +817,8 @@ class Input extends UI5Element {
786817
return !!(this.suggestionItems.length
787818
&& this.focused
788819
&& this.showSuggestions
789-
&& !this.hasSuggestionItemSelected);
820+
&& !this.hasSuggestionItemSelected
821+
&& !this.suggestionSelectionCanceled);
790822
}
791823

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

842+
this.valueBeforeItemPreview = "";
843+
this.suggestionSelectionCanceled = false;
844+
810845
this.fireEvent(this.EVENT_SUGGESTION_ITEM_SELECT, { item });
811846
}
812847

@@ -857,6 +892,7 @@ class Input extends UI5Element {
857892

858893
this.value = inputValue;
859894
this.highlightValue = inputValue;
895+
this.valueBeforeItemPreview = inputValue;
860896

861897
if (isSafari()) {
862898
// When setting the value by hand, Safari moves the cursor when typing in the middle of the text (See #1761)

packages/main/test/pages/Input.html

+9
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,15 @@ <h3> Input test change</h3>
134134
<h3> Input test change result</h3>
135135
<ui5-input id="inputChangeResult"></ui5-input>
136136

137+
<h3>Input test ESC</h3>
138+
<ui5-input id="myInputEsc"show-suggestions style="width: 100%">
139+
<ui5-suggestion-item text="Chromium"></ui5-suggestion-item>
140+
<ui5-suggestion-item text="Titanium"></ui5-suggestion-item>
141+
<ui5-suggestion-item text="Iron"></ui5-suggestion-item>
142+
<ui5-suggestion-item text="Gold"></ui5-suggestion-item>
143+
<ui5-suggestion-item text="Silver"></ui5-suggestion-item>
144+
</ui5-input>
145+
137146
<h3> Input readonly</h3>
138147
<ui5-input id="input-readonly" value="Value can`t be changed" readonly></ui5-input>
139148
<ui5-input id="input-readonly" placeholder="Value can`t be changed" readonly></ui5-input>

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

+20
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,26 @@ describe("Input general interaction", () => {
215215
assert.strictEqual(inputResult.getValue(), "1", "suggestionItemSelect is not fired as item is 'Inactive'");
216216
});
217217

218+
it("handles suggestions selection cancel with ESC", () => {
219+
const suggestionsInput = $("#myInputEsc").shadow$("input");
220+
221+
// act
222+
suggestionsInput.click();
223+
suggestionsInput.keys("ch");
224+
suggestionsInput.keys("ArrowDown");
225+
226+
// assert
227+
assert.strictEqual(suggestionsInput.getValue(), "Chromium",
228+
"The value is updated as the item has been previewed.");
229+
230+
// act
231+
suggestionsInput.keys("Escape");
232+
233+
// assert
234+
assert.strictEqual(suggestionsInput.getValue(), "ch",
235+
"The value is restored as ESC has been pressed.");
236+
});
237+
218238
it("handles group suggestion item via keyboard", () => {
219239
const suggestionsInput = $("#myInputGrouping").shadow$("input");
220240
const inputResult = $("#inputResultGrouping").shadow$("input");

0 commit comments

Comments
 (0)