From 68dc349cad49919afb7dc6218ff4dc6caff3c851 Mon Sep 17 00:00:00 2001 From: Konstantin Gogov Date: Fri, 8 Aug 2025 17:47:24 +0300 Subject: [PATCH 1/2] fix(ui5-select): prevent crash on ArrowUp/Down when value matches no option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If select's value property is set to a string that doesn’t match any option, nothing is selected (selectedIndex = -1). When you press ArrowUp or ArrowDown, _changeSelectedItem tries to use invalid indices, which causes it to access undefined and crash at runtime. Fixes: #12093 --- packages/main/cypress/specs/Select.cy.tsx | 46 +++++++++++++++++++++++ packages/main/src/Select.ts | 16 +++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/packages/main/cypress/specs/Select.cy.tsx b/packages/main/cypress/specs/Select.cy.tsx index d202c3503642..e9060a8fd555 100644 --- a/packages/main/cypress/specs/Select.cy.tsx +++ b/packages/main/cypress/specs/Select.cy.tsx @@ -1393,4 +1393,50 @@ describe("Select general interaction", () => { cy.get("[ui5-select]").should("have.prop", "value", "Third"); }); + + it("navigates with ArrowDown when initial value does not match any option", () => { + cy.mount( + + ); + + cy.get("#sel-down") + .should("have.attr", "value", "missing") + .find("[ui5-option][selected]") + .should("not.exist"); + + cy.get("#sel-down").realClick().realPress("ArrowDown"); + + cy.get("#sel-down") + .find("[ui5-option]") + .eq(0) + .should("have.attr", "selected"); + cy.get("#sel-down").should("have.prop", "value", "A"); + }); + + it("navigates with ArrowUp when initial value does not match any option", () => { + cy.mount( + + ); + + cy.get("#sel-up") + .should("have.attr", "value", "missing") + .find("[ui5-option][selected]") + .should("not.exist"); + + cy.get("#sel-up").realClick().realPress("ArrowUp"); + + cy.get("#sel-up") + .find("[ui5-option]") + .eq(2) + .should("have.attr", "selected"); + cy.get("#sel-up").should("have.prop", "value", "C"); + }); }); \ No newline at end of file diff --git a/packages/main/src/Select.ts b/packages/main/src/Select.ts index 1ab1dbc635a9..52b5608b260d 100644 --- a/packages/main/src/Select.ts +++ b/packages/main/src/Select.ts @@ -768,6 +768,16 @@ class Select extends UI5Element implements IFormInputElement { _changeSelectedItem(oldIndex: number, newIndex: number) { const options: Array = this.options; + // Normalize: first navigation with Up when nothing selected -> last item + if (oldIndex === -1 && newIndex < 0 && options.length) { + newIndex = options.length - 1; + } + + // Abort on invalid target + if (newIndex < 0 || newIndex >= options.length) { + return; + } + const previousOption = options[oldIndex]; const nextOption = options[newIndex]; @@ -775,8 +785,10 @@ class Select extends UI5Element implements IFormInputElement { return; } - previousOption.selected = false; - previousOption.focused = false; + if (previousOption) { + previousOption.selected = false; + previousOption.focused = false; + } nextOption.selected = true; nextOption.focused = true; From c6ce6f2e09b4db7966a4232f07c1b6da8343e8c3 Mon Sep 17 00:00:00 2001 From: Konstantin Gogov Date: Fri, 15 Aug 2025 17:06:17 +0300 Subject: [PATCH 2/2] chore: update Select component tests --- packages/main/cypress/specs/Select.cy.tsx | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/main/cypress/specs/Select.cy.tsx b/packages/main/cypress/specs/Select.cy.tsx index e9060a8fd555..cb0f25ff567e 100644 --- a/packages/main/cypress/specs/Select.cy.tsx +++ b/packages/main/cypress/specs/Select.cy.tsx @@ -1396,47 +1396,47 @@ describe("Select general interaction", () => { it("navigates with ArrowDown when initial value does not match any option", () => { cy.mount( - ); - cy.get("#sel-down") - .should("have.attr", "value", "missing") + cy.get("[ui5-select]") + .should("have.prop", "value", "missing") .find("[ui5-option][selected]") .should("not.exist"); - cy.get("#sel-down").realClick().realPress("ArrowDown"); + cy.get("[ui5-select]").realClick().realPress("ArrowDown"); - cy.get("#sel-down") + cy.get("[ui5-select]") .find("[ui5-option]") .eq(0) .should("have.attr", "selected"); - cy.get("#sel-down").should("have.prop", "value", "A"); + cy.get("[ui5-select]").should("have.prop", "value", "A"); }); it("navigates with ArrowUp when initial value does not match any option", () => { cy.mount( - ); - cy.get("#sel-up") - .should("have.attr", "value", "missing") + cy.get("[ui5-select]") + .should("have.prop", "value", "missing") .find("[ui5-option][selected]") .should("not.exist"); - cy.get("#sel-up").realClick().realPress("ArrowUp"); + cy.get("[ui5-select]").realClick().realPress("ArrowUp"); - cy.get("#sel-up") + cy.get("[ui5-select]") .find("[ui5-option]") .eq(2) .should("have.attr", "selected"); - cy.get("#sel-up").should("have.prop", "value", "C"); + cy.get("[ui5-select]").should("have.prop", "value", "C"); }); }); \ No newline at end of file