Skip to content

Commit

Permalink
feat(ui5-input): prevent suggestion-item-select event per suggestion …
Browse files Browse the repository at this point in the history
…item (#7940)

* feat(ui5-input): prevent suggestion-item-select

* feat(ui5-input): prevent suggestion item select

* feat(ui5-input): close suggestions popover on group item enter

* feat(ui5-input): close suggestions popover on group item enter

* feat(ui5-input): add tests for preventing suggestion-item-select event

* feat(ui5-input): keep old order of firing events when suggestion item is selected

* feat(ui5-input): keep old order of firing events when suggestion item is selected
  • Loading branch information
nikoletavnv authored Dec 12, 2023
1 parent 8acfd67 commit a265a65
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 16 deletions.
52 changes: 39 additions & 13 deletions packages/main/src/Input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {
hasSuggestionItemSelected: boolean;
valueBeforeItemSelection: string;
valueBeforeItemPreview: string
suggestionSelectionCanceled: boolean;
suggestionSelectionCancelled: boolean;
previousValue: string;
firstRendering: boolean;
typedInValue: string;
Expand Down Expand Up @@ -664,7 +664,7 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {
this.valueBeforeItemPreview = "";

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

// tracks the value between focus in and focus out to detect that change event should be fired.
this.previousValue = "";
Expand Down Expand Up @@ -870,7 +870,8 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {
}

_handleEnter(e: KeyboardEvent) {
const itemPressed = !!(this.Suggestions && this.Suggestions.onEnter(e));
const suggestionItemPressed = !!(this.Suggestions && this.Suggestions.onEnter(e));

const innerInput = this.getInputDOMRefSync()!;
// Check for autocompleted item
const matchingItem = this.suggestionItems.find(item => {
Expand All @@ -881,7 +882,7 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {
const itemText = matchingItem.text ? matchingItem.text : (matchingItem.textContent || "");

innerInput.setSelectionRange(itemText.length, itemText.length);
if (!itemPressed) {
if (!suggestionItemPressed) {
this.selectSuggestion(matchingItem, true);
this.open = false;
}
Expand All @@ -891,7 +892,7 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {
innerInput.setSelectionRange(this.value.length, this.value.length);
}

if (!itemPressed) {
if (!suggestionItemPressed) {
this.lastConfirmedValue = this.value;

if (this.FormSupport) {
Expand Down Expand Up @@ -949,9 +950,9 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {
// Restore the value.
this.value = this.typedInValue || this.valueBeforeItemPreview;

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

return;
Expand Down Expand Up @@ -1100,7 +1101,7 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {
];

this._shouldAutocomplete = !allowedEventTypes.includes(eventType) && !this.noTypeahead;
this.suggestionSelectionCanceled = false;
this.suggestionSelectionCancelled = false;

if (e instanceof InputEvent) {
// ---- Special cases of numeric Input ----
Expand Down Expand Up @@ -1292,14 +1293,19 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {

this.hasSuggestionItemSelected = true;

const valueOriginal = this.value;
const valueBeforeItemSelectionOriginal = this.valueBeforeItemSelection;
const lastConfirmedValueOriginal = this.lastConfirmedValue;
const performTextSelectionOriginal = this._performTextSelection;
const typedInValueOriginal = this.typedInValue;
const previousValueOriginal = this.previousValue;

if (fireInput) {
this.value = itemText;
this.valueBeforeItemSelection = itemText;
this.lastConfirmedValue = itemText;

this._performTextSelection = true;
this.hasSuggestionItemSelected = true;
this.value = itemText;

this.fireEvent(INPUT_EVENTS.CHANGE);

Expand All @@ -1313,9 +1319,29 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {
}

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

this.fireEvent<InputSuggestionItemSelectEventDetail>(INPUT_EVENTS.SUGGESTION_ITEM_SELECT, { item });
this.suggestionSelectionCancelled = false;

// Fire suggestion-item-select event after input change events for backward compatibility, but revert all input properties set before suggestion was prevented.
// For v2.0 this code will be reworked.
const isCancelledByUser = !this.fireEvent<InputSuggestionItemSelectEventDetail>(INPUT_EVENTS.SUGGESTION_ITEM_SELECT, { item }, true);

if (isCancelledByUser) {
this.Suggestions?._clearSelectedSuggestionAndAccInfo();
this.hasSuggestionItemSelected = false;
this.suggestionSelectionCancelled = true;

if (fireInput) {
// revert properties set during fireInput
if (itemText === this.value) { // If no chnages were made to the input value after suggestion-item-select was prevented - revert value to the original text
this.value = valueOriginal;
}
this.valueBeforeItemSelection = valueBeforeItemSelectionOriginal;
this.lastConfirmedValue = lastConfirmedValueOriginal;
this._performTextSelection = performTextSelectionOriginal;
this.typedInValue = typedInValueOriginal;
this.previousValue = previousValueOriginal;
}
}

this.isTyping = false;
this.openOnMobile = false;
Expand Down
5 changes: 5 additions & 0 deletions packages/main/src/features/InputSuggestions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,11 @@ class Suggestions {
this.component._isValueStateFocused = false;
}

_clearSelectedSuggestionAndAccInfo() {
this.accInfo = undefined;
this.selectedItemIndex = 0;
}

static get dependencies() {
return [
SuggestionItem,
Expand Down
16 changes: 16 additions & 0 deletions packages/main/test/pages/Input.html
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ <h3>Input with disabled autocomplete (type-ahead)</h3>
<ui5-li>Condensed</ui5-li>
</ui5-input>

<h3>Input with disabled autocomplete and preventable suggestion select</h3>
<ui5-input id="input-prevent-suggestion-select" show-suggestions no-typeahead="true">
<ui5-li>Cozy</ui5-li>
<ui5-li>Compact</ui5-li>
<ui5-li>Condensed</ui5-li>
</ui5-input>

<h3> 'change' event result</h3>
<ui5-input id="inputResult"></ui5-input>

Expand Down Expand Up @@ -752,6 +759,15 @@ <h3>Input - change event handling</h3>
});

document.getElementById("change-event-value").addEventListener("ui5-change", event => event.target.value = "");

const inputWithPreventableSelection = document.getElementById("input-prevent-suggestion-select");
inputWithPreventableSelection.addEventListener("ui5-suggestionItemSelect", (e) => {
const selectedItemText = e.detail.item.text || e.detail.item.textContent;
if(selectedItemText === "Cozy"){
e.preventDefault();
inputWithPreventableSelection.value = "test test";
}
});
</script>
</body>
</html>
64 changes: 61 additions & 3 deletions packages/main/test/specs/Input.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -735,8 +735,6 @@ describe("Input general interaction", () => {
});

it("Tests disabled autocomplete(type-ahead)", async () => {
let hasSelection;

const input = await browser.$("#input-disabled-autocomplete").shadow$("input");

await input.click();
Expand Down Expand Up @@ -1519,6 +1517,66 @@ describe("XSS tests for suggestions", () => {
});
});

describe("Prevent suggestion-item-select event", () => {
let input;
let SUGGESTION_TEXT;
const INPUT_ID_SELECTOR = "#input-prevent-suggestion-select";

beforeEach(async () => {
await browser.url(`test/pages/Input.html`);

input = await browser.$(INPUT_ID_SELECTOR);
});

it("User can prevent suggested-item-select on desired item", async () => {
SUGGESTION_TEXT = "Cozy";

await input.click();
await input.keys(SUGGESTION_TEXT.at(0));

const staticAreaItemClassName =
await browser.getStaticAreaItemClassName(INPUT_ID_SELECTOR);
const respPopover = await browser
.$(`.${staticAreaItemClassName}`)
.shadow$("ui5-responsive-popover");

// Select first suggestion item that has event prevent
const firstSuggestion = await respPopover
.$("ui5-list")
.$("ui5-li-suggestion-item");
await firstSuggestion.click();

assert.strictEqual(
await input.getProperty("value"),
"test test",
"Prevent suggestion-item-select event does not work"
);
});

it("Suggestion selection works as usual for items that do not match event prevent criterias defined by user", async () => {
SUGGESTION_TEXT = "Compact";

await input.click();
await input.keys(SUGGESTION_TEXT.at(0));

const staticAreaItemClassName =
await browser.getStaticAreaItemClassName(INPUT_ID_SELECTOR);
const respPopover = await browser
.$(`.${staticAreaItemClassName}`)
.shadow$("ui5-responsive-popover");

const secondSuggestion = await respPopover
.$("ui5-list")
.$$("ui5-li-suggestion-item")[1];
await secondSuggestion.click();

assert.strictEqual(
await input.getProperty("value"),
SUGGESTION_TEXT,
"Event suggestion-item-select works as expected for items without event prevention"
);
});
});

describe("Lazy loading", () => {
beforeEach(async () => {
Expand Down Expand Up @@ -1578,4 +1636,4 @@ describe("Lazy loading", () => {

assert.strictEqual(await respPopover.getProperty("opened"), true, "Picker should not be open");
});
});
});

0 comments on commit a265a65

Please sign in to comment.