diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5fc2c32e7..af2d6b5514 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,17 @@
+## [123.2.0](https://github.com/Sage/carbon/compare/v123.1.0...v123.2.0) (2023-10-30)
+
+
+### Features
+
+* **confirm:** make data tag props available on cancel and confirm buttons ([2c69101](https://github.com/Sage/carbon/commit/2c691011688c5186a7d20f8af7e6acd768e5a0e8)), closes [#6374](https://github.com/Sage/carbon/issues/6374)
+
+## [123.1.0](https://github.com/Sage/carbon/compare/v123.0.1...v123.1.0) (2023-10-27)
+
+
+### Features
+
+* **action-popover:** disabled items can no longer be focused ([208148b](https://github.com/Sage/carbon/commit/208148b2d5009db64fa3ce4d8ad86250c860c523))
+
### [123.0.1](https://github.com/Sage/carbon/compare/v123.0.0...v123.0.1) (2023-10-27)
diff --git a/cypress/components/flat-table/flat-table.cy.tsx b/cypress/components/flat-table/flat-table.cy.tsx
index ace2d473ad..b729fca4f8 100644
--- a/cypress/components/flat-table/flat-table.cy.tsx
+++ b/cypress/components/flat-table/flat-table.cy.tsx
@@ -526,7 +526,7 @@ context("Tests for Flat Table component", () => {
it.skip("should render Flat Table with multiple sticky row headers, stickyAlignment set to right", () => {
cy.viewport(700, 700);
- CypressMountWithProviders();
+ CypressMountWithProviders();
flatTableBodyRowByPosition(1)
.find("td")
@@ -2932,7 +2932,7 @@ context("Tests for Flat Table component", () => {
it.skip("should render Flat Table with multiple sticky row headers for accessibility tests", () => {
cy.viewport(700, 700);
- CypressMountWithProviders();
+ CypressMountWithProviders();
cy.checkAccessibility();
});
diff --git a/cypress/components/grouped-character/grouped-character.cy.tsx b/cypress/components/grouped-character/grouped-character.cy.tsx
index efbd8f72b3..d8febd130f 100644
--- a/cypress/components/grouped-character/grouped-character.cy.tsx
+++ b/cypress/components/grouped-character/grouped-character.cy.tsx
@@ -148,7 +148,7 @@ context("Tests for GroupedCharacter component", () => {
["right", "end"],
["left", "start"],
] as [GroupedCharacterProps["labelAlign"], string][])(
- "should use %s as labelAligment and render it with %s as css properties",
+ "should use %s as labelAlignment and render it with %s as css properties",
(alignment, cssProp) => {
CypressMountWithProviders(
diff --git a/cypress/components/menu/menu.cy.tsx b/cypress/components/menu/menu.cy.tsx
index 519cf0c60d..dd9854e6ca 100644
--- a/cypress/components/menu/menu.cy.tsx
+++ b/cypress/components/menu/menu.cy.tsx
@@ -333,12 +333,12 @@ context("Testing Menu component", () => {
searchDefaultInput().tab();
searchCrossIcon().parent().should("have.focus");
- const bouding = (element: JQuery) => {
+ const bounding = (element: JQuery) => {
return element[0].getBoundingClientRect();
};
searchCrossIcon()
- .then(($el) => bouding($el))
+ .then(($el) => bounding($el))
.as("position");
cy.get("@position")
@@ -627,7 +627,7 @@ context("Testing Menu component", () => {
"center",
"flex-start",
"flex-end",
- ])("should verify Menu alignItmes is %s", (alignment) => {
+ ])("should verify Menu alignItems is %s", (alignment) => {
CypressMountWithProviders();
menu().should("have.css", "align-items", alignment);
@@ -1501,7 +1501,7 @@ context("Testing Menu component", () => {
"text-top",
"top",
])(
- "should pass accessibility tests for Menu when alignItmes is %s",
+ "should pass accessibility tests for Menu when alignItems is %s",
(alignment) => {
CypressMountWithProviders();
diff --git a/cypress/components/multi-action-button/multi-action-button.cy.tsx b/cypress/components/multi-action-button/multi-action-button.cy.tsx
index 94e8225019..0ec9430e69 100644
--- a/cypress/components/multi-action-button/multi-action-button.cy.tsx
+++ b/cypress/components/multi-action-button/multi-action-button.cy.tsx
@@ -229,7 +229,7 @@ context("Tests for MultiActionButton component", () => {
});
});
- describe("user interactions with MultiActionutton", () => {
+ describe("user interactions with MultiActionButton", () => {
describe("pressing ArrowUp while MultiActionButton is open", () => {
it("should move focus to previous child button and should not loop to last button when first is focused", () => {
CypressMountWithProviders(
@@ -448,7 +448,7 @@ context("Tests for MultiActionButton component", () => {
});
});
- describe("user interactions with MultiActionutton when wrapping the child buttons in a custom component", () => {
+ describe("user interactions with MultiActionButton when wrapping the child buttons in a custom component", () => {
describe("pressing ArrowUp while MultiActionButton is open", () => {
it("should move focus to previous child button and should not loop to last button when first is focused", () => {
CypressMountWithProviders(
diff --git a/cypress/components/number/number.cy.tsx b/cypress/components/number/number.cy.tsx
index c071bb7c9b..33f472cd16 100644
--- a/cypress/components/number/number.cy.tsx
+++ b/cypress/components/number/number.cy.tsx
@@ -178,7 +178,7 @@ context("Tests for Number component", () => {
["right", "end"],
["left", "start"],
] as [NumberProps["labelAlign"], string][])(
- "should use %s as labelAligment and render it with flex-%s as css properties",
+ "should use %s as labelAlignment and render it with flex-%s as css properties",
(alignment, cssProp) => {
CypressMountWithProviders(
diff --git a/cypress/components/pager/pager.cy.tsx b/cypress/components/pager/pager.cy.tsx
index a41fa68ba7..47de7e6729 100644
--- a/cypress/components/pager/pager.cy.tsx
+++ b/cypress/components/pager/pager.cy.tsx
@@ -323,7 +323,7 @@ context("Test for Pager component", () => {
});
});
- describe("check funtionality for Pager component", () => {
+ describe("check functionality for Pager component", () => {
it.each([-1, -10, -100, ...testData])(
"should set totalRecords out of scope to %s",
(totalRecords) => {
@@ -379,14 +379,14 @@ context("Test for Pager component", () => {
viewportWidth,
showItemsAssertion,
firstAndLastArrowsAssertion,
- totalRecordsAssetion
+ totalRecordsAssertion
) => {
cy.viewport(viewportWidth, 768);
CypressMountWithProviders();
showLabelBefore().should(showItemsAssertion);
- pagerSummary().should(totalRecordsAssetion);
+ pagerSummary().should(totalRecordsAssertion);
firstArrow().should(firstAndLastArrowsAssertion);
lastArrow().should(firstAndLastArrowsAssertion);
nextArrow().should("be.visible");
diff --git a/cypress/components/password/password.cy.tsx b/cypress/components/password/password.cy.tsx
index 0bd2de5821..9b5a8a0b08 100644
--- a/cypress/components/password/password.cy.tsx
+++ b/cypress/components/password/password.cy.tsx
@@ -305,7 +305,7 @@ context("Tests for Password component", () => {
["right", "end"],
["left", "start"],
] as [PasswordProps["labelAlign"], string][])(
- "should use %s as labelAligment and render it with %s as css properties",
+ "should use %s as labelAlignment and render it with %s as css properties",
(alignment, cssProp) => {
CypressMountWithProviders(
diff --git a/cypress/components/switch/switch.cy.tsx b/cypress/components/switch/switch.cy.tsx
index 2934318169..5ce15ffbfd 100644
--- a/cypress/components/switch/switch.cy.tsx
+++ b/cypress/components/switch/switch.cy.tsx
@@ -314,7 +314,7 @@ context("Testing Switch component", () => {
["warning", VALIDATION.WARNING],
["info", VALIDATION.INFO],
])(
- "verify Switch component is verifyed with appropriate border color for validations",
+ "verify Switch component is verified with appropriate border color for validations",
(type, validation) => {
CypressMountWithProviders();
diff --git a/cypress/components/tooltip/tooltip.cy.tsx b/cypress/components/tooltip/tooltip.cy.tsx
index 8cea855472..b4c876db14 100644
--- a/cypress/components/tooltip/tooltip.cy.tsx
+++ b/cypress/components/tooltip/tooltip.cy.tsx
@@ -208,14 +208,14 @@ context("Tests for Tooltip component", () => {
});
describe("Accessibility tests for Tooltip component", () => {
- it("should pass accessibilty tests for Tooltip Default story", () => {
+ it("should pass accessibility tests for Tooltip Default story", () => {
CypressMountWithProviders();
getDataElementByValue("main-text").click();
cy.checkAccessibility();
});
- it("should pass accessibilty tests for Tooltip Controlled story", () => {
+ it("should pass accessibility tests for Tooltip Controlled story", () => {
CypressMountWithProviders();
getDataElementByValue("main-text").eq(0).click();
@@ -228,7 +228,7 @@ context("Tests for Tooltip component", () => {
["left", 2],
["right", 3],
])(
- "should pass accessibilty tests for Tooltip Positioning story %s position",
+ "should pass accessibility tests for Tooltip Positioning story %s position",
(position, button) => {
CypressMountWithProviders();
@@ -237,20 +237,20 @@ context("Tests for Tooltip component", () => {
}
);
- it("should pass accessibilty tests for Tooltip FlipBehviourOverrides story", () => {
- CypressMountWithProviders();
+ it("should pass accessibility tests for Tooltip FlipBehaviourOverrides story", () => {
+ CypressMountWithProviders();
cy.checkAccessibility();
});
- it("should pass accessibilty tests for Tooltip LargeTooltip story", () => {
+ it("should pass accessibility tests for Tooltip LargeTooltip story", () => {
CypressMountWithProviders();
getDataElementByValue("main-text").click();
cy.checkAccessibility();
});
- it("should pass accessibilty tests for Tooltip Types story", () => {
+ it("should pass accessibility tests for Tooltip Types story", () => {
CypressMountWithProviders();
getDataElementByValue("main-text").eq(1).click();
@@ -259,7 +259,7 @@ context("Tests for Tooltip component", () => {
cy.checkAccessibility();
});
- it("should pass accessibilty tests for Tooltip ColorOverrides story", () => {
+ it("should pass accessibility tests for Tooltip ColorOverrides story", () => {
CypressMountWithProviders();
getDataElementByValue("main-text").click();
diff --git a/cypress/components/vertical-menu/vertical-menu.cy.tsx b/cypress/components/vertical-menu/vertical-menu.cy.tsx
index 373ea6e42c..b756ae3961 100644
--- a/cypress/components/vertical-menu/vertical-menu.cy.tsx
+++ b/cypress/components/vertical-menu/vertical-menu.cy.tsx
@@ -487,20 +487,20 @@ context("Testing Vertical Menu component", () => {
});
describe("should check the accessibility tests", () => {
- it("should check accessiblity for verticalMenuComponent", () => {
+ it("should check accessibility for verticalMenuComponent", () => {
CypressMountWithProviders();
cy.checkAccessibility();
});
- it("should check accessiblity for verticalMenuComponent open", () => {
+ it("should check accessibility for verticalMenuComponent open", () => {
CypressMountWithProviders();
verticalMenuItem().tab().tab().click();
cy.checkAccessibility();
});
- it("should check accessiblity for verticalMenuComponent Active", () => {
+ it("should check accessibility for verticalMenuComponent Active", () => {
CypressMountWithProviders(
!isOpen} />
);
@@ -508,32 +508,32 @@ context("Testing Vertical Menu component", () => {
cy.checkAccessibility();
});
- it("should check accessiblity for verticalMenuComponent Adornment", () => {
+ it("should check accessibility for verticalMenuComponent Adornment", () => {
CypressMountWithProviders();
cy.checkAccessibility();
});
- it("should check accessiblity for verticalMenuComponent CustomItemHeight", () => {
+ it("should check accessibility for verticalMenuComponent CustomItemHeight", () => {
CypressMountWithProviders();
cy.checkAccessibility();
});
- it("should check accessiblity for verticalMenuComponent CustomItemPadding", () => {
+ it("should check accessibility for verticalMenuComponent CustomItemPadding", () => {
CypressMountWithProviders();
cy.checkAccessibility();
});
- it("should check accessiblity for verticalMenuComponent FullScreen", () => {
+ it("should check accessibility for verticalMenuComponent FullScreen", () => {
cy.viewport(320, 599);
CypressMountWithProviders();
cy.checkAccessibility();
});
- it("should check accessiblity for verticalMenuComponent FullScreen open", () => {
+ it("should check accessibility for verticalMenuComponent FullScreen open", () => {
cy.viewport(320, 599);
CypressMountWithProviders();
diff --git a/package-lock.json b/package-lock.json
index b88d14079b..faf87f1a7c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,16 +1,15 @@
{
"name": "carbon-react",
- "version": "123.0.1",
+ "version": "123.2.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "carbon-react",
- "version": "123.0.1",
+ "version": "123.2.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
- "@actions/github": "^5.1.1",
"@axe-core/playwright": "^4.7.3",
"@floating-ui/dom": "^1.2.7",
"@floating-ui/react-dom": "^1.3.0",
@@ -173,21 +172,11 @@
"uuid": "^8.3.2"
}
},
- "node_modules/@actions/github": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
- "integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
- "dependencies": {
- "@actions/http-client": "^2.0.1",
- "@octokit/core": "^3.6.0",
- "@octokit/plugin-paginate-rest": "^2.17.0",
- "@octokit/plugin-rest-endpoint-methods": "^5.13.0"
- }
- },
"node_modules/@actions/http-client": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz",
"integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==",
+ "dev": true,
"dependencies": {
"tunnel": "^0.0.6"
}
@@ -14080,32 +14069,35 @@
"dev": true
},
"node_modules/browserify-sign": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
- "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz",
+ "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==",
"dev": true,
"dependencies": {
- "bn.js": "^5.1.1",
- "browserify-rsa": "^4.0.1",
+ "bn.js": "^5.2.1",
+ "browserify-rsa": "^4.1.0",
"create-hash": "^1.2.0",
"create-hmac": "^1.1.7",
- "elliptic": "^6.5.3",
+ "elliptic": "^6.5.4",
"inherits": "^2.0.4",
- "parse-asn1": "^5.1.5",
- "readable-stream": "^3.6.0",
- "safe-buffer": "^5.2.0"
+ "parse-asn1": "^5.1.6",
+ "readable-stream": "^3.6.2",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 4"
}
},
"node_modules/browserify-sign/node_modules/bn.js": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
- "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==",
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
"dev": true
},
"node_modules/browserify-sign/node_modules/readable-stream": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
- "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"dependencies": {
"inherits": "^2.0.3",
@@ -16425,9 +16417,9 @@
}
},
"node_modules/crypto-js": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
- "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
},
"node_modules/crypto-random-string": {
"version": "2.0.0",
@@ -38451,6 +38443,7 @@
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
+ "dev": true,
"engines": {
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
@@ -40310,21 +40303,11 @@
"uuid": "^8.3.2"
}
},
- "@actions/github": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
- "integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
- "requires": {
- "@actions/http-client": "^2.0.1",
- "@octokit/core": "^3.6.0",
- "@octokit/plugin-paginate-rest": "^2.17.0",
- "@octokit/plugin-rest-endpoint-methods": "^5.13.0"
- }
- },
"@actions/http-client": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz",
"integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==",
+ "dev": true,
"requires": {
"tunnel": "^0.0.6"
}
@@ -50772,32 +50755,32 @@
}
},
"browserify-sign": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
- "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz",
+ "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==",
"dev": true,
"requires": {
- "bn.js": "^5.1.1",
- "browserify-rsa": "^4.0.1",
+ "bn.js": "^5.2.1",
+ "browserify-rsa": "^4.1.0",
"create-hash": "^1.2.0",
"create-hmac": "^1.1.7",
- "elliptic": "^6.5.3",
+ "elliptic": "^6.5.4",
"inherits": "^2.0.4",
- "parse-asn1": "^5.1.5",
- "readable-stream": "^3.6.0",
- "safe-buffer": "^5.2.0"
+ "parse-asn1": "^5.1.6",
+ "readable-stream": "^3.6.2",
+ "safe-buffer": "^5.2.1"
},
"dependencies": {
"bn.js": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
- "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==",
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
"dev": true
},
"readable-stream": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
- "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
@@ -52564,9 +52547,9 @@
}
},
"crypto-js": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
- "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
},
"crypto-random-string": {
"version": "2.0.0",
@@ -69483,7 +69466,8 @@
"tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
- "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
+ "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
+ "dev": true
},
"tunnel-agent": {
"version": "0.6.0",
diff --git a/package.json b/package.json
index 86267e357d..8a56373986 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "carbon-react",
- "version": "123.0.1",
+ "version": "123.2.0",
"description": "A library of reusable React components for easily building user interfaces.",
"files": [
"lib",
diff --git a/playwright/components/step-sequence/index.ts b/playwright/components/step-sequence/index.ts
index 671aa7bb5b..3607b1b76b 100644
--- a/playwright/components/step-sequence/index.ts
+++ b/playwright/components/step-sequence/index.ts
@@ -11,6 +11,6 @@ export const stepSequenceItemIndicator = (page: Page) =>
export const stepSequenceDataComponentItem = (page: Page) =>
page.locator(STEP_SEQUENCE_DATA_COMPONENT_ITEM);
-
+
export const stepSequenceDataComponent = (page: Page) =>
page.locator(STEP_SEQUENCE_DATA_COMPONENT);
diff --git a/playwright/support/helper.ts b/playwright/support/helper.ts
index f33aa191f1..1822df2625 100644
--- a/playwright/support/helper.ts
+++ b/playwright/support/helper.ts
@@ -350,3 +350,8 @@ export const continuePressingSHIFTTAB = async (page: Page, count: number) => {
await Promise.all(promises);
};
+
+export const waitForAnimationEnd = (locator: Locator) =>
+ locator.evaluate((element) =>
+ Promise.all(element.getAnimations().map((animation) => animation.finished))
+ );
diff --git a/src/components/action-popover/action-popover-item/action-popover-item.component.tsx b/src/components/action-popover/action-popover-item/action-popover-item.component.tsx
index 3ab0dde7f4..c599902df8 100644
--- a/src/components/action-popover/action-popover-item/action-popover-item.component.tsx
+++ b/src/components/action-popover/action-popover-item/action-popover-item.component.tsx
@@ -212,6 +212,7 @@ export const ActionPopoverItem = ({
}
}, [alignSubmenu, submenu]);
+ // focuses item on opening of actionPopover submenu
useEffect(() => {
if (focusItem) {
ref.current?.focus();
diff --git a/src/components/action-popover/action-popover-menu/action-popover-menu.component.tsx b/src/components/action-popover/action-popover-menu/action-popover-menu.component.tsx
index 5a69efcd5a..0d54db4d98 100644
--- a/src/components/action-popover/action-popover-menu/action-popover-menu.component.tsx
+++ b/src/components/action-popover/action-popover-menu/action-popover-menu.component.tsx
@@ -1,4 +1,10 @@
-import React, { useCallback, useMemo, useContext, useState } from "react";
+import React, {
+ useCallback,
+ useMemo,
+ useContext,
+ useState,
+ useEffect,
+} from "react";
import invariant from "invariant";
import { Menu } from "../action-popover.style";
@@ -87,8 +93,10 @@ const ActionPopoverMenu = React.forwardRef<
}
return (
- child.type !== ActionPopoverItem &&
- child.type !== ActionPopoverDivider
+ (child.type as React.FunctionComponent).displayName !==
+ ActionPopoverItem.displayName &&
+ (child.type as React.FunctionComponent).displayName !==
+ ActionPopoverDivider.displayName
);
}
);
@@ -108,46 +116,100 @@ const ActionPopoverMenu = React.forwardRef<
});
}, [children]);
+ const isItemDisabled = useCallback(
+ (value: number) => {
+ const item = items[value];
+ // The invariant will be triggered before this else path can be explored, hence the ignore else.
+ // istanbul ignore else
+ return React.isValidElement(item) && item.props.disabled;
+ },
+ [items]
+ );
+
+ const firstFocusableItem = items.findIndex(
+ (_, index) => !isItemDisabled(index)
+ );
+
+ // FIX-ME: FE-6248
+ // Once we no longer support Node 16, this function can be removed and `findLastIndex()` can be used in it's place.
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex
+ function findLastFocusableItem() {
+ let lastFocusableItem = -1;
+ for (let i = items.length - 1; i >= 0; i--) {
+ if (!isItemDisabled(i)) {
+ lastFocusableItem = i;
+ break;
+ }
+ }
+ return lastFocusableItem;
+ }
+
+ const lastFocusableItem = findLastFocusableItem();
+
+ useEffect(() => {
+ if (isOpen && firstFocusableItem !== -1)
+ setFocusIndex(firstFocusableItem);
+ }, [isOpen, firstFocusableItem, setFocusIndex]);
+
const onKeyDown = useCallback(
(e) => {
if (Events.isTabKey(e)) {
e.preventDefault();
- // TAB: close menu and allow focus to change to next focusable element
+ // TAB: close menu and allow focus to change to the next focusable element
focusButton();
setOpen(false);
} else if (Events.isDownKey(e)) {
- // DOWN: focus next item or first
+ // DOWN: focus on the next item or first non-disabled item
e.preventDefault();
e.stopPropagation();
- const indexValue = focusIndex < items.length - 1 ? focusIndex + 1 : 0;
+ let indexValue = focusIndex + 1;
+ while (indexValue < items.length && isItemDisabled(indexValue)) {
+ indexValue += 1;
+ }
+ if (indexValue >= items.length) {
+ indexValue = firstFocusableItem;
+ }
setFocusIndex(indexValue);
} else if (Events.isUpKey(e)) {
- // UP: focus previous item or last
+ // UP: focus on the previous item or last non-disabled item
e.preventDefault();
e.stopPropagation();
- const indexValue = focusIndex > 0 ? focusIndex - 1 : items.length - 1;
+ let indexValue = focusIndex - 1;
+ while (
+ indexValue >= firstFocusableItem &&
+ isItemDisabled(indexValue)
+ ) {
+ indexValue -= 1;
+ }
+ if (indexValue < firstFocusableItem) {
+ indexValue = lastFocusableItem;
+ }
setFocusIndex(indexValue);
} else if (Events.isHomeKey(e)) {
- // HOME: focus first item
+ // HOME: focus on the first non-disabled item
e.preventDefault();
e.stopPropagation();
- setFocusIndex(0);
+ const indexValue = firstFocusableItem;
+ setFocusIndex(indexValue);
} else if (Events.isEndKey(e)) {
- // END: focus last item
+ // END: focus on the last non-disabled item
e.preventDefault();
e.stopPropagation();
- setFocusIndex(items.length - 1);
+ const indexValue = lastFocusableItem;
+ setFocusIndex(indexValue);
} else if (e.key.length === 1) {
- // any printable character: focus the next item on the list that starts with that character
- // selection should wrap to the start of the list
+ // Any printable character: focus on the next non-disabled item on the list that starts with that character
+ // Selection should wrap to the start of the list
e.stopPropagation();
let firstMatch: number | undefined;
let nextMatch: number | undefined;
- React.Children.forEach(items, (child, index) => {
+ items.forEach((item, index) => {
if (
- React.isValidElement(child) &&
- child.props.children.toLowerCase().startsWith(e.key.toLowerCase())
+ React.isValidElement(item) &&
+ !isItemDisabled(index) &&
+ item.props.children.toLowerCase().startsWith(e.key.toLowerCase())
) {
+ // istanbul ignore else
if (firstMatch === undefined) {
firstMatch = index;
}
@@ -164,7 +226,16 @@ const ActionPopoverMenu = React.forwardRef<
}
}
},
- [focusButton, setOpen, focusIndex, items, setFocusIndex]
+ [
+ focusButton,
+ setOpen,
+ focusIndex,
+ items,
+ isItemDisabled,
+ setFocusIndex,
+ firstFocusableItem,
+ lastFocusableItem,
+ ]
);
const [childHasSubmenu, setChildHasSubmenu] = useState(false);
diff --git a/src/components/action-popover/action-popover-test.stories.tsx b/src/components/action-popover/action-popover-test.stories.tsx
index a3223d2078..a27f87a77e 100644
--- a/src/components/action-popover/action-popover-test.stories.tsx
+++ b/src/components/action-popover/action-popover-test.stories.tsx
@@ -63,7 +63,7 @@ export const Default = () => {
First Name
Last Name
-
+ Options
@@ -177,7 +177,7 @@ export const ActionPopoverCustom = ({ ...props }) => {
First Name
Last Name
-
+ Options
@@ -610,3 +610,99 @@ export const ActionPopoverPropsComponent = (
);
};
+
+export const ActionPopoverPropsComponentWithFirstAndLastDisabled = (
+ props: Partial
+) => {
+ return (
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+ Item 6
+ Item 7
+
+ );
+};
+
+export const ActionPopoverPropsComponentWithOnlyFirstAndLastNotDisabled = (
+ props: Partial
+) => {
+ return (
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+ Item 6
+ Item 7
+
+ );
+};
+
+export const ActionPopoverPropsComponentWithOnlyFirstDisabled = (
+ props: Partial
+) => {
+ return (
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+ Item 6
+ Item 7
+
+ );
+};
+
+export const ActionPopoverPropsComponentWithOnlyLastDisabled = (
+ props: Partial
+) => {
+ return (
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+ Item 6
+ Item 7
+
+ );
+};
+
+export const ActionPopoverPropsComponentWithSomeDisabled = (
+ props: Partial
+) => {
+ return (
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+ Item 6
+ Item 7
+
+ );
+};
+
+export const ActionPopoverPropsComponentAllDisabled = (
+ props: Partial
+) => {
+ return (
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+ Item 6
+ Item 7
+
+ );
+};
diff --git a/src/components/action-popover/action-popover.component.tsx b/src/components/action-popover/action-popover.component.tsx
index 87db208b9a..3a66c5128e 100644
--- a/src/components/action-popover/action-popover.component.tsx
+++ b/src/components/action-popover/action-popover.component.tsx
@@ -199,9 +199,8 @@ export const ActionPopover = ({
// There will be multiple document click listeners but we cant prevent propagation because it will interfere with
// other instances on the same page
- const isInMenu = menu.current && menu.current.contains(target as Node);
- const isInButton =
- buttonRef.current && buttonRef.current.contains(target as Node);
+ const isInMenu = menu?.current?.contains(target as Node);
+ const isInButton = buttonRef?.current?.contains(target as Node);
if (!isInMenu && !isInButton) {
setOpen(false);
diff --git a/src/components/action-popover/action-popover.pw.tsx b/src/components/action-popover/action-popover.pw.tsx
index a1d14c8cbd..5a16203c74 100644
--- a/src/components/action-popover/action-popover.pw.tsx
+++ b/src/components/action-popover/action-popover.pw.tsx
@@ -38,8 +38,8 @@ import {
ActionPopoverComponentIcons,
ActionPopoverComponentInFlatTable,
ActionPopoverComponentInOverflowHiddenContainer,
- ActionPopoverComponentKeyboardNaviationLeftAlignedSubmenu,
- ActionPopoverComponentKeyboardNaviationRightAlignedSubmenu,
+ ActionPopoverComponentKeyboardNavigationLeftAlignedSubmenu,
+ ActionPopoverComponentKeyboardNavigationRightAlignedSubmenu,
ActionPopoverComponentKeyboardNavigation,
ActionPopoverComponentMenuOpeningAbove,
ActionPopoverComponentMenuRightAligned,
@@ -63,6 +63,10 @@ import {
ActionPopoverWithSubmenusAndSomeIcons,
ActionPopoverWithVariableChildren,
ActionPopoverWithRenderProp,
+ ActionPopoverPropsComponentAllDisabled,
+ ActionPopoverPropsComponentWithSomeDisabled,
+ ActionPopoverPropsComponentWithOnlyFirstAndLastNotDisabled,
+ ActionPopoverPropsComponentWithFirstAndLastDisabled,
} from "../../../src/components/action-popover/components.test-pw";
const keyToTrigger = ["Enter", " ", "End", "ArrowDown", "ArrowUp"] as const;
@@ -78,12 +82,11 @@ test.describe("check functionality for ActionPopover component", () => {
});
([
- [0, "Business"],
- [1, "Email Invoice"],
- [2, "Print Invoice"],
- [3, "Download PDF"],
- [4, "Download CSV"],
- [5, "Delete"],
+ [0, "Email Invoice"],
+ [1, "Print Invoice"],
+ [2, "Download PDF"],
+ [3, "Download CSV"],
+ [4, "Delete"],
] as [number, string][]).forEach(([times, elementText]) => {
test(`should be able to press downarrow ${times} times and get button ${elementText} focused`, async ({
mount,
@@ -109,13 +112,13 @@ test.describe("check functionality for ActionPopover component", () => {
).first();
await actionPopoverButtonElement.press(key);
const focusedElement = await page.locator("*:focus");
- await expect(focusedElement).toContainText("Business");
+ await expect(focusedElement).toContainText("Email Invoice");
const actionPopoverElement = await actionPopover(page).first();
await expect(actionPopoverElement).toBeVisible();
});
});
- test("should focus the first element Business using Home key", async ({
+ test("should focus the first element Email Invoice using Home key", async ({
mount,
page,
}) => {
@@ -128,7 +131,7 @@ test.describe("check functionality for ActionPopover component", () => {
}
const focusedElement = await page.locator("*:focus");
await focusedElement.press("Home");
- await expect(focusedElement).toContainText("Business");
+ await expect(focusedElement).toContainText("Email Invoice");
});
test("should focus the first sub menu 1 element using Home key", async ({
@@ -165,7 +168,7 @@ test.describe("check functionality for ActionPopover component", () => {
});
});
- test("should focus the last sub menu 3 element using End keyboard key", async ({
+ test("should focus the last sub menu 2 element using End keyboard key", async ({
mount,
page,
}) => {
@@ -179,7 +182,7 @@ test.describe("check functionality for ActionPopover component", () => {
const focusedElement = await page.locator("*:focus");
await focusedElement.press("ArrowLeft");
await focusedElement.press("End");
- await expect(focusedElement).toContainText("Sub Menu 3");
+ await expect(focusedElement).toContainText("Sub Menu 2");
});
test("should close using Tab key", async ({ mount, page }) => {
@@ -292,7 +295,6 @@ test.describe("check functionality for ActionPopover component", () => {
([
[subMenuOption[0], 0],
[subMenuOption[1], 1],
- [subMenuOption[2], 2],
] as [typeof subMenuOption[number], number][]).forEach(
([innerText, times]) => {
test(`should focus ${innerText} element`, async ({ mount, page }) => {
@@ -315,6 +317,28 @@ test.describe("check functionality for ActionPopover component", () => {
}
);
+ ([[subMenuOption[2], 2]] as [typeof subMenuOption[number], number][]).forEach(
+ ([innerText, times]) => {
+ test(`should not focus ${innerText} element`, async ({ mount, page }) => {
+ await mount();
+ const actionPopoverButtonElementEq0 = await actionPopoverButton(
+ page
+ ).nth(0);
+ await actionPopoverButtonElementEq0.click();
+ for (let i = 0; i < 2; i++) {
+ const focusedElement = await page.locator("*:focus");
+ await focusedElement.press("ArrowDown");
+ }
+ const focusedElement = await page.locator("*:focus");
+ await focusedElement.press("ArrowLeft");
+ for (let i = 0; i < times; i++) {
+ await focusedElement.press("ArrowDown");
+ }
+ await expect(focusedElement).not.toContainText(innerText);
+ });
+ }
+ );
+
([
[subMenuOption[0], 0],
[subMenuOption[1], 1],
@@ -401,10 +425,7 @@ test.describe("check functionality for ActionPopover component", () => {
}
);
- ([
- [subMenuOption[0], 0],
- [subMenuOption[1], 1],
- ] as [typeof subMenuOption[number], number][]).forEach(([name, item]) => {
+ [[subMenuOption[0]], [subMenuOption[1]]].forEach(([name]) => {
test(`should close ${name} and ActionPopover after clicking on the submenu`, async ({
mount,
page,
@@ -421,8 +442,7 @@ test.describe("check functionality for ActionPopover component", () => {
for (let i = 0; i < 2; i++) {
await focusedElement.press("ArrowDown");
}
- const submenuItem = await actionPopoverSubmenu(page, item);
- await submenuItem.click();
+ await focusedElement.click();
const actionPopoverElement = await actionPopover(page).first();
await expect(actionPopoverElement).not.toBeVisible();
});
@@ -522,6 +542,190 @@ test.describe("check functionality for ActionPopover component", () => {
);
});
});
+
+ ([
+ [0, "Item 2"],
+ [1, "Item 3"],
+ [2, "Item 4"],
+ [3, "Item 5"],
+ [4, "Item 6"],
+ ] as [number, string][]).forEach(([times, elementText]) => {
+ test(`should be able to press downarrow ${times} times and get button ${elementText} focused when first and last items are disabled`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const actionPopoverButtonElement = await actionPopoverButton(page).nth(0);
+ await actionPopoverButtonElement.click();
+ for (let i = 0; i < times; i++) {
+ const focusedElement = await page.locator("*:focus");
+ await focusedElement.press("ArrowDown");
+ }
+ const focusedElement = await page.locator("*:focus");
+ await expect(focusedElement).toContainText(elementText);
+ });
+ });
+
+ ([
+ [0, "Item 2"],
+ [1, "Item 6"],
+ [2, "Item 5"],
+ [3, "Item 4"],
+ [4, "Item 3"],
+ ] as [number, string][]).forEach(([times, elementText]) => {
+ test(`should be able to press up ${times} times and get button ${elementText} focused when first and last items are disabled`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const actionPopoverButtonElement = await actionPopoverButton(page).nth(0);
+ await actionPopoverButtonElement.click();
+ for (let i = 0; i < times; i++) {
+ const focusedElement = await page.locator("*:focus");
+ await focusedElement.press("ArrowUp");
+ }
+ const focusedElement = await page.locator("*:focus");
+ await expect(focusedElement).toContainText(elementText);
+ });
+ });
+
+ ([
+ [0, "Item 1"],
+ [1, "Item 7"],
+ ] as [number, string][]).forEach(([times, elementText]) => {
+ test(`should be able to press downarrow ${times} times and get button ${elementText} focused when only the first and last items are not disabled`, async ({
+ mount,
+ page,
+ }) => {
+ await mount(
+
+ );
+
+ const actionPopoverButtonElement = await actionPopoverButton(page).nth(0);
+ await actionPopoverButtonElement.click();
+ for (let i = 0; i < times; i++) {
+ const focusedElement = await page.locator("*:focus");
+ await focusedElement.press("ArrowDown");
+ }
+ const focusedElement = await page.locator("*:focus");
+ await expect(focusedElement).toContainText(elementText);
+ });
+ });
+
+ ([
+ [0, "Item 1"],
+ [1, "Item 7"],
+ ] as [number, string][]).forEach(([times, elementText]) => {
+ test(`should be able to press up ${times} times and get button ${elementText} focused`, async ({
+ mount,
+ page,
+ }) => {
+ await mount(
+
+ );
+
+ const actionPopoverButtonElement = await actionPopoverButton(page).nth(0);
+ await actionPopoverButtonElement.click();
+ for (let i = 0; i < times; i++) {
+ const focusedElement = await page.locator("*:focus");
+ await focusedElement.press("ArrowUp");
+ }
+ const focusedElement = await page.locator("*:focus");
+ await expect(focusedElement).toContainText(elementText);
+ });
+ });
+
+ ([
+ [0, "Item 1"],
+ [1, "Item 4"],
+ [2, "Item 6"],
+ ] as [number, string][]).forEach(([times, elementText]) => {
+ test(`should be able to press downarrow ${times} times and get button ${elementText} focused when only a few items are disabled`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const actionPopoverButtonElement = await actionPopoverButton(page).nth(0);
+ await actionPopoverButtonElement.click();
+ for (let i = 0; i < times; i++) {
+ const focusedElement = await page.locator("*:focus");
+ await focusedElement.press("ArrowDown");
+ }
+ const focusedElement = await page.locator("*:focus");
+ await expect(focusedElement).toContainText(elementText);
+ });
+ });
+
+ ([
+ [0, "Item 1"],
+ [1, "Item 6"],
+ [2, "Item 4"],
+ ] as [number, string][]).forEach(([times, elementText]) => {
+ test(`should be able to press up ${times} times and get button ${elementText} focused when only a few items are disabled`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const actionPopoverButtonElement = await actionPopoverButton(page).nth(0);
+ await actionPopoverButtonElement.click();
+ for (let i = 0; i < times; i++) {
+ const focusedElement = await page.locator("*:focus");
+ await focusedElement.press("ArrowUp");
+ }
+ const focusedElement = await page.locator("*:focus");
+ await expect(focusedElement).toContainText(elementText);
+ });
+ });
+
+ ([
+ [1, "Item 2"],
+ [2, "Item 3"],
+ [3, "Item 4"],
+ [4, "Item 5"],
+ ] as [number, string][]).forEach(([times, elementText]) => {
+ test(`should be able to press down ${times} times and not get button ${elementText} focused when all items are disabled`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const actionPopoverButtonElement = await actionPopoverButton(page).nth(0);
+ await actionPopoverButtonElement.click();
+ for (let i = 0; i < times; i++) {
+ const focusedElement = await page.locator("*:focus");
+ await focusedElement.press("ArrowDown");
+ }
+ const focusedElement = await page.locator("*:focus");
+ await expect(focusedElement).not.toContainText(elementText);
+ });
+ });
+
+ ([
+ [1, "Item 2"],
+ [2, "Item 3"],
+ [3, "Item 4"],
+ [4, "Item 5"],
+ ] as [number, string][]).forEach(([times, elementText]) => {
+ test(`should be able to press up ${times} times and not get button ${elementText} focused when all items are disabled`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const actionPopoverButtonElement = await actionPopoverButton(page).nth(0);
+ await actionPopoverButtonElement.click();
+ for (let i = 0; i < times; i++) {
+ const focusedElement = await page.locator("*:focus");
+ await focusedElement.press("ArrowUp");
+ }
+ const focusedElement = await page.locator("*:focus");
+ await expect(focusedElement).not.toContainText(elementText);
+ });
+ });
});
test.describe("check props for ActionPopover component", () => {
@@ -1090,7 +1294,7 @@ test.describe("when focused", () => {
"rgb(255, 188, 25) solid 3px"
);
await actionPopoverButtonElement.click();
- const focusedItem = await actionPopoverInnerItem(page, 0);
+ const focusedItem = await actionPopoverInnerItem(page, 1);
await expect(focusedItem).toHaveCSS(
"outline",
"rgb(255, 188, 25) solid 3px"
@@ -1109,7 +1313,7 @@ test.describe("when focused", () => {
"rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px"
);
await actionPopoverButtonElement.click();
- const focusedItem = await actionPopoverInnerItem(page, 0);
+ const focusedItem = await actionPopoverInnerItem(page, 1);
await expect(focusedItem).toHaveCSS(
"box-shadow",
"rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px"
@@ -1272,7 +1476,7 @@ test.describe("Accessibility tests for ActionPopover", () => {
mount,
page,
}) => {
- await mount();
+ await mount();
const actionPopoverButtonElement = await actionPopoverButton(page).nth(0);
await actionPopoverButtonElement.click();
const submenuTrigger = await actionPopoverInnerItem(page, 0);
@@ -1284,7 +1488,9 @@ test.describe("Accessibility tests for ActionPopover", () => {
mount,
page,
}) => {
- await mount();
+ await mount(
+
+ );
const actionPopoverButtonElement = await actionPopoverButton(page).nth(0);
await actionPopoverButtonElement.click();
const submenuTrigger = await actionPopoverInnerItem(page, 0);
diff --git a/src/components/action-popover/action-popover.spec.tsx b/src/components/action-popover/action-popover.spec.tsx
index 3dce97b470..d53faa9e95 100644
--- a/src/components/action-popover/action-popover.spec.tsx
+++ b/src/components/action-popover/action-popover.spec.tsx
@@ -409,6 +409,49 @@ describe("ActionPopover", () => {
);
}
+ function renderActionPopoverWithMultipleDisabledItems(
+ props = {},
+ renderer = mount
+ ) {
+ const defaultProps = {
+ children: [
+ {}}>
+ Business
+ ,
+ {}}>
+ Email Invoice
+ ,
+ {}}>
+ Print Invoice
+ ,
+ {}}>
+ Download PDF
+ ,
+ {}}>
+ Download CSV
+ ,
+ ,
+ {}}>
+ Delete
+ ,
+ {}}>
+ Return Home
+ ,
+ null,
+ undefined,
+ ],
+ ...props,
+ };
+
+ renderer(
+ <>
+
+
+
+ >
+ );
+ }
+
function getElements() {
return {
items: wrapper.find(ActionPopoverItem),
@@ -683,10 +726,10 @@ describe("ActionPopover", () => {
expect(stopPropagation).toHaveBeenCalled();
});
- it("Clicking focuses the first element", () => {
+ it("Clicking focuses the first focusable element", () => {
const { items } = getElements();
- expect(items.at(0).find(StyledMenuItem)).toBeFocused();
+ expect(items.at(1).find(StyledMenuItem)).toBeFocused();
});
it("Clicking closes the menu", () => {
@@ -773,7 +816,7 @@ describe("ActionPopover", () => {
);
it.each(["ArrowDown", "Space", "Enter"] as const)(
- "Pressing %s key selects the first item",
+ "Pressing %s key selects the first focusable item",
(key) => {
render();
const { menubutton } = getElements();
@@ -781,17 +824,17 @@ describe("ActionPopover", () => {
jest.runAllTimers();
const { items } = getElements();
- expect(items.first().find(StyledMenuItem)).toBeFocused();
+ expect(items.at(1).find(StyledMenuItem)).toBeFocused();
}
);
- it("Pressing UpArrow selects the last item", () => {
+ it("Pressing UpArrow selects the last focusable item", () => {
render();
- const { menubutton } = getElements();
- simulate.keydown.pressArrowUp(menubutton);
+ openMenu();
+ const { items } = getElements();
+ simulate.keydown.pressArrowUp(items.at(1).find(StyledMenuItem).at(0));
jest.runAllTimers();
- const { items } = getElements();
expect(items.last().find(StyledMenuItem)).toBeFocused();
});
});
@@ -865,9 +908,6 @@ describe("ActionPopover", () => {
"next",
"",
(items: ReactWrapper) => {
- simulate.keydown.pressArrowDown(
- items.first().find(StyledMenuItem).at(0)
- );
jest.runAllTimers();
expect(items.at(1).find(StyledMenuItem)).toBeFocused();
@@ -888,8 +928,8 @@ describe("ActionPopover", () => {
items.at(3).find(StyledMenuItem).at(0)
);
jest.runAllTimers();
- expect(items.at(0).find(StyledMenuItem)).toBeFocused();
- // we're checking that we can focus the disabled item, this is intentional behaviour
+ // we're checking that we cannot focus the first disabled item
+ expect(items.at(0).find(StyledMenuItem)).not.toBeFocused();
expect(items.at(0).prop("disabled")).toBe(true);
expect(
items
@@ -903,9 +943,9 @@ describe("ActionPopover", () => {
[
"Down",
"first",
- "if the focus on the last item",
+ "if the focus on the last focusable item",
(items: ReactWrapper) => {
- simulate.keydown.pressEnd(items.first().find(StyledMenuItem).at(0));
+ simulate.keydown.pressEnd(items.at(1).find(StyledMenuItem).at(0));
jest.runAllTimers();
expect(items.last().find(StyledMenuItem)).toBeFocused();
@@ -913,7 +953,7 @@ describe("ActionPopover", () => {
items.last().find(StyledMenuItem).at(0)
);
jest.runAllTimers();
- expect(items.first().find(StyledMenuItem)).toBeFocused();
+ expect(items.at(1).find(StyledMenuItem)).toBeFocused();
},
],
[
@@ -921,9 +961,6 @@ describe("ActionPopover", () => {
"previous",
"",
(items: ReactWrapper) => {
- simulate.keydown.pressArrowDown(
- items.first().find(StyledMenuItem).at(0)
- );
simulate.keydown.pressArrowDown(
items.at(1).find(StyledMenuItem).at(0)
);
@@ -949,16 +986,17 @@ describe("ActionPopover", () => {
items.at(1).find(StyledMenuItem).at(0)
);
jest.runAllTimers();
- expect(items.first().find(StyledMenuItem)).toBeFocused();
+ expect(items.at(3).find(StyledMenuItem)).toBeFocused();
+ expect(items.first().find(StyledMenuItem)).not.toBeFocused();
},
],
[
"Up",
"last",
- "if the focus is on the first item",
+ "if the focus is on the first focusable item",
(items: ReactWrapper) => {
simulate.keydown.pressArrowUp(
- items.first().find(StyledMenuItem).at(0)
+ items.at(1).find(StyledMenuItem).at(0)
);
jest.runAllTimers();
expect(items.last().find(StyledMenuItem)).toBeFocused();
@@ -969,13 +1007,9 @@ describe("ActionPopover", () => {
"first",
"",
(items: ReactWrapper) => {
- simulate.keydown.pressArrowDown(
- items.first().find(StyledMenuItem).at(0)
- );
-
simulate.keydown.pressHome(items.at(1));
- expect(items.first().find(StyledMenuItem)).toBeFocused();
+ expect(items.at(1).find(StyledMenuItem)).toBeFocused();
},
],
[
@@ -1004,10 +1038,10 @@ describe("ActionPopover", () => {
openMenu();
const { items } = getElements();
- simulate.keydown.pressSpace(items.at(0).find(StyledMenuItem));
+ simulate.keydown.pressSpace(items.at(1).find(StyledMenuItem));
const { menu } = getElements();
- expect(items.at(0).find(StyledMenuItem)).toBeFocused();
+ expect(items.at(1).find(StyledMenuItem)).toBeFocused();
expect(menu.exists()).toBe(true);
});
@@ -1018,7 +1052,7 @@ describe("ActionPopover", () => {
const { items } = getElements();
// moves to first element starting with P
- simulate.keydown.pressP(items.first().find(StyledMenuItem).at(0));
+ simulate.keydown.pressP(items.at(1).find(StyledMenuItem).at(0));
jest.runAllTimers();
expect(items.at(2).find(StyledMenuItem)).toBeFocused();
@@ -1028,10 +1062,11 @@ describe("ActionPopover", () => {
expect(items.at(3).find(StyledMenuItem)).toBeFocused();
// moves to next element starting with D, it loops to the start
- // we're checking that we can focus the disabled item, this is intentional behaviour
simulate.keydown.pressD(items.at(3).find(StyledMenuItem).at(0));
jest.runAllTimers();
- expect(items.at(0).find(StyledMenuItem)).toBeFocused();
+ expect(items.at(3).find(StyledMenuItem)).toBeFocused();
+ // we're checking that we cannot focus the disabled item.
+ expect(items.at(0).find(StyledMenuItem)).not.toBeFocused();
expect(items.at(0).prop("disabled")).toBe(true);
expect(
items
@@ -1042,9 +1077,9 @@ describe("ActionPopover", () => {
).toBe("true");
// does nothing when there are no matches
- simulate.keydown.pressZ(items.at(0).find(StyledMenuItem).at(0));
+ simulate.keydown.pressZ(items.at(3).find(StyledMenuItem).at(0));
jest.runAllTimers();
- expect(items.at(0).find(StyledMenuItem)).toBeFocused();
+ expect(items.at(3).find(StyledMenuItem)).toBeFocused();
});
it("does nothing when a non printable character key is pressed", () => {
@@ -1053,9 +1088,9 @@ describe("ActionPopover", () => {
const { items } = getElements();
- items.at(0).simulate("keydown", { key: "F1" });
+ items.at(1).simulate("keydown", { key: "F1" });
jest.runAllTimers();
- expect(items.at(0).find(StyledMenuItem)).toBeFocused();
+ expect(items.at(1).find(StyledMenuItem)).toBeFocused();
});
});
});
@@ -2113,4 +2148,55 @@ describe("ActionPopover", () => {
}
);
});
+
+ describe("When ActionPopoverMenu contains multiple disabled items", () => {
+ it("should focus the next focusable item when down arrow is pressed", () => {
+ renderActionPopoverWithMultipleDisabledItems();
+ openMenu();
+ const { items } = getElements();
+
+ simulate.keydown.pressArrowDown(items.at(0).find(StyledMenuItem).at(0));
+ jest.runAllTimers();
+
+ expect(items.at(3).find(StyledMenuItem)).toBeFocused();
+ });
+
+ it("should focus the next focusable item when up arrow is pressed", () => {
+ renderActionPopoverWithMultipleDisabledItems();
+ openMenu();
+ const { items } = getElements();
+
+ simulate.keydown.pressArrowDown(items.at(0).find(StyledMenuItem).at(0));
+ jest.runAllTimers();
+
+ expect(items.at(3).find(StyledMenuItem)).toBeFocused();
+
+ simulate.keydown.pressArrowUp(items.at(3).find(StyledMenuItem).at(0));
+ jest.runAllTimers();
+
+ expect(items.at(0).find(StyledMenuItem)).toBeFocused();
+ });
+
+ it("should focus the first focusable item when Home is pressed", () => {
+ renderActionPopoverWithMultipleDisabledItems();
+ openMenu();
+ const { items } = getElements();
+
+ simulate.keydown.pressHome(items.at(5).find(StyledMenuItem).at(0));
+ jest.runAllTimers();
+
+ expect(items.at(0).find(StyledMenuItem)).toBeFocused();
+ });
+
+ it("should focus the last focusable item when End is pressed", () => {
+ renderActionPopoverWithMultipleDisabledItems();
+ openMenu();
+ const { items } = getElements();
+
+ simulate.keydown.pressEnd(items.at(0).find(StyledMenuItem).at(0));
+ jest.runAllTimers();
+
+ expect(items.at(5).find(StyledMenuItem)).toBeFocused();
+ });
+ });
});
diff --git a/src/components/action-popover/action-popover.stories.mdx b/src/components/action-popover/action-popover.stories.mdx
index cada646c03..992ff7085a 100644
--- a/src/components/action-popover/action-popover.stories.mdx
+++ b/src/components/action-popover/action-popover.stories.mdx
@@ -83,7 +83,7 @@ Each item is clickable and represents an action to be taken on a given row.
### With disabled item
-ActionPopoverItem's can be disabled and will not dispatch an action but can still be navigated with the keyboard.
+ActionPopoverItem's can be disabled. These items are not clickable and can not be navigated to with the keyboard.
+### Confirm with Custom Data Tags
+
+
+
## Props
### Confirm
diff --git a/src/components/confirm/confirm.stories.tsx b/src/components/confirm/confirm.stories.tsx
index 59aff49225..eb09336eb9 100644
--- a/src/components/confirm/confirm.stories.tsx
+++ b/src/components/confirm/confirm.stories.tsx
@@ -25,6 +25,33 @@ export const Default = () => {
);
};
+export const DefaultWithCustomDataTags = () => {
+ const [isOpen, setIsOpen] = useState(defaultOpenState);
+ return (
+ <>
+
+ setIsOpen(false)}
+ onCancel={() => setIsOpen(false)}
+ cancelButtonDataProps={{
+ "data-element": "bang",
+ "data-role": "wallop",
+ }}
+ confirmButtonDataProps={{
+ "data-element": "bar",
+ "data-role": "wiz",
+ }}
+ >
+ Content
+
+ >
+ );
+};
+
export const SingleAction = () => {
const [isOpen, setIsOpen] = useState(defaultOpenState);
return (
diff --git a/src/components/dialog/dialog.pw.tsx b/src/components/dialog/dialog.pw.tsx
index cb007a28cc..b45aab7c21 100644
--- a/src/components/dialog/dialog.pw.tsx
+++ b/src/components/dialog/dialog.pw.tsx
@@ -25,6 +25,7 @@ import toastComponent from "../../../playwright/components/toast";
import {
checkAccessibility,
getStyle,
+ waitForAnimationEnd,
} from "../../../playwright/support/helper";
import { CHARACTERS, SIZE } from "../../../playwright/support/constants";
@@ -374,6 +375,7 @@ test.describe(
await openToastButton.click();
const toast = toastComponent(page);
+ await waitForAnimationEnd(toast);
await expect(toast).toBeFocused();
});
diff --git a/src/components/drawer/__snapshots__/drawer.spec.tsx.snap b/src/components/drawer/__snapshots__/drawer.spec.tsx.snap
index 05f92616ad..50710a2fd4 100644
--- a/src/components/drawer/__snapshots__/drawer.spec.tsx.snap
+++ b/src/components/drawer/__snapshots__/drawer.spec.tsx.snap
@@ -55,6 +55,7 @@ exports[`Drawer uncontrolled matches snapshot 1`] = `
{stickyHeader && (
diff --git a/src/components/drawer/drawer.pw.tsx b/src/components/drawer/drawer.pw.tsx
index 1982132083..5a5b770c06 100644
--- a/src/components/drawer/drawer.pw.tsx
+++ b/src/components/drawer/drawer.pw.tsx
@@ -17,6 +17,7 @@ import {
checkAccessibility,
assertCssValueIsApproximately,
isInViewport,
+ waitForAnimationEnd,
} from "../../../playwright/support/helper";
test.describe(
@@ -361,6 +362,10 @@ test.describe("Accessibility tests for Drawer component", () => {
);
+ await waitForAnimationEnd(
+ page.locator('[data-element="drawer-content"]')
+ );
+
const drawerToggleButton = drawerToggle(page);
await drawerToggleButton.click();
const sidebar = drawerSidebar(page);
diff --git a/src/components/flat-table/flat-table-test.stories.tsx b/src/components/flat-table/flat-table-test.stories.tsx
index 30f1304b15..92518beab2 100644
--- a/src/components/flat-table/flat-table-test.stories.tsx
+++ b/src/components/flat-table/flat-table-test.stories.tsx
@@ -893,7 +893,7 @@ export const FlatTableCellRowSpanComponent = (
);
};
-export const FlatTableMutipleStickyComponent = (
+export const FlatTableMultipleStickyComponent = (
props: Partial
) => {
return (
diff --git a/src/components/loader/loader.pw.tsx b/src/components/loader/loader.pw.tsx
index 712b1b7703..2fb884d7cb 100644
--- a/src/components/loader/loader.pw.tsx
+++ b/src/components/loader/loader.pw.tsx
@@ -182,7 +182,7 @@ test.describe("check props for Loader component test", () => {
});
test.describe("Accessibility tests for Loader component", async () => {
- test("should pass accessibilty tests for Loader default story", async ({
+ test("should pass accessibility tests for Loader default story", async ({
mount,
page,
}) => {
diff --git a/src/components/menu/menu.spec.tsx b/src/components/menu/menu.spec.tsx
index b79d88ef8b..f14378756a 100644
--- a/src/components/menu/menu.spec.tsx
+++ b/src/components/menu/menu.spec.tsx
@@ -197,7 +197,7 @@ describe("Menu", () => {
});
describe("with multiple submenus", () => {
- it("when a sumenu is opened, any previously open submenu is closed", () => {
+ it("when a submenu is opened, any previously open submenu is closed", () => {
wrapper = mount(