diff --git a/packages/main/cypress/specs/Input.cy.tsx b/packages/main/cypress/specs/Input.cy.tsx index 67c036c394bb..1f5dd8f886e2 100644 --- a/packages/main/cypress/specs/Input.cy.tsx +++ b/packages/main/cypress/specs/Input.cy.tsx @@ -126,3 +126,460 @@ describe("Input Tests", () => { .should("not.have.attr", "tabindex", "0"); }); }); + +describe("Input general interaction", () => { + it("handles suggestions selection cancel with ESC", () => { + cy.mount( + + + + + + + + ); + + cy.get("ui5-input") + .as("input"); + + cy.get("@input") + .shadow() + .find("ui5-responsive-popover") + .as("popover"); + + cy.get("@input") + .realClick(); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .realType("C"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@popover") + .should("have.attr", "open"); + + cy.get("@input") + .realPress("ArrowDown"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .should("have.attr", "value", "Titanium"); + + cy.get("@input") + .realPress("Escape"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .should("have.value", "C"); + }); + + it("tests selection-change with custom items", () => { + cy.mount( + + + + + + + + ); + + cy.get("ui5-input") + .as("input"); + + cy.get("@input") + .shadow() + .find("ui5-responsive-popover") + .as("popover"); + + cy.get("@input") + .realClick(); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .realType("c"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@popover") + .should("have.attr", "open"); + + cy.get("@input") + .realPress("ArrowDown"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .should("have.value", "Compact") + .should("not.have.attr", "focused"); + + cy.get("ui5-suggestion-item") + .eq(1) + .should("have.attr", "focused"); + + cy.get("@input") + .realPress("ArrowDown"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("ui5-suggestion-item") + .eq(2) + .should("have.attr", "focused"); + + cy.get("ui5-suggestion-item") + .eq(1) + .should("not.have.attr", "focused"); + }); +}); + +describe("Input arrow navigation", () => { + it("Value state header and group headers should be included in the arrow navigation", () => { + cy.mount( + +
+ Custom error value state message with a Link. +
+ + + + + + + ); + + cy.get("ui5-input") + .as("input"); + + cy.get("@input") + .realClick() + .realType("a") + .realPress("ArrowDown"); + + cy.get("@input") + .should("not.have.attr", "focused"); + + cy.get("@input") + .shadow() + .find("ui5-responsive-popover") + .as("ui5-responsive-popover"); + + cy.get("@ui5-responsive-popover") + .find("div") + .as("valueMessage") + .should("have.class", "ui5-responsive-popover-header--focused"); + + cy.get("@input") + .realPress("ArrowDown"); + + cy.get("@valueMessage") + .should("not.have.class", "ui5-responsive-popover-header--focused"); + + cy.get("ui5-suggestion-item") + .eq(0) + .should("have.attr", "focused"); + }); + + it("Should navigate up and down through the suggestions popover with arrow keys", () => { + cy.mount( + + + + + + + + ); + + cy.get("#myInput2") + .as("input"); + + cy.get("@input") + .shadow() + .find("ui5-responsive-popover") + .as("popover"); + + cy.get("@input").realClick(); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + cy.get("@input").realType("c"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@popover") + .should("have.attr", "open"); + + cy.get("@input").realPress("ArrowDown"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("ui5-suggestion-item").eq(1).should("have.attr", "text", "Compact"); + cy.get("@input").should("not.have.attr", "focused"); + cy.get("ui5-suggestion-item").eq(1).should("have.attr", "focused"); + + cy.get("@input") + .realPress("ArrowDown"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("ui5-suggestion-item").eq(2).should("have.attr", "focused"); + cy.get("ui5-suggestion-item").eq(1).should("not.have.attr", "focused"); + + cy.get("@input") + .realPress("ArrowUp"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("ui5-suggestion-item").eq(1).should("have.attr", "focused"); + cy.get("ui5-suggestion-item").eq(2).should("not.have.attr", "focused"); + + cy.get("@input").realPress("ArrowUp"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + cy.get("@input").realPress("ArrowUp"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input").should("have.attr", "focused"); + cy.get("ui5-suggestion-item").first().should("not.have.attr", "focused"); + }); +}); + +describe("Input PAGEUP/PAGEDOWN navigation", () => { + beforeEach(() => { + cy.mount( + + + + + + + + + + + + + + + + + ); + }); + it("Should focus the tenth item from the suggestions popover with PAGEDOWN", () => { + cy.get("ui5-input") + .as("input"); + + cy.get("@input") + .realClick(); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .realType("a"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .realPress("ArrowDown"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .realPress("PageDown"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("ui5-suggestion-item") + .eq(11) + .should("have.attr", "text", "Antigua and Barbuda"); + + cy.get("ui5-suggestion-item") + .eq(11) + .should("have.attr", "focused"); + }); + + it("Should focus the -10 item/group header from the suggestions popover with PAGEUP", () => { + cy.get("ui5-input") + .as("input"); + + cy.get("@input") + .realClick(); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .realType("a"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .realPress("ArrowUp"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("ui5-suggestion-item-group") + .eq(0) + .should("have.attr", "focused"); + + cy.get("@input") + .realPress("PageDown"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("@input") + .realPress("PageUp"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + + cy.get("ui5-suggestion-item-group") + .eq(0) + .should("have.attr", "focused"); + }); +}); + +describe("Selection-change event", () => { + it("Selection-change event fires with null arguments when suggestion was selected but user alters input value to something else", () => { + cy.mount( + + + + + + ); + + cy.get("ui5-input") + .as("input"); + + cy.get("ui5-input") + .shadow() + .find("input") + .as("innerInput"); + + let eventCount = 0; + + cy.get("@input").then($input => { + $input[0].addEventListener("ui5-selection-change", () => { + eventCount++; + }); + }); + + cy.get("@innerInput") + .realClick(); + cy.get("@innerInput") + .type("C"); + cy.get("@innerInput") + .realPress("ArrowDown"); + cy.get("@innerInput") + .realPress("Enter"); + + cy.get("@innerInput") + .should("have.value", "Compact"); + + cy.get("@innerInput") + .realClick(); + cy.get("@innerInput") + .clear(); + cy.get("@innerInput") + .type("N"); + cy.get("@innerInput") + .realPress("Enter"); + + cy.get("@innerInput") + .should("have.value", "N"); + + cy.then(() => { + expect(eventCount).to.equal(2); + }); + }); +}); + +describe("Change event behavior when selecting the same suggestion item", () => { + let changeCount = 0; + + beforeEach(() => { + cy.mount( + + + + + + + + + + ); + + cy.get("#myInput") + .as("input"); + + cy.get("@input").then($el => { + $el[0].addEventListener("change", () => { + changeCount++; + }); + }); + }); + + it("Change event is not fired when the same suggestion item is selected (with typeahead)", () => { + cy.get("@input") + .realClick(); + + cy.get("@input") + .realType("a"); + + cy.get("@input").realPress("Enter"); + cy.get("@input").should("have.value", "Afghanistan"); + + cy.get("@input").realPress("Backspace"); + cy.get("@input").realPress("ArrowDown"); + cy.get("@input").realPress("ArrowDown"); + cy.get("@input").realPress("Enter"); + + cy.get("@input").should("have.value", "Afghanistan"); + cy.then(() => { + expect(changeCount).to.equal(1); + }); + }); + + it("Change event is not fired when the same suggestion item is selected (no-typeahead)", () => { + cy.get("@input").invoke("attr", "value", "Afghanistan"); + cy.get("@input").invoke("attr", "no-typeahead", true); + + cy.get("@input").realPress("Backspace"); + + cy.get("@input").realPress("ArrowDown"); + cy.get("@input").realPress("ArrowDown"); + cy.get("@input").realPress("Enter"); + + cy.get("@input").should("have.value", "Afghanistan"); + cy.then(() => { + expect(changeCount).to.equal(1); + }); + }); + + it("Change event is not fired when the same suggestion item is selected after focus out and selecting suggestion again", () => { + cy.get("@input") + .invoke("attr", "value", "Afghanistan"); + + cy.get("@input") + .realPress("Tab"); + + cy.get("@input") + .realClick(); + cy.get("@input") + .realPress("ArrowDown"); + cy.get("@input") + .realPress("ArrowDown"); + cy.get("@input") + .realPress("Enter"); + + cy.get("@input").should("have.value", "Afghanistan"); + cy.then(() => { + expect(changeCount).to.equal(1); + }); + }); +}); diff --git a/packages/main/src/Input.ts b/packages/main/src/Input.ts index bdc3d63e79cb..dc07c69154b1 100644 --- a/packages/main/src/Input.ts +++ b/packages/main/src/Input.ts @@ -573,6 +573,7 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement _shouldAutocomplete?: boolean; _keyDown?: boolean; _isKeyNavigation?: boolean; + _indexOfSelectedItem: number; _selectedText?: string; _clearIconClicked?: boolean; _focusedAfterClear: boolean; @@ -639,6 +640,8 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement this._isChangeTriggeredBySuggestion = false; + this._indexOfSelectedItem = -1; + this._handleResizeBound = this._handleResize.bind(this); this._keepInnerValue = false; @@ -717,6 +720,7 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement const item = this._getFirstMatchingItem(value); if (item) { this._handleTypeAhead(item); + this._selectMatchingItem(item); } } } @@ -810,15 +814,22 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement this._keyDown = false; } + get currentItemIndex() { + const allItems = this.Suggestions?._getItems() as IInputSuggestionItemSelectable[]; + const currentItem = allItems.find(item => { return item.selected || item.focused; }); + const indexOfCurrentItem = currentItem ? allItems.indexOf(currentItem) : -1; + return indexOfCurrentItem; + } + _handleUp(e: KeyboardEvent) { if (this.Suggestions?.isOpened()) { - this.Suggestions.onUp(e); + this.Suggestions.onUp(e, this.currentItemIndex); } } _handleDown(e: KeyboardEvent) { if (this.Suggestions?.isOpened()) { - this.Suggestions.onDown(e); + this.Suggestions.onDown(e, this.currentItemIndex); } } @@ -1160,6 +1171,10 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement this.Suggestions?.onItemPress(e); } + _selectMatchingItem(item: IInputSuggestionItemSelectable) { + item.selected = true; + } + _handleTypeAhead(item: IInputSuggestionItemSelectable) { const value = item.text ? item.text : ""; @@ -1201,10 +1216,12 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement this.focused = false; } - if (this._changeToBeFired) { + if (this._changeToBeFired && !this._isChangeTriggeredBySuggestion) { this.fireDecoratorEvent(INPUT_EVENTS.CHANGE); - this._changeToBeFired = false; + } else { + this._isChangeTriggeredBySuggestion = false; } + this._changeToBeFired = false; this.open = false; this.isTyping = false; @@ -1402,7 +1419,6 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement if (shouldFireSelectionChange) { this.fireSelectionChange(suggestionItem, true); } - this.acceptSuggestion(suggestionItem, keyboardUsed); } diff --git a/packages/main/src/features/InputSuggestions.ts b/packages/main/src/features/InputSuggestions.ts index 5705e954bffe..045b60ba8918 100644 --- a/packages/main/src/features/InputSuggestions.ts +++ b/packages/main/src/features/InputSuggestions.ts @@ -81,15 +81,17 @@ class Suggestions { this.selectedItemIndex = -1; } - onUp(e: KeyboardEvent) { + onUp(e: KeyboardEvent, indexOfItem: number) { e.preventDefault(); - this._handleItemNavigation(false /* forward */); + const index = !this.isOpened && this._hasValueState && indexOfItem === -1 ? 0 : indexOfItem; + this._handleItemNavigation(false /* forward */, index); return true; } - onDown(e: KeyboardEvent) { + onDown(e: KeyboardEvent, indexOfItem: number) { e.preventDefault(); - this._handleItemNavigation(true /* forward */); + const index = !this.isOpened && this._hasValueState && indexOfItem === -1 ? 0 : indexOfItem; + this._handleItemNavigation(true /* forward */, index); return true; } @@ -298,11 +300,12 @@ class Suggestions { return !!(this._getPicker()?.open); } - _handleItemNavigation(forward: boolean) { + _handleItemNavigation(forward: boolean, index: number) { + this.selectedItemIndex = index; + if (!this._getItems().length) { return; } - if (forward) { this._selectNextItem(); } else { @@ -312,6 +315,7 @@ class Suggestions { _selectNextItem() { const itemsCount = this._getItems().length; + const previousSelectedIdx = this.selectedItemIndex; if (this._hasValueState && previousSelectedIdx === -1 && !this.component._isValueStateFocused) { diff --git a/packages/main/test/specs/Input.spec.js b/packages/main/test/specs/Input.spec.js index 486cc2327ed3..8425aa111d89 100644 --- a/packages/main/test/specs/Input.spec.js +++ b/packages/main/test/specs/Input.spec.js @@ -390,28 +390,6 @@ describe("Input general interaction", () => { // assert.strictEqual(await suggestionsInput.getValue(), "Portugal", "First item has been selected again"); }); - it("handles suggestions selection cancel with ESC", async () => { - await browser.url(`test/pages/Input.html`); - - const suggestionsInput = await browser.$("#myInputEsc"); - - // act - await suggestionsInput.click(); - await suggestionsInput.keys("c"); - await suggestionsInput.keys("ArrowDown"); - - // assert - assert.strictEqual(await suggestionsInput.getValue(), "Chromium", - "The value is updated as the item has been previewed."); - - // act - await suggestionsInput.keys("Escape"); - - // assert - assert.strictEqual(await suggestionsInput.getProperty("value"), "c", - "The value is restored as ESC has been pressed."); - }); - it("Input value should correspond to suggestion item when it is clicked", async () => { await browser.url(`test/pages/Input.html`); @@ -759,27 +737,6 @@ describe("Input general interaction", () => { assert.ok(await helpPopover.isDisplayedInViewport(), "The help popover remains open as the focus is within."); }); - it("tests selection-change with custom items", async () => { - await browser.url(`test/pages/Input.html`); - - const selChangeFireCount = $("#custom-items-selection-change-count"); - const selChangeItemIndex = $("#custom-items-selection-item-index"); - const input = await $("#input-custom-flat").shadow$("input"); - - await input.click(); - await input.keys("a"); - - await input.keys("ArrowDown"); - - assert.strictEqual(await selChangeFireCount.getHTML(false), "1", "The selection-change event is fired once"); - assert.strictEqual(await selChangeItemIndex.getHTML(false), "0", "The selected item index is correct"); - - await input.keys("ArrowDown"); - - assert.strictEqual(await selChangeFireCount.getHTML(false), "2", "The selection-change event is fired twice"); - assert.strictEqual(await selChangeItemIndex.getHTML(false), "1", "The selected item index is correct"); - }); - it("fires open event when suggestions picker is opened on typing", async () => { await browser.url(`test/pages/Input.html`); const input = await browser.$("#myInput"); @@ -864,50 +821,6 @@ describe("Input general interaction", () => { assert.strictEqual(await suggestionsCount.getText(), "5 results are available", "Suggestions count is available since the suggestions popover is opened"); }); - it("Suggestions announcement", async () => { - await browser.url(`test/pages/Input.html`); - - const inputWithGroups = await $("#inputCompact"); - const inputSuggestions = await $("#myInput2"); - const inputWithGroupsInnerInput = await inputWithGroups.shadow$("input"); - const inputWithGroupsAnnouncement = await inputWithGroups.shadow$(`#selectionText`); - const suggestionsAnnouncement = await inputSuggestions.shadow$(`#selectionText`); - - //act - await inputWithGroups.click(); - - //assert - assert.strictEqual(await inputWithGroupsAnnouncement.getText(), "", "Suggestions announcement is not available on initially"); - - //act - await inputWithGroupsInnerInput.keys("a"); - await inputWithGroupsInnerInput.keys("ArrowDown"); - - //assert - assert.strictEqual(await inputWithGroupsAnnouncement.getText(), "Group Header A", "Group item announcement should not contain the position and total count."); - - //act - await inputWithGroupsInnerInput.keys("ArrowDown"); - - const announcementText = await inputWithGroupsAnnouncement.getText(); - - //assert - assert.ok(announcementText.includes("List item 1 of 12"), "The total count announcement and position of items should exclude group items."); - assert.strictEqual(await inputWithGroupsAnnouncement.getText(), "explore List item 1 of 12", "The additional text is announced"); - await inputWithGroupsInnerInput.keys("Backspace"); - - // Close suggestions to not intercept the click in the input below for the last test - await inputWithGroupsInnerInput.keys("Escape"); - - //act - await inputSuggestions.click(); - await (await inputSuggestions.shadow$("input")).keys("c"); - await inputWithGroupsInnerInput.keys("ArrowDown"); - - //assert - assert.strictEqual(await suggestionsAnnouncement.getText(), "List item 1 of 5", "Item position and count is announced correctly"); - }); - it("Should close the Popover when no suggestions are available", async () => { await browser.url(`test/pages/Input.html`); @@ -1128,81 +1041,12 @@ describe("Input general interaction", () => { assert.strictEqual(await inner.getValue(), "", "Inner input's value should be empty"); }); - it("Change event is not fired when the same suggestion item is selected (with typeahead) - #8912", async () => { - const suggestionsInput = await browser.$("#myInput"); - - await suggestionsInput.click(); - await suggestionsInput.keys("a"); - - await suggestionsInput.keys("ArrowDown"); - await suggestionsInput.keys("ArrowDown"); - await suggestionsInput.keys("Enter"); - - const changeCount = await browser.$("#myInput-change-count"); - - // Assert - assert.strictEqual(await changeCount.getHTML(false), "1", "The change event is called once"); - assert.strictEqual(await suggestionsInput.getValue(), "Afghanistan", "Input's value should be the text of the selected item"); - - await suggestionsInput.keys("Backspace"); - - await suggestionsInput.keys("ArrowDown"); - await suggestionsInput.keys("ArrowDown"); - await suggestionsInput.keys("Enter"); - - assert.strictEqual(await changeCount.getHTML(false), "1", "The change event is still called once"); - assert.strictEqual(await suggestionsInput.getValue(), "Afghanistan", "Input's value should be the text of the selected item"); - }); - - it("Change event is not fired when the same suggestion item is selected (no-typeahead) - #8912", async () => { - const suggestionsInput = await browser.$("#myInput"); - await browser.execute(() => { - document.querySelector("#myInput").noTypeahead = true; - }); - - await suggestionsInput.keys("Backspace"); - - await suggestionsInput.keys("ArrowDown"); - await suggestionsInput.keys("ArrowDown"); - await suggestionsInput.keys("Enter"); - - const changeCount = await browser.$("#myInput-change-count"); - - assert.strictEqual(await changeCount.getHTML(false), "1", "The change event is still called once"); - assert.strictEqual(await suggestionsInput.getValue(), "Afghanistan", "Input's value should be the text of the selected item"); - - // restore the default property value - await browser.execute(() => { - document.querySelector("#myInput").noTypeahead = false; - }); - }); - - it("Change event is not fired when the same suggestion item is selected after focus out and selecting suggestion again - #8912", async () => { - const suggestionsInput = await browser.$("#myInput"); - const changeCount = await browser.$("#myInput-change-count"); - - assert.strictEqual(await changeCount.getHTML(false), "1", "The change event is called once"); - assert.strictEqual(await suggestionsInput.getValue(), "Afghanistan", "Input's value should be the text of the selected item"); - - await suggestionsInput.keys("Tab"); - - await suggestionsInput.click(); - await suggestionsInput.keys("Backspace"); - await suggestionsInput.keys("ArrowDown"); - await suggestionsInput.keys("ArrowDown"); - await suggestionsInput.keys("Enter"); - - // Assert - assert.strictEqual(await changeCount.getHTML(false), "1", "The change event is still called once"); - assert.strictEqual(await suggestionsInput.getValue(), "Afghanistan", "Input's value should be the text of the selected item"); - }); - it("Tests prevented input event", async () => { const input = await $("#prevent-input-event"); const innerInput = await input.shadow$("input"); await input.click(); - + await innerInput.keys("a"); await innerInput.keys("b"); await innerInput.keys("c"); @@ -1222,196 +1066,6 @@ describe("Input general interaction", () => { }); }); -describe("Input arrow navigation", () => { - - it("Should navigate up and down through the suggestions popover with arrow keys", async () => { - await browser.url(`test/pages/Input.html`); - - const suggestionsInput = await browser.$("#myInput2"); - - await suggestionsInput.click(); - await suggestionsInput.keys("c"); - await suggestionsInput.keys("ArrowDown"); - - const firstListItem = await suggestionsInput.$("ui5-suggestion-item"); - - assert.strictEqual(await suggestionsInput.getValue(), "Cozy", "First item has been selected"); - assert.strictEqual(await suggestionsInput.getProperty("focused"), false, "Input is not focused"); - assert.strictEqual(await firstListItem.getProperty("focused"), true, "First list item is focused"); - - await suggestionsInput.keys("ArrowDown"); - const secondListItem = await suggestionsInput.$$("ui5-suggestion-item")[1]; - - assert.strictEqual(await suggestionsInput.getProperty("focused"), false, "Input is not focused"); - assert.strictEqual(await suggestionsInput.getProperty("focused"), false, "Input is not focused"); - assert.strictEqual(await secondListItem.getProperty("focused"), true, "Second list item is focused"); - - await suggestionsInput.keys("ArrowUp"); - - assert.strictEqual(await firstListItem.getProperty("focused"), true, "First list item is focused"); - assert.strictEqual(await secondListItem.getProperty("focused"), false, "Second list item is not focused"); - - await suggestionsInput.keys("ArrowUp"); - - assert.strictEqual(await suggestionsInput.getProperty("focused"), true, "Input is focused"); - assert.strictEqual(await firstListItem.getProperty("focused"), false, "First list item is not focused"); - }); - - it("Value state header and group headers should be included in the arrow navigation", async () => { - await browser.url(`test/pages/Input.html`); - - const suggestionsInput = await browser.$("#inputError"); - - await suggestionsInput.click(); - await suggestionsInput.keys("a"); - await suggestionsInput.keys("ArrowDown"); - - const respPopover = await suggestionsInput.shadow$("ui5-responsive-popover"); - const valueStateHeader = await respPopover.$(".ui5-responsive-popover-header.ui5-valuestatemessage-root"); - const firstListItem = suggestionsInput.$("ui5-suggestion-item"); - const groupHeader = suggestionsInput.$("ui5-suggestion-item-group"); - - assert.strictEqual(await suggestionsInput.getValue(), "a", "Input's value should be the typed-in value"); - assert.strictEqual(await suggestionsInput.getProperty("focused"), false, "Input is not focused"); - assert.strictEqual(await firstListItem.getProperty("focused"), false, "First list item is not focused"); - assert.strictEqual(await groupHeader.getProperty("focused"), false, "Group header is not focused"); - assert.strictEqual(await suggestionsInput.getProperty("_isValueStateFocused"), true, "Value State should not be focused"); - assert.strictEqual(await valueStateHeader.hasClass("ui5-responsive-popover-header--focused"), true, "Value state header is focused"); - - await suggestionsInput.keys("ArrowDown"); - - assert.strictEqual(await suggestionsInput.getProperty("focused"), false, "Input is not focused"); - assert.strictEqual(await firstListItem.getProperty("focused"), false, "First list item is not focused"); - assert.strictEqual(await groupHeader.getProperty("focused"), true, "Group header is focused"); - assert.strictEqual(await valueStateHeader.hasClass("ui5-responsive-popover-header--focused"), false, "Value state header is not focused"); - - await suggestionsInput.keys("ArrowDown"); - - assert.strictEqual(await suggestionsInput.getValue(), "Afghanistan", "Input's value should be the text of the selected item"); - assert.strictEqual(await suggestionsInput.getProperty("focused"), false, "Input is not focused"); - assert.strictEqual(await firstListItem.getProperty("focused"), true, "First list item is focused"); - assert.strictEqual(await groupHeader.getProperty("focused"), false, "Group header is no longer focused"); - assert.strictEqual(await valueStateHeader.hasClass("ui5-responsive-popover-header--focused"), false, "Value state header is not focused"); - - await suggestionsInput.keys("ArrowUp"); - - assert.strictEqual(await suggestionsInput.getProperty("focused"), false, "Input is not focused"); - assert.strictEqual(await firstListItem.getProperty("focused"), false, "First list item is not focused"); - assert.strictEqual(await groupHeader.getProperty("focused"), true, "Group header is focused"); - assert.strictEqual(await valueStateHeader.hasClass("ui5-responsive-popover-header--focused"), false, "Value state header is not focused"); - - - await suggestionsInput.keys("ArrowUp"); - - assert.strictEqual(await suggestionsInput.getProperty("focused"), false, "Input is not focused"); - assert.strictEqual(await firstListItem.getProperty("focused"), false, "First list item is not focused"); - assert.strictEqual(await groupHeader.getProperty("focused"), false, "Group header is not focused"); - assert.strictEqual(await suggestionsInput.getProperty("_isValueStateFocused"), true, "Value State should not be focused"); - assert.strictEqual(await valueStateHeader.hasClass("ui5-responsive-popover-header--focused"), true, "Value state header is focused"); - - await suggestionsInput.keys("ArrowUp"); - - assert.strictEqual(await suggestionsInput.getProperty("focused"), true, "Input is focused"); - assert.strictEqual(await firstListItem.getProperty("focused"), false, "First list item is not focused"); - assert.strictEqual(await groupHeader.getProperty("focused"), false, "Group header is not focused"); - assert.strictEqual(await valueStateHeader.hasClass("ui5-responsive-popover-header--focused"), false, "Value state header is not focused"); - }); - - it("Items should not hide behind value state header on arrow up navigation", async () => { - await browser.url(`test/pages/Input.html`); - await browser.setWindowSize(1000, 400); - - const input = await browser.$("#inputError"); - await input.scrollIntoView(); - await input.click(); - await input.keys("a"); - - let isInVisibleArea = await browser.executeAsync(async done => { - const input = document.getElementById("inputError"); - const listItems = input.querySelectorAll("ui5-suggestion-item"); - const elementRect = listItems[6].getBoundingClientRect(); //Suggestion item "Angola" - - const popover = input.shadowRoot.querySelector("ui5-responsive-popover"); - const scrollableRect = popover.shadowRoot.querySelector(".ui5-popup-content").getBoundingClientRect(); - - // Check if the element is within the visible area - const isElementAboveViewport = elementRect.bottom < scrollableRect.top; - const isElementBelowViewport = elementRect.top > scrollableRect.bottom; - const isElementLeftOfViewport = elementRect.right < scrollableRect.left; - const isElementRightOfViewport = elementRect.left > scrollableRect.right; - - const isListItemInVisibleArea = ( - !isElementAboveViewport && - !isElementBelowViewport && - !isElementLeftOfViewport && - !isElementRightOfViewport - ); - - done(isListItemInVisibleArea); - }); - - assert.notOk(isInVisibleArea, "Item 'Angola' should not be displayed in the viewport"); - - // click ArrowDown 9 times - 1 time through VSH and 1 through group header and 7 times through the list items - await input.keys(["ArrowDown", "ArrowDown", "ArrowDown", "ArrowDown", "ArrowDown", "ArrowDown", "ArrowDown", "ArrowDown", "ArrowDown"]); - - isInVisibleArea = isInVisibleArea = await browser.executeAsync(async done => { - const input = document.getElementById("inputError"); - const listItems = input.querySelectorAll("ui5-suggestion-item"); - const elementRect = listItems[6].getBoundingClientRect(); //Suggestion item "Angola" - - const popover = input.shadowRoot.querySelector("ui5-responsive-popover"); - const scrollableRect = popover.shadowRoot.querySelector(".ui5-popup-content").getBoundingClientRect(); - - // Check if the element is within the visible area - const isElementAboveViewport = elementRect.bottom < scrollableRect.top; - const isElementBelowViewport = elementRect.top > scrollableRect.bottom; - const isElementLeftOfViewport = elementRect.right < scrollableRect.left; - const isElementRightOfViewport = elementRect.left > scrollableRect.right; - - const isListItemInVisibleArea = ( - !isElementAboveViewport && - !isElementBelowViewport && - !isElementLeftOfViewport && - !isElementRightOfViewport - ); - - done(isListItemInVisibleArea); - }); - - assert.ok(isInVisibleArea, "Item 'Angola' should be displayed in the viewport"); - - // click ArrowUp 6 times through the list items to trigger scrolling and reach the first suggestion item - await input.keys(["ArrowUp", "ArrowUp", "ArrowUp", "ArrowUp", "ArrowUp", "ArrowUp"]); - - isInVisibleArea = await browser.executeAsync(async done => { - const input = document.getElementById("inputError"); - const listItems = input.querySelectorAll("ui5-suggestion-item"); - const elementRect = listItems[0].getBoundingClientRect(); //Suggestion item "Afganistan" - - const popover = input.shadowRoot.querySelector("ui5-responsive-popover"); - const scrollableRect = popover.shadowRoot.querySelector(".ui5-popup-content").getBoundingClientRect(); - - // Check if the element is within the visible area - const isElementAboveViewport = elementRect.bottom < scrollableRect.top; - const isElementBelowViewport = elementRect.top > scrollableRect.bottom; - const isElementLeftOfViewport = elementRect.right < scrollableRect.left; - const isElementRightOfViewport = elementRect.left > scrollableRect.right; - - const isListItemInVisibleArea = ( - !isElementAboveViewport && - !isElementBelowViewport && - !isElementLeftOfViewport && - !isElementRightOfViewport - ); - - done(isListItemInVisibleArea); - }); - - assert.ok(isInVisibleArea, "Item 'Afganistan' should be displayed in the viewport"); - }); -}); - describe("Input HOME navigation", () => { it("Should move caret to beginning of input with HOME if focus is on Input", async () => { await browser.url(`test/pages/Input.html`); @@ -1587,48 +1241,6 @@ describe("Input PAGEUP/PAGEDOWN navigation", () => { assert.strictEqual(await firstListItem.getProperty("focused"), false, "Responsive popover remains open and first list item is not focused"); }); - - it("Should focus the tenth item from the suggestions popover with PAGEDOWN", async () => { - await browser.url(`test/pages/Input.html`); - - const suggestionsInput = await browser.$("#myInput"); - - await suggestionsInput.click(); - await suggestionsInput.keys("a"); - - // Moving focus to suggestions popover, because by design PAGEDOWN does nothing if focus is on input - await suggestionsInput.keys("ArrowDown"); - - await suggestionsInput.keys("PageDown"); - - const tenthListItem = await suggestionsInput.$("ui5-suggestion-item:nth-of-type(10)"); - - assert.strictEqual(await suggestionsInput.getValue(), "Azerbaijan", "Tenth item has been selected"); - assert.strictEqual(await suggestionsInput.getProperty("focused"), false, "Input is not focused"); - assert.strictEqual(await tenthListItem.getProperty("focused"), true, "Tenth list item is focused"); - }); - - it("Should focus the -10 item/group header from the suggestions popover with PAGEUP", async () => { - await browser.url(`test/pages/Input.html`); - - const suggestionsInput = await browser.$("#myInput"); - await suggestionsInput.scrollIntoView(); - - await suggestionsInput.click(); - await suggestionsInput.keys("a"); - - // Moving focus to suggestions popover, because by design PAGEUP does nothing if focus is on input - await suggestionsInput.keys("ArrowDown"); - - await suggestionsInput.keys("PageDown"); - await suggestionsInput.keys("PageUp"); - - const groupHeader = await suggestionsInput.$("ui5-suggestion-item-group"); - - assert.strictEqual(await suggestionsInput.getValue(), "a", "No item has been selected"); - assert.strictEqual(await suggestionsInput.getProperty("focused"), false, "Input is not focused"); - assert.strictEqual(await groupHeader.getProperty("focused"), true, "Group header is focused"); - }); }); describe("XSS tests for suggestions", () => { @@ -1780,30 +1392,6 @@ describe("Selection-change event", () => { assert.strictEqual(await selectionChangeCount.getText(), "1", "Selection-change event was fired once"); }); - - it("Selection-change event fires with null arguments when suggestion was selected but user alters input value to something else", async () => { - await browser.url(`test/pages/Input.html`); - - const input = await $("#input-selection-change"); - const inner = await input.shadow$("input"); - const selectionChangeCount = await $("#input-selection-change-count"); - const selectionChangeValue = await $("#input-selection-change-value"); - - await inner.click(); - await inner.keys("C"); - - // select first item - await input.keys("ArrowDown"); - assert.strictEqual(await selectionChangeCount.getText(), "1", "Selection-change event was fired once"); - assert.strictEqual(await selectionChangeValue.getText(), "Cozy", "Selection-change event was fired with arguments"); - - await inner.click(); - await inner.keys("N"); // this value is not in the suggestions - await inner.keys("Enter"); - - assert.strictEqual(await selectionChangeCount.getText(), "2", "Selection-change event was fired twice"); - assert.strictEqual(await selectionChangeValue.getText(), "", "Selection-change event was fired with null arguments"); - }); }); describe("Property open", () => {