Skip to content

Commit edcdd24

Browse files
authored
fix(ui5-input): fix typeahead on mobile devices (#5292)
* fix(ui5-input): fix typeahead on mobile devices The component no longer throws an exception when the input has suggestions and the typeahead functionality is adjusted. * fix(ui5-input): fix typeahead on mobile devices add tests * fix(ui5-input): fix typeahead on mobile devices try different DOM selectors in the mobile tests * fix(ui5-input): fix typeahead on mobile devices adjust mobile tests * fix(ui5-input): fix typeahead on mobile devices adjust tests * fix(ui5-input): fix typeahead on mobile devices adjust mobile tests
1 parent ff044b0 commit edcdd24

File tree

4 files changed

+67
-20
lines changed

4 files changed

+67
-20
lines changed

packages/main/src/Input.js

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ class Input extends UI5Element {
804804

805805
_handleEnter(event) {
806806
const itemPressed = !!(this.Suggestions && this.Suggestions.onEnter(event));
807-
807+
const innerInput = this.getInputDOMRefSync();
808808
// Check for autocompleted item
809809
const matchingItem = this.suggestionItems.find(item => {
810810
return (item.text && item.text === this.value) || (item.textContent === this.value);
@@ -820,8 +820,12 @@ class Input extends UI5Element {
820820
}
821821
}
822822

823+
if (this._isPhone && !this.suggestionItems.length) {
824+
innerInput.setSelectionRange(this.value.length, this.value.length);
825+
}
826+
823827
if (!itemPressed) {
824-
this.fireEventByAction(this.ACTION_ENTER);
828+
this.fireEventByAction(this.ACTION_ENTER, event);
825829
this.lastConfirmedValue = this.value;
826830

827831
if (this.FormSupport) {
@@ -902,7 +906,6 @@ class Input extends UI5Element {
902906
this.focused = true; // invalidating property
903907
this.previousValue = this.value;
904908
this.valueBeforeItemPreview = this.value;
905-
this._shouldAutocomplete = false;
906909

907910
this._inputIconFocused = event.target && event.target === this.querySelector("[ui5-icon]");
908911
}
@@ -995,7 +998,9 @@ class Input extends UI5Element {
995998
_handleInput(event) {
996999
const inputDomRef = this.getInputDOMRefSync();
9971000
const emptyValueFiredOnNumberInput = this.value && this.isTypeNumber && !inputDomRef.value;
1001+
const eventType = event.inputType || event.detail.inputType;
9981002

1003+
this._shouldAutocomplete = eventType !== "deleteContentBackward" && !this.noTypeahead;
9991004
this.suggestionSelectionCanceled = false;
10001005

10011006
if (emptyValueFiredOnNumberInput && !this._backspaceKeyDown) {
@@ -1030,7 +1035,7 @@ class Input extends UI5Element {
10301035
this.valueBeforeItemPreview = newValue;
10311036

10321037
// fire events
1033-
this.fireEvent(this.EVENT_INPUT);
1038+
this.fireEvent(this.EVENT_INPUT, { inputType: event.inputType });
10341039
this.fireEvent("value-changed");
10351040
return;
10361041
}
@@ -1049,7 +1054,7 @@ class Input extends UI5Element {
10491054
*/
10501055
const skipFiring = (inputDomRef.value === this.value) && isIE() && !this._keyDown && !!this.placeholder;
10511056

1052-
!skipFiring && this.fireEventByAction(this.ACTION_USER_INPUT);
1057+
!skipFiring && this.fireEventByAction(this.ACTION_USER_INPUT, event);
10531058

10541059
this.hasSuggestionItemSelected = false;
10551060
this._isValueStateFocused = false;
@@ -1090,7 +1095,11 @@ class Input extends UI5Element {
10901095
this.value = value;
10911096

10921097
innerInput.value = value;
1093-
innerInput.setSelectionRange(filterValue.length, value.length);
1098+
setTimeout(() => {
1099+
innerInput.setSelectionRange(filterValue.length, value.length);
1100+
}, 0);
1101+
1102+
this._shouldAutocomplete = false;
10941103
}
10951104

10961105
_handleResize() {
@@ -1242,7 +1251,7 @@ class Input extends UI5Element {
12421251
return this.getSuggestionByListItem(this._previewItem);
12431252
}
12441253

1245-
async fireEventByAction(action) {
1254+
async fireEventByAction(action, event) {
12461255
await this.getInputDOMRef();
12471256

12481257
if (this.disabled || this.readonly) {
@@ -1268,7 +1277,7 @@ class Input extends UI5Element {
12681277
}
12691278

12701279
if (isUserInput) { // input
1271-
this.fireEvent(this.EVENT_INPUT);
1280+
this.fireEvent(this.EVENT_INPUT, { inputType: event.inputType });
12721281
// Angular two way data binding
12731282
this.fireEvent("value-changed");
12741283
return;
@@ -1300,8 +1309,8 @@ class Input extends UI5Element {
13001309
}
13011310

13021311
getInputDOMRefSync() {
1303-
if (isPhone() && this.Suggestions) {
1304-
return this.Suggestions && this.Suggestions.responsivePopover.querySelector(".ui5-input-inner-phone");
1312+
if (isPhone() && this.Suggestions && this.Suggestions.responsivePopover) {
1313+
return this.Suggestions.responsivePopover.querySelector(".ui5-input-inner-phone").shadowRoot.querySelector("input");
13051314
}
13061315

13071316
return this.nativeInput;

packages/main/src/InputPopover.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
.value="{{value}}"
3131
?show-clear-icon={{showClearIcon}}
3232
placeholder="{{placeholder}}"
33-
@input="{{_handleInput}}"
33+
@ui5-input="{{_handleInput}}"
3434
@change="{{_handleChange}}"
3535
></ui5-input>
3636
</div>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const assert = require("chai").assert;
2+
const PORT = require("./_port.js");
3+
4+
describe("Typeahead", () => {
5+
before(async () => {
6+
await browser.url(`http://localhost:${PORT}/test-resources/pages/Input.html`);
7+
await browser.emulateDevice('iPhone X');
8+
});
9+
10+
it("Should autocomplete the first matched suggestion item", async () => {
11+
const input = await browser.$("#myInput2");
12+
const sExpected = "Cozy";
13+
const staticAreaItemClassName = await browser.getStaticAreaItemClassName("#myInput2")
14+
15+
await input.scrollIntoView();
16+
await input.click();
17+
18+
const dialogInput = await browser.$(`.${staticAreaItemClassName}`).shadow$("ui5-responsive-popover").$(".ui5-input-inner-phone");
19+
await dialogInput.keys("c");
20+
assert.strictEqual(await dialogInput.getProperty("value"), sExpected, "Value is autocompleted");
21+
});
22+
23+
it("Should not perform typeahead when it is disabled", async () => {
24+
await browser.url(`http://localhost:${PORT}/test-resources/pages/Input.html`);
25+
26+
const input = await browser.$("#input-disabled-autocomplete");
27+
const staticAreaItemClassName = await browser.getStaticAreaItemClassName("#input-disabled-autocomplete")
28+
29+
await input.scrollIntoView();
30+
await input.click();
31+
32+
const dialogInput = await browser.$(`.${staticAreaItemClassName}`).shadow$("ui5-responsive-popover").$(".ui5-input-inner-phone");
33+
await dialogInput.keys("c");
34+
35+
assert.strictEqual(await dialogInput.getProperty("value"), "c", "Value is not autocompleted");
36+
});
37+
});

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -345,11 +345,11 @@ describe("Input general interaction", () => {
345345
it("handles suggestions selection cancel with ESC", async () => {
346346
await browser.url(`http://localhost:${PORT}/test-resources/pages/Input.html`);
347347

348-
const suggestionsInput = await browser.$("#myInputEsc").shadow$("input");
348+
const suggestionsInput = await browser.$("#myInputEsc");
349349

350350
// act
351351
await suggestionsInput.click();
352-
await suggestionsInput.keys("ch");
352+
await suggestionsInput.keys("c");
353353
await suggestionsInput.keys("ArrowDown");
354354

355355
// assert
@@ -360,7 +360,7 @@ describe("Input general interaction", () => {
360360
await suggestionsInput.keys("Escape");
361361

362362
// assert
363-
assert.strictEqual(await suggestionsInput.getValue(), "ch",
363+
assert.strictEqual(await suggestionsInput.getProperty("value"), "c",
364364
"The value is restored as ESC has been pressed.");
365365
});
366366

@@ -379,19 +379,20 @@ describe("Input general interaction", () => {
379379

380380
assert.strictEqual(await suggestionsInput.getValue(), "", "The value is restored as ESC has been pressed.");
381381

382-
await suggestionsInput.keys("Some value");
382+
await suggestionsInput.keys(["a", "b", "c"]);
383383
await suggestionsInput.keys("Enter");
384-
await suggestionsInput.keys("Another value");
384+
await suggestionsInput.keys(["c", "b", "a"]);
385385

386386
// Close sugggestions
387387
await suggestionsInput.keys("Escape");
388388
// Clear value
389389
await suggestionsInput.keys("Escape");
390390

391-
assert.strictEqual(await suggestionsInput.getValue(), "Some value", "The value is restored to the last confirmed by 'ENTER' press one.");
391+
assert.strictEqual(await suggestionsInput.getValue(), "abc", "The value is restored to the last confirmed by 'ENTER' press one.");
392392
});
393393

394394
it("handles group suggestion item via keyboard", async () => {
395+
395396
const suggestionsInput = await browser.$("#myInputGrouping").shadow$("input");
396397
const inputResult = await browser.$("#inputResultGrouping").shadow$("input");
397398
const staticAreaItemClassName = await browser.getStaticAreaItemClassName("#myInputGrouping");
@@ -539,19 +540,19 @@ describe("Input general interaction", () => {
539540
it("Tests suggestions highlighting", async () => {
540541
await browser.url(`http://localhost:${PORT}/test-resources/pages/Input.html`);
541542

542-
const input = await browser.$("#myInputHighlighted").shadow$("input");
543+
const input = await browser.$("#myInputHighlighted");
543544
const staticAreaItemClassName = await browser.getStaticAreaItemClassName("#myInputHighlighted");
544545
const EXPTECTED_TEXT = "<b>Ad</b>am";
545546

546547
await input.click();
547-
await input.keys("ad");
548+
await input.keys(["a", "d"]);
548549

549550
const respPopover = await browser.$(`.${staticAreaItemClassName}`).shadow$("ui5-responsive-popover");
550551
const firstListItem = await respPopover.$("ui5-list").$("ui5-li-suggestion-item");
551552

552553
assert.ok(await respPopover.isDisplayedInViewport(), "The popover is visible");
553554
const firstItemHtml = await firstListItem.getHTML();
554-
assert.include(firstItemHtml, EXPTECTED_TEXT, "The suggestions is highlighted.");
555+
assert.include(firstItemHtml, "<b>Ad</b>am", "The suggestions is highlighted.");
555556
});
556557

557558
it("Doesn't remove value on number type input even if locale specific delimiter/multiple delimiters", async () => {

0 commit comments

Comments
 (0)