diff --git a/packages/main/cypress/specs/List.cy.tsx b/packages/main/cypress/specs/List.cy.tsx index eb6dfe355402..3fde23adcca5 100644 --- a/packages/main/cypress/specs/List.cy.tsx +++ b/packages/main/cypress/specs/List.cy.tsx @@ -153,10 +153,10 @@ describe("List Tests", () => { HP Monitor 24 ); - + cy.get("[ui5-list]") .as("list"); - + cy.get("@list").invoke('prop', 'accessibilityAttributes', { growingButton: { name: "Load more products from catalog" @@ -167,7 +167,7 @@ describe("List Tests", () => { .shadow() .find("[id$='growing-btn']") .should("have.attr", "aria-label", "Load more products from catalog"); - + cy.get("@list") .shadow() .find("[id$='growing-btn']") @@ -182,7 +182,7 @@ describe("List Tests", () => { Product 3 ); - + cy.get("[ui5-list]") .as("list"); @@ -652,22 +652,22 @@ describe("List Tests", () => { ); - + cy.get("[ui5-list]").then(($list) => { $list[0].addEventListener("ui5-item-click", cy.stub().as("itemClickStub")); $list[0].addEventListener("ui5-selection-change", cy.stub().as("selectionChangeStub")); }); - + cy.get("[ui5-li]").first().click(); - + cy.get("@itemClickStub").should("have.been.calledOnce"); cy.get("@selectionChangeStub").should("have.been.calledOnce"); - + cy.get("[ui5-li]").eq(1) .shadow() .find("ui5-radio-button") .click(); - + cy.get("@itemClickStub").should("have.been.calledOnce"); cy.get("@selectionChangeStub").should("have.been.calledTwice"); cy.get("@selectionChangeStub").should("have.been.calledWith", Cypress.sinon.match.has("detail", Cypress.sinon.match.has("selectionComponentPressed", true))); @@ -686,14 +686,14 @@ describe("List Tests", () => { ); - + cy.get("[ui5-list]").then(($list) => { $list[0].addEventListener("ui5-item-click", cy.stub().as("itemClickStub")); $list[0].addEventListener("ui5-selection-change", cy.stub().as("selectionChangeStub")); }); - + cy.get("[ui5-li]").first().click(); - + cy.get("@itemClickStub").should("have.been.calledOnce"); cy.get("@selectionChangeStub").should("have.been.calledOnce"); }); @@ -710,13 +710,13 @@ describe("List Tests", () => { ); - + cy.get("[ui5-list]").then(($list) => { $list[0].addEventListener("ui5-selection-change", cy.stub().as("selectionChangeStub")); }); - + cy.get("[ui5-li]").first().click(); - + cy.get("@selectionChangeStub").should("have.been.calledOnce"); cy.get("@selectionChangeStub").should("have.been.calledWith", Cypress.sinon.match.has("detail", Cypress.sinon.match.has("previouslySelectedItems"))); cy.get("[ui5-li]").eq(1).should("exist"); @@ -730,7 +730,7 @@ describe("List Tests", () => { China ); - + cy.get("[ui5-list]").then(($list) => { const stub = cy.stub().as("selectionChangeStub"); stub.callsFake((event) => { @@ -738,11 +738,11 @@ describe("List Tests", () => { }); $list[0].addEventListener("ui5-selection-change", stub); }); - + cy.get("[ui5-li]").eq(2).should("have.attr", "selected"); - + cy.get("[ui5-li]").first().click(); - + cy.get("@selectionChangeStub").should("have.been.calledOnce"); cy.get("[ui5-li]").first().should("not.have.attr", "selected"); cy.get("[ui5-li]").eq(2).should("have.attr", "selected"); @@ -756,7 +756,7 @@ describe("List Tests", () => { China ); - + cy.get("[ui5-list]").then(($list) => { const stub = cy.stub().as("selectionChangeStub"); stub.callsFake((event) => { @@ -764,13 +764,13 @@ describe("List Tests", () => { }); $list[0].addEventListener("ui5-selection-change", stub); }); - + cy.get("[ui5-li]").first().should("not.have.attr", "selected"); cy.get("[ui5-li]").eq(1).should("have.attr", "selected"); cy.get("[ui5-li]").eq(2).should("have.attr", "selected"); - + cy.get("[ui5-li]").first().click(); - + cy.get("@selectionChangeStub").should("have.been.calledOnce"); cy.get("[ui5-li]").first().should("not.have.attr", "selected"); cy.get("[ui5-li]").eq(1).should("have.attr", "selected"); @@ -1129,35 +1129,35 @@ describe("List Tests", () => { ); - + cy.get("[ui5-list]").then(($list) => { $list[0].addEventListener("ui5-item-delete", cy.stub().as("itemDeleteStub")); }); - + cy.get("[ui5-list]") .find("ui5-li") .first() .click(); - + cy.get("[ui5-list]") .find("ui5-li") .first() .should("not.have.attr", "selected"); - + cy.get("[ui5-list]") .find("ui5-li") .first() .shadow() .find("ui5-button") .should("exist"); - + cy.get("[ui5-list]") .find("ui5-li") .first() .shadow() .find("ui5-button") .click(); - + cy.get("@itemDeleteStub").should("have.been.calledOnce"); cy.get("@itemDeleteStub").should("have.been.calledWith", Cypress.sinon.match.has("detail", Cypress.sinon.match.has("item"))); }); @@ -1180,27 +1180,97 @@ describe("List Tests", () => { ); - + cy.get("[ui5-list]").then(($list) => { $list[0].addEventListener("ui5-item-delete", cy.stub().as("itemDeleteStub")); }); - + cy.get("[ui5-list]") .find("ui5-li") .first() .click(); - + cy.get("[ui5-list]") .find("ui5-li") .first() .should("not.have.attr", "selected"); - + cy.realPress("Delete"); - + cy.get("@itemDeleteStub").should("have.been.calledOnce"); cy.get("@itemDeleteStub").should("have.been.calledWith", Cypress.sinon.match.has("detail", Cypress.sinon.match.has("item"))); }); + it("selectionMode: delete. Tab key moves focus to delete button", () => { + cy.mount( +
+ + + Laptop HP + Laptop Lenovo + + +
+ ); + + cy.get("[ui5-list]").then(($list) => { + $list[0].addEventListener("ui5-item-delete", cy.stub().as("itemDeleteStub")); + }); + + // Click first item to focus it + cy.get("[ui5-li]").first().click(); + cy.get("[ui5-li]").first().should("be.focused"); + + // Tab should move focus to the delete button inside the first item + cy.realPress("Tab"); + cy.get("[ui5-li]") + .first() + .shadow() + .find("[ui5-button]") + .should("be.focused"); + + // Enter on the focused delete button should trigger deletion + cy.realPress("Enter"); + cy.get("@itemDeleteStub").should("have.been.calledOnce"); + + // Tab again from delete button should move focus out of the item + cy.get("[ui5-li]").first().click(); + cy.realPress("Tab"); + cy.get("[ui5-li]") + .first() + .shadow() + .find("[ui5-button]") + .should("be.focused"); + + cy.realPress("Tab"); + cy.get("button").last().should("be.focused"); + }); + + it("selectionMode: delete. F2 toggles focus to delete button", () => { + cy.mount( + + Laptop HP + Laptop Lenovo + + ); + + // Click first item to focus it + cy.get("[ui5-li]").first().click(); + cy.get("[ui5-li]").first().should("be.focused"); + + // F2 should move focus to the delete button + cy.realPress("F2"); + cy.get("[ui5-li]") + .first() + .shadow() + .find("[ui5-button]") + .should("be.focused"); + + // F2 again should return focus to the list item + cy.realPress("F2"); + cy.get("[ui5-li]").first().should("be.focused"); + }); + it("item size and classes, when an item has both text and description", () => { const EXPECTED_HEIGHT = 80; @@ -1714,16 +1784,16 @@ describe("List Tests", () => { ); - + cy.get("[ui5-li]").first().then(($item) => { $item[0].addEventListener("ui5-detail-click", cy.stub().as("detailClickStub")); }); - + cy.get("[ui5-li]").first() .shadow() .find(".ui5-li-detailbtn") .click(); - + cy.get("@detailClickStub").should("have.been.calledOnce"); }); @@ -1870,12 +1940,12 @@ describe("List Tests", () => { ); - + const NEW_TEXT = "updated"; - + cy.get("[ui5-li]").first() .should("have.prop", "innerHTML", ""); - + cy.get("[ui5-li]").first() .shadow() .find("slot") @@ -1883,7 +1953,7 @@ describe("List Tests", () => { const assignedNodes = ($slot[0] as any).assignedNodes(); expect(assignedNodes.length).to.equal(0); }); - + cy.get("[ui5-button]").then(($btn) => { const stub = cy.stub().as("buttonClickStub"); stub.callsFake(() => { @@ -1894,13 +1964,13 @@ describe("List Tests", () => { }); $btn[0].addEventListener("click", stub); }); - + cy.get("[ui5-button]").click(); - + cy.get("@buttonClickStub").should("have.been.calledOnce"); cy.get("[ui5-li]").first() .should("have.prop", "innerHTML", NEW_TEXT); - + cy.get("[ui5-li]").first() .shadow() .find("slot") @@ -1920,7 +1990,7 @@ describe("List Tests", () => { ); - + cy.get("[ui5-list]").then(($list) => { const stub = cy.stub().as("itemClickStub"); stub.callsFake((event) => { @@ -1928,9 +1998,9 @@ describe("List Tests", () => { }); $list[0].addEventListener("ui5-item-click", stub); }); - + cy.get("[ui5-li]").first().click(); - + cy.get("@itemClickStub").should("have.been.calledOnce"); cy.get("[ui5-li]").first().should("not.have.attr", "selected"); }); @@ -2113,18 +2183,18 @@ describe("List Tests", () => { ); - + cy.get("[ui5-select]").then(($select) => { const listItem = $select.closest("ui5-li-custom")[0]; if (listItem) { listItem.addEventListener("ui5-item-close", cy.stub().as("itemCloseStub")); } }); - + cy.get("[ui5-select]").click(); - + cy.realPress("Escape"); - + cy.get("@itemCloseStub").should("not.have.been.called"); }); @@ -2487,14 +2557,14 @@ describe("List keyboard drag and drop tests", () => { cy.get("[ui5-list]").then(($list) => { const list = $list[0]; - + list.addEventListener("keydown", (e) => { if (e.ctrlKey) { const focusedItem = document.activeElement as HTMLElement; if (!focusedItem || !focusedItem.matches("[ui5-li]")) return; - + let targetItem = null; - + switch (e.key) { case "ArrowRight": case "ArrowDown": @@ -2554,14 +2624,14 @@ describe("List keyboard drag and drop tests", () => { cy.get("[ui5-list]").then(($list) => { const list = $list[0]; - + list.addEventListener("keydown", (e) => { if (e.ctrlKey) { const focusedItem = document.activeElement as HTMLElement; if (!focusedItem || !focusedItem.matches("[ui5-li]")) return; - + let targetItem = null; - + switch (e.key) { case "ArrowUp": targetItem = focusedItem.previousElementSibling as HTMLElement; @@ -2625,10 +2695,10 @@ describe("List sticky header", () => { cy.get("@header") .then(($headerBefore) => { const headerTopBefore = $headerBefore[0].getBoundingClientRect().top; - + cy.get("@container") .scrollTo(0, 50); - + cy.get("@header") .should(($headerAfter) => { const headerTopAfter = $headerAfter[0].getBoundingClientRect().top; @@ -2661,10 +2731,10 @@ describe("List sticky header", () => { cy.get("@header") .then(($headerBefore) => { const headerTopBefore = $headerBefore[0].getBoundingClientRect().top; - + cy.get("@container") .scrollTo(0, 50); - + cy.get("@header") .should(($headerAfter) => { const headerTopAfter = $headerAfter[0].getBoundingClientRect().top; @@ -2672,4 +2742,4 @@ describe("List sticky header", () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/packages/main/src/ListItemTemplate.tsx b/packages/main/src/ListItemTemplate.tsx index 6d747378a9dc..2a289da505fe 100644 --- a/packages/main/src/ListItemTemplate.tsx +++ b/packages/main/src/ListItemTemplate.tsx @@ -154,8 +154,6 @@ function selectionElement(this: ListItem) { ) : (