diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..70029ba9 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,88 @@ +name: "(Lint): CSS, JS, PHP" + +on: + pull_request: + types: [opened, synchronize, reopened, labeled] + push: + branches: + - 'core' + - 'pro' + paths-ignore: + - '**.md' + - '**.txt' + - '.gitignore' + - 'docs/**' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: lint-${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + lint: + name: stylelint, eslint, phpcs + runs-on: ubuntu-22.04 + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version-file: .node-version + cache: 'npm' + + - name: Install npm dependencies + run: npm ci + + - name: Set up PHP + uses: codesnippetspro/setup-php@v2 + with: + php-version: '8.2' + + - name: Compute dependency hash + id: deps-hash + run: | + set -euo pipefail + tmpfile=$(mktemp) + if [ -f "src/composer.lock" ]; then + cat "src/composer.lock" >> "$tmpfile" + fi + if [ -s "$tmpfile" ]; then + deps_hash=$(shasum -a 1 "$tmpfile" | awk '{print $1}' | cut -c1-8) + else + deps_hash=$(echo "${GITHUB_SHA:-unknown}" | cut -c1-8) + fi + echo "deps_hash=$deps_hash" >> "$GITHUB_OUTPUT" + + - name: Get Composer cache + id: composer-cache + uses: actions/cache/restore@v4 + with: + path: src/vendor + key: ${{ runner.os }}-php-8.2-composer-${{ steps.deps-hash.outputs.deps_hash }} + restore-keys: | + ${{ runner.os }}-php-8.2-composer- + + - name: Install Composer dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install -d src --no-progress --prefer-dist --optimize-autoloader + + - name: Save Composer cache + if: steps.composer-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: src/vendor + key: ${{ runner.os }}-php-8.2-composer-${{ steps.deps-hash.outputs.deps_hash }} + + - name: Run Stylelint + run: npm run lint:styles + + - name: Run ESLint + run: npm run lint:js + + - name: Run PHP CS + run: npm run lint:php diff --git a/eslint.config.mjs b/eslint.config.mjs index 63e623ca..34274913 100755 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -6,6 +6,7 @@ import eslintTs from 'typescript-eslint' import stylistic from '@stylistic/eslint-plugin' import reactHooks from 'eslint-plugin-react-hooks' import importPlugin from 'eslint-plugin-import' +import jsxA11yPlugin from 'eslint-plugin-jsx-a11y' import reactPlugin from 'eslint-plugin-react' import { FlatCompat } from '@eslint/eslintrc' @@ -21,6 +22,7 @@ export default eslintTs.config( ...compat.extends('plugin:react-hooks/recommended'), reactPlugin.configs.flat.recommended, importPlugin.flatConfigs.recommended, + jsxA11yPlugin.flatConfigs.recommended, { plugins: { 'react-hooks': reactHooks }, rules: reactHooks.configs.recommended.rules, @@ -67,7 +69,7 @@ export default eslintTs.config( '@stylistic/indent': ['error', 'tab', { SwitchCase: 1 }], '@stylistic/jsx-quotes': ['error', 'prefer-double'], '@stylistic/linebreak-style': ['error', 'unix'], - '@stylistic/max-len': ['warn', 140, { ignorePattern: 'd="(.*?)"|_[_xn]\\(|import .+ from .+' }], + '@stylistic/max-len': ['error', 140, { ignorePattern: 'd="(.*?)"|_[_xn]\\(|import .+ from .+' }], '@stylistic/multiline-ternary': 'off', '@stylistic/no-extra-parens': ['error', 'all', { ignoreJSX: 'multi-line' }], '@stylistic/no-mixed-spaces-and-tabs': ['error', 'smart-tabs'], @@ -127,7 +129,30 @@ export default eslintTs.config( 'prefer-named-capture-group': 'error', 'prefer-template': 'error', 'sort-imports': ['error', { ignoreDeclarationSort: true }], - 'yoda': ['error', 'always'] + 'yoda': ['error', 'always'], + // Accessibility rules. + 'jsx-a11y/alt-text': 'error', + 'jsx-a11y/anchor-has-content': 'error', + 'jsx-a11y/anchor-is-valid': 'error', + 'jsx-a11y/aria-props': 'error', + 'jsx-a11y/aria-proptypes': 'error', + 'jsx-a11y/aria-role': 'error', + 'jsx-a11y/aria-unsupported-elements': 'error', + 'jsx-a11y/click-events-have-key-events': 'error', + 'jsx-a11y/control-has-associated-label': 'warn', + 'jsx-a11y/heading-has-content': 'error', + 'jsx-a11y/iframe-has-title': 'error', + 'jsx-a11y/img-redundant-alt': 'error', + 'jsx-a11y/interactive-supports-focus': 'error', + 'jsx-a11y/label-has-associated-control': 'error', + 'jsx-a11y/no-autofocus': 'error', + 'jsx-a11y/no-noninteractive-element-interactions': 'error', + 'jsx-a11y/no-noninteractive-tabindex': 'error', + 'jsx-a11y/no-redundant-roles': 'error', + 'jsx-a11y/no-static-element-interactions': 'error', + 'jsx-a11y/role-has-required-aria-props': 'error', + 'jsx-a11y/role-supports-aria-props': 'error', + 'jsx-a11y/tabindex-no-positive': 'error' } }, { diff --git a/package-lock.json b/package-lock.json index 10d1aa31..9c17ed43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "react-select": "^5.10.0" }, "devDependencies": { + "@axe-core/playwright": "^4.11.2", "@eslint/eslintrc": "^3.3.3", "@eslint/js": "^9.20.0", "@playwright/test": "^1.48.0", @@ -54,6 +55,7 @@ "eslint": "^9.20.1", "eslint-import-resolver-typescript": "^3.7.0", "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.1.0", "eslint-webpack-plugin": "^4.2.0", @@ -129,6 +131,19 @@ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@axe-core/playwright": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.11.3.tgz", + "integrity": "sha512-h/kfksv4F0cVIDlKpT4700OehdRgpvuVskuQ2nb7/JmtWUXpe9ftHAPtwyXGvVSsa6SJ64A9ER7Zrzc/sIvC4w==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "axe-core": "~4.11.4" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "license": "MIT", @@ -2615,6 +2630,27 @@ "@parcel/watcher-win32-x64": "2.5.0" } }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@parcel/watcher-darwin-arm64": { "version": "2.5.0", "cpu": [ @@ -2634,6 +2670,255 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "dev": true, @@ -4764,6 +5049,16 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "dev": true, @@ -4922,6 +5217,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, "node_modules/astral-regex": { "version": "2.0.0", "dev": true, @@ -4999,6 +5301,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, "node_modules/axios": { "version": "1.13.5", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", @@ -5010,6 +5322,16 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/b4a": { "version": "1.6.6", "dev": true, @@ -6221,6 +6543,13 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/data-view-buffer": { "version": "1.0.2", "dev": true, @@ -7028,6 +7357,36 @@ "strip-bom": "^3.0.0" } }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, "node_modules/eslint-plugin-react": { "version": "7.37.5", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", @@ -9051,6 +9410,26 @@ "dev": true, "license": "MIT" }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/lazystream": { "version": "1.0.1", "dev": true, @@ -12068,6 +12447,21 @@ "dev": true, "license": "MIT" }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.12", "dev": true, diff --git a/package.json b/package.json index ca44ad34..4dca5023 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "react-select": "^5.10.0" }, "devDependencies": { + "@axe-core/playwright": "^4.11.2", "@eslint/eslintrc": "^3.3.3", "@eslint/js": "^9.20.0", "@playwright/test": "^1.48.0", @@ -96,6 +97,7 @@ "eslint": "^9.20.1", "eslint-import-resolver-typescript": "^3.7.0", "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.1.0", "eslint-webpack-plugin": "^4.2.0", diff --git a/src/js/components/ImportMenu/MigrateForm/MigrateForm.tsx b/src/js/components/ImportMenu/MigrateForm/MigrateForm.tsx index be660a48..5bbeafa7 100644 --- a/src/js/components/ImportMenu/MigrateForm/MigrateForm.tsx +++ b/src/js/components/ImportMenu/MigrateForm/MigrateForm.tsx @@ -50,7 +50,10 @@ const StatusMessages: React.FC = () => { {createInterpolateElement( __('Selected snippets have been successfully imported to your Code Snippets library.', 'code-snippets'), - { a: } + { + // eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label + a: + } )} )} diff --git a/src/js/components/ImportMenu/UploadForm/SelectSnippets/ImportResultDisplay.tsx b/src/js/components/ImportMenu/UploadForm/SelectSnippets/ImportResultDisplay.tsx index 069f5be2..f7321deb 100644 --- a/src/js/components/ImportMenu/UploadForm/SelectSnippets/ImportResultDisplay.tsx +++ b/src/js/components/ImportMenu/UploadForm/SelectSnippets/ImportResultDisplay.tsx @@ -43,7 +43,10 @@ export const ImportResultDisplay: React.FC = ({ succes

{createInterpolateElement( __('Go to All Snippets to activate your imported snippets.', 'code-snippets'), - { a: } + { + // eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label + a: + } )}

)} diff --git a/src/js/components/ImportMenu/UploadForm/UploadForm.tsx b/src/js/components/ImportMenu/UploadForm/UploadForm.tsx index 7c52fcee..9b11a08e 100644 --- a/src/js/components/ImportMenu/UploadForm/UploadForm.tsx +++ b/src/js/components/ImportMenu/UploadForm/UploadForm.tsx @@ -29,7 +29,10 @@ export const UploadForm: React.FC = () => {

{createInterpolateElement( __('Afterward, you will need to visit the All Snippets page to activate the imported snippets.', 'code-snippets'), - { a: } + { + // eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label + a: + } )}

diff --git a/src/js/components/ManageMenu/SnippetsTable/SnippetsListTable.tsx b/src/js/components/ManageMenu/SnippetsTable/SnippetsListTable.tsx index a8c49af4..3dcb43ca 100644 --- a/src/js/components/ManageMenu/SnippetsTable/SnippetsListTable.tsx +++ b/src/js/components/ManageMenu/SnippetsTable/SnippetsListTable.tsx @@ -244,7 +244,10 @@ const NoItemsMessage = () => { currentType ? __("It looks like you don't have any snippets of this type. Perhaps you would like to add a new one?", 'code-snippets') : __("It looks like you don't have any snippets. Perhaps you would like to add a new one?", 'code-snippets'), - { a: } + { + // eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label + a: + } )} } diff --git a/src/js/components/common/ListTable/TableItems.tsx b/src/js/components/common/ListTable/TableItems.tsx index 8c3393eb..0c9dc430 100644 --- a/src/js/components/common/ListTable/TableItems.tsx +++ b/src/js/components/common/ListTable/TableItems.tsx @@ -55,7 +55,9 @@ export interface TableItemsProps setSelected: Dispatch>> } -export const TableItems = ({ items, getKey, columns, noItems, selected, setSelected, rowClassName }: TableItemsProps) => +export const TableItems = ( + { items, getKey, columns, noItems, selected, setSelected, rowClassName }: TableItemsProps +) => 0 < items.length ? items.map(item => diff --git a/tests/e2e/helpers/SnippetsTestHelper.ts b/tests/e2e/helpers/SnippetsTestHelper.ts index 7933ae7f..040ecfaf 100644 --- a/tests/e2e/helpers/SnippetsTestHelper.ts +++ b/tests/e2e/helpers/SnippetsTestHelper.ts @@ -249,7 +249,7 @@ export class SnippetsTestHelper { * Click the "Add New" button to start creating a snippet */ async clickAddNewSnippet(): Promise { - await this.page.goto(URLS.ADD_SNIPPET) + await this.page.goto(URLS.ADD_SNIPPET_ADMIN) await this.page.waitForSelector(SELECTORS.TITLE_INPUT, { timeout: TIMEOUTS.DEFAULT }) } diff --git a/tests/e2e/helpers/constants.ts b/tests/e2e/helpers/constants.ts index 604a5a17..38efa6a4 100644 --- a/tests/e2e/helpers/constants.ts +++ b/tests/e2e/helpers/constants.ts @@ -26,7 +26,11 @@ export const TIMEOUTS = { export const URLS = { SNIPPETS_ADMIN: '/wp-admin/admin.php?page=snippets', - ADD_SNIPPET: '/wp-admin/admin.php?page=add-snippet', + COMMUNITY_CLOUD_ADMIN: '/wp-admin/admin.php?page=snippets&subpage=cloud-community', + ADD_SNIPPET_ADMIN: '/wp-admin/admin.php?page=add-snippet', + IMPORT_SNIPPETS_ADMIN: '/wp-admin/admin.php?page=import-code-snippets', + SETTINGS_ADMIN: '/wp-admin/admin.php?page=snippets-settings', + WELCOME_SCREEN_ADMIN: '/wp-admin/admin.php?page=code-snippets-welcome', FRONTEND: '/' }