From 71d3754e5799d48c3039e86b97b9331da2490977 Mon Sep 17 00:00:00 2001 From: Artem Mozgovoi Date: Wed, 26 Nov 2025 16:44:44 +0100 Subject: [PATCH] feat(ComboBox): implement noTypeahead behavior to skip auto-selection during filtering --- packages/main/cypress/specs/ComboBox.cy.tsx | 152 ++++++++++++++++++++ packages/main/src/ComboBox.ts | 7 +- 2 files changed, 158 insertions(+), 1 deletion(-) diff --git a/packages/main/cypress/specs/ComboBox.cy.tsx b/packages/main/cypress/specs/ComboBox.cy.tsx index b1a2fa6b5bff..fbb1ae2281ae 100644 --- a/packages/main/cypress/specs/ComboBox.cy.tsx +++ b/packages/main/cypress/specs/ComboBox.cy.tsx @@ -596,6 +596,158 @@ describe("Keyboard navigation", () => { cy.get("@input").should("have.value", "b"); }); + + it("should NOT auto-select item when noTypeahead is enabled and filtering", () => { + cy.mount( + + + + + , + ); + + cy.get("[ui5-combobox]") + .as("combo") + .invoke("on", "ui5-selection-change", cy.spy().as("selectionChangeSpy")); + + cy.get("@combo").shadow().find("input").as("input"); + + // Type full item text - should NOT auto-select when noTypeahead is true + cy.get("@input").realClick(); + cy.get("@input").realType("Bulgaria"); + + // Verify no selection was made automatically + cy.get("@selectionChangeSpy").should("not.have.been.called"); + cy.get("@combo") + .find("[ui5-cb-item]") + .eq(1) + .should("not.have.prop", "selected", true); + }); + + it("should allow explicit selection via click when noTypeahead is enabled", () => { + cy.mount( + + + + + , + ); + + cy.get("[ui5-combobox]") + .as("combo") + .invoke("on", "ui5-selection-change", cy.spy().as("selectionChangeSpy")); + + cy.get("@combo").shadow().find("input").as("input"); + + // Type to filter, then click item - should select + cy.get("@input").realClick(); + cy.get("@input").realType("Bulg"); + + cy.get("@combo").find("[ui5-cb-item]").eq(1).realClick(); + + // Verify selection was made via click + cy.get("@selectionChangeSpy").should("have.been.calledOnce"); + cy.get("@selectionChangeSpy").should( + "have.been.calledWithMatch", + Cypress.sinon.match((event) => { + return event.detail.item.text === "Bulgaria"; + }), + ); + cy.get("@combo").should("have.prop", "value", "Bulgaria"); + }); + + it("should allow explicit selection via Enter key when noTypeahead is enabled", () => { + cy.mount( + + + + + , + ); + + cy.get("[ui5-combobox]") + .as("combo") + .invoke("on", "ui5-selection-change", cy.spy().as("selectionChangeSpy")); + + cy.get("@combo").shadow().find("input").as("input"); + + // Type to filter items - this opens the picker + cy.get("@input").realClick(); + cy.get("@input").realType("Bul"); + + // No auto-selection should have happened yet + cy.get("@selectionChangeSpy").should("not.have.been.called"); + + // Navigate to the first filtered item with arrow down + cy.get("@input").realPress("ArrowDown"); + + // Selection should occur via keyboard navigation + cy.get("@selectionChangeSpy").should("have.been.calledOnce"); + + // Now press Enter to confirm and close picker + cy.get("@input").realPress("Enter"); + cy.get("@combo").should("have.prop", "value", "Bulgaria"); + }); + + it("should allow explicit selection via keyboard navigation when noTypeahead is enabled", () => { + cy.mount( + + + + + , + ); + + cy.get("[ui5-combobox]") + .as("combo") + .invoke("on", "ui5-selection-change", cy.spy().as("selectionChangeSpy")); + + cy.get("@combo").shadow().find("input").as("input"); + + // Open picker and use keyboard navigation to select + cy.get("@combo").shadow().find(".inputIcon").realClick(); + + // Navigate with arrow keys + cy.get("@input").realPress("ArrowDown"); + + // Selection should occur via keyboard navigation + cy.get("@selectionChangeSpy").should("have.been.calledOnce"); + cy.get("@selectionChangeSpy").should( + "have.been.calledWithMatch", + Cypress.sinon.match((event) => { + return event.detail.item.text === "Argentina"; + }), + ); + cy.get("@combo").should("have.prop", "value", "Argentina"); + }); + + it("should auto-select when noTypeahead is false (default behavior)", () => { + cy.mount( + + + + + , + ); + + cy.get("[ui5-combobox]") + .as("combo") + .invoke("on", "ui5-selection-change", cy.spy().as("selectionChangeSpy")); + + cy.get("@combo").shadow().find("input").as("input"); + + // Type full item text - should auto-select when noTypeahead is false + cy.get("@input").realClick(); + cy.get("@input").realType("Bulgaria"); + + // Verify selection was made automatically + cy.get("@selectionChangeSpy").should("have.been.calledOnce"); + cy.get("@combo") + .find("[ui5-cb-item]") + .eq(1) + .should("have.prop", "selected", true); + cy.get("@combo").should("have.prop", "value", "Bulgaria"); + }); }); describe("Grouping", () => { diff --git a/packages/main/src/ComboBox.ts b/packages/main/src/ComboBox.ts index 22fb72924d70..15f55423da39 100644 --- a/packages/main/src/ComboBox.ts +++ b/packages/main/src/ComboBox.ts @@ -1188,8 +1188,13 @@ class ComboBox extends UI5Element implements IFormInputElement { } }); + // Skip auto-selection during filtering when noTypeahead is enabled + // Allow selection only through explicit user interaction (clicks, Enter key, keyboard navigation) + const isActiveFiltering = this.focused && this.value && !this._selectionPerformed && !this._isKeyNavigation; + const shouldSkipAutoSelection = this.noTypeahead && isActiveFiltering; + this._filteredItems.forEach(item => { - if (!shouldSelectionBeCleared && !itemToBeSelected) { + if (!shouldSelectionBeCleared && !itemToBeSelected && !shouldSkipAutoSelection) { itemToBeSelected = ((!item.isGroupItem && (item.text === this.value)) ? item : item?.items?.find(i => i.text === this.value)); } });