diff --git a/.github/workflows/wcag.yml b/.github/workflows/wcag.yml new file mode 100644 index 00000000000..a59b63bd360 --- /dev/null +++ b/.github/workflows/wcag.yml @@ -0,0 +1,42 @@ +name: WCAG checks + +on: + push: + branches: [ master, next ] + pull_request: + branches: [ master, next ] + +env: + CYPRESS_CACHE_FOLDER: cypress/cache + +permissions: + contents: read + +jobs: + copy-to-clipboard-buttons: + name: Copy-to-clipboard button names + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Use Node.js + uses: actions/setup-node@v6 + with: + node-version-file: .nvmrc + + - name: Cache Node Modules and Cypress binary + uses: actions/cache@v5 + id: cache-primes + with: + path: | + node_modules + ${{ env.CYPRESS_CACHE_FOLDER }} + key: ${{ runner.os }}-node-and-cypress-${{ hashFiles('package-lock.json') }} + + - name: Install dependencies + if: steps.cache-primes.outputs.cache-hit != 'true' + run: npm ci + + - name: Run WCAG copy-button checks + run: npm run cy:wcag diff --git a/package-lock.json b/package-lock.json index 1dc0d8c27ff..96280543487 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,7 @@ "@release-it/conventional-changelog": "=11.0.0", "@svgr/webpack": "=8.1.0", "autoprefixer": "^10.4.21", + "axe-core": "=4.11.4", "babel-loader": "^9.2.1", "babel-plugin-lodash": "=3.3.4", "babel-plugin-module-resolver": "=5.0.2", @@ -75,6 +76,7 @@ "css-loader": "=7.1.2", "cssnano": "=7.0.7", "cypress": "=14.2.0", + "cypress-axe": "=1.7.0", "dedent": "^1.6.0", "deepmerge": "^4.3.1", "enzyme": "=3.11.0", @@ -6078,6 +6080,38 @@ "node": ">=18" } }, + "node_modules/@release-it/conventional-changelog/node_modules/conventional-commits-parser": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.4.0.tgz", + "integrity": "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@simple-libs/stream-utils": "^1.2.0", + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@release-it/conventional-changelog/node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@release-it/conventional-changelog/node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -8855,6 +8889,15 @@ "dev": true, "license": "MIT" }, + "node_modules/axe-core": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz", + "integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/axios": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", @@ -11486,6 +11529,19 @@ "node": "^18.0.0 || ^20.0.0 || >=22.0.0" } }, + "node_modules/cypress-axe": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/cypress-axe/-/cypress-axe-1.7.0.tgz", + "integrity": "sha512-zzJpvAAjauEB3GZl0KYXb8i3w6MztWAt2WM3czYTFyNVC30alDmqCm9E7GwZ4bgkldZJlmHakaVEyu73R5St4w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "axe-core": "^3 || ^4", + "cypress": "^10 || ^11 || ^12 || ^13 || ^14 || ^15" + } + }, "node_modules/cypress/node_modules/ansi-styles": { "version": "4.3.0", "dev": true, diff --git a/package.json b/package.json index 016941c49cd..5b33b2803f7 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "cy:open": "cross-env BROWSERSLIST_ENV=browser-production cypress open", "cy:run": "cross-env BROWSERSLIST_ENV=browser-production cypress run", "cy:ci": "start-server-and-test cy:start http://localhost:3204 cy:run", + "cy:wcag": "start-server-and-test cy:start http://localhost:3204 'npm run cy:run -- --spec test/e2e-cypress/e2e/a11y/button-accessible-names.cy.js'", "cy:dev": "start-server-and-test cy:start http://localhost:3204 cy:open", "open-static": "npx open-cli http://localhost:3002", "security-audit": "run-s -sc security-audit:all security-audit:prod", @@ -127,6 +128,7 @@ "@release-it/conventional-changelog": "=11.0.0", "@svgr/webpack": "=8.1.0", "autoprefixer": "^10.4.21", + "axe-core": "=4.11.4", "babel-loader": "^9.2.1", "babel-plugin-lodash": "=3.3.4", "babel-plugin-module-resolver": "=5.0.2", @@ -139,6 +141,7 @@ "css-loader": "=7.1.2", "cssnano": "=7.0.7", "cypress": "=14.2.0", + "cypress-axe": "=1.7.0", "dedent": "^1.6.0", "deepmerge": "^4.3.1", "enzyme": "=3.11.0", diff --git a/src/core/components/copy-to-clipboard-btn.jsx b/src/core/components/copy-to-clipboard-btn.jsx index 9c44487e2c5..2147b3ccccc 100644 --- a/src/core/components/copy-to-clipboard-btn.jsx +++ b/src/core/components/copy-to-clipboard-btn.jsx @@ -2,6 +2,8 @@ import React from "react" import { CopyToClipboard } from "react-copy-to-clipboard" import PropTypes from "prop-types" +const COPY_PATH_LABEL = "Copy path to clipboard" + /** * @param {{ getComponent: func, textToCopy: string }} props * @returns {JSX.Element} @@ -14,9 +16,19 @@ export default class CopyToClipboardBtn extends React.Component { const CopyIcon = getComponent("CopyIcon") return ( -