diff --git a/.erb-lint.yml b/.erb-lint.yml new file mode 100644 index 0000000..be2bc7e --- /dev/null +++ b/.erb-lint.yml @@ -0,0 +1,4 @@ +--- +inherit_gem: + erblint-github: + - config/accessibility.yml diff --git a/.erb-linters/linters.rb b/.erb-linters/linters.rb new file mode 100644 index 0000000..c0e4b62 --- /dev/null +++ b/.erb-linters/linters.rb @@ -0,0 +1 @@ +require "erblint-github/linters" diff --git a/.github/workflows/accessibility-alt-text-bot.yml b/.github/workflows/accessibility-alt-text-bot.yml index 4b3c2b3..192edb8 100644 --- a/.github/workflows/accessibility-alt-text-bot.yml +++ b/.github/workflows/accessibility-alt-text-bot.yml @@ -23,4 +23,4 @@ jobs: if: ${{ github.event.issue || github.event.pull_request || github.event.discussion }} steps: - name: Get action 'github/accessibility-alt-text-bot' - uses: github/accessibility-alt-text-bot@v1.2.0 + uses: github/accessibility-alt-text-bot@v1.3.0 diff --git a/.github/workflows/auto-approve-and-merge.yml b/.github/workflows/auto-approve-and-merge.yml new file mode 100644 index 0000000..baa04d9 --- /dev/null +++ b/.github/workflows/auto-approve-and-merge.yml @@ -0,0 +1,28 @@ +name: Auto-approve and merge PR +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + auto_approve: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - name: Approve PR + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.rest.pulls.createReview({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + pull_number: context.payload.pull_request.number, + event: "APPROVE", + }) + - name: Enable auto-merge for PR + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da478ad..c21c19e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@v1 with: @@ -23,7 +23,7 @@ jobs: ruby_version: ["2.7", "3.0"] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@v1 with: @@ -36,7 +36,7 @@ jobs: docs-coverage: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@v1 with: @@ -49,7 +49,7 @@ jobs: tests-coverage: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@v1 with: diff --git a/Gemfile.lock b/Gemfile.lock index 1d6d837..989e808 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,24 +1,25 @@ PATH remote: . specs: - erblint-github (0.4.1) + erblint-github (0.5.0) GEM remote: https://rubygems.org/ specs: - actionview (7.0.5) - activesupport (= 7.0.5) + actionview (7.0.7.2) + activesupport (= 7.0.7.2) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activesupport (7.0.5) + activesupport (7.0.7.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) ast (2.4.2) - better_html (2.0.1) + base64 (0.1.1) + better_html (2.0.2) actionview (>= 6.0) activesupport (>= 6.0) ast (~> 2.0) @@ -28,7 +29,7 @@ GEM builder (3.2.4) concurrent-ruby (1.2.2) crass (1.0.6) - erb_lint (0.4.0) + erb_lint (0.5.0) activesupport better_html (>= 2.0.1) parser (>= 2.7.1.4) @@ -36,42 +37,47 @@ GEM rubocop smart_properties erubi (1.12.0) - i18n (1.13.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) json (2.6.3) + language_server-protocol (3.17.0.3) loofah (2.21.3) crass (~> 1.0.2) nokogiri (>= 1.12.0) - mini_portile2 (2.8.2) - minitest (5.18.0) - mocha (2.0.4) + mini_portile2 (2.8.4) + minitest (5.20.0) + mocha (2.1.0) ruby2_keywords (>= 0.0.5) - nokogiri (1.15.2) + nokogiri (1.15.4) mini_portile2 (~> 2.8.2) racc (~> 1.4) parallel (1.23.0) - parser (3.2.2.1) + parser (3.2.2.3) ast (~> 2.4.1) - racc (1.6.2) - rack (3.0.4.1) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + racc + racc (1.7.1) + rack (3.0.8) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) rainbow (3.1.1) rake (13.0.6) - regexp_parser (2.8.0) - rexml (3.2.5) - rubocop (1.52.0) + regexp_parser (2.8.1) + rexml (3.2.6) + rubocop (1.56.4) + base64 (~> 0.1.1) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.0.0) + parser (>= 3.2.2.3) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.0, < 2.0) + rubocop-ast (>= 1.28.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.29.0) @@ -92,18 +98,18 @@ GEM smart_properties (1.17.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.4.2) + unicode-display_width (2.5.0) PLATFORMS ruby DEPENDENCIES - erb_lint (~> 0.4.0) + erb_lint (~> 0.5.0) erblint-github! - minitest (~> 5.18.0) - mocha (~> 2.0.2) + minitest (~> 5.20.0) + mocha (~> 2.1.0) rake (~> 13.0.6) - rubocop (= 1.52.0) + rubocop (= 1.56.4) rubocop-github (~> 0.20.0) BUNDLED WITH diff --git a/bin/erblint-disable b/bin/erblint-disable new file mode 100755 index 0000000..d8ecf75 --- /dev/null +++ b/bin/erblint-disable @@ -0,0 +1,53 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "json" + +# Accepts comma-separated args with simple rule name +# e.g. script/erblint-disable SomeRule1,SomeRule2 +# e.g. script/erblint-disable GitHub::Accessibility::Rule1,SomeRule2 +rules = ARGV[0] + +rules_array = rules.split(",") +rules_map = {} +rules_array.each do |rule| + rule_underscored = rule.to_s.gsub("::", "/"). + gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2'). + gsub(/([a-z\d])([A-Z])/, '\1_\2'). + tr("-", "_"). + downcase. + gsub("git_hub", "github") # corrects rule names with `GitHub` name to align with erb-lint expectations + rules_map[rule] = rule_underscored +end + +rules_map.each do |disable_comment_name, command_line_name| + output = `bin/erblint --format json --enable-linters #{command_line_name} app/views app/components packages/**/app/components app/forms/**/` + hashed_output = JSON.parse(output) + hashed_output["files"].each do |file| + path = file["path"] + offenses = file["offenses"] + line_numbers = offenses.map do |offense| + offense["location"]["last_line"] + end + File.open(path, "r+") do |file| + lines = file.each_line.to_a + line_numbers.each do |line_number| + line = lines[line_number - 1] + unless line.match?(/erblint:disable (?.*#{disable_comment_name}).*/) + existing_disable = line.match(/(?<=# erblint:disable)(.*) (?=%>)/) + if existing_disable + existing_disable_string = existing_disable.captures[0] + add_new_disable = "#{existing_disable_string}, #{disable_comment_name}" + lines[line_number - 1] = lines[line_number - 1].gsub(existing_disable_string, add_new_disable) + else + lines[line_number - 1] = lines[line_number - 1].gsub("\n", "") + "<%# erblint:disable #{disable_comment_name} %>\n" + end + end + end + file.rewind + file.write(lines.join) + end + end +end + +exit 0 diff --git a/config/accessibility.yml b/config/accessibility.yml index e55a2cb..eeb9bc3 100644 --- a/config/accessibility.yml +++ b/config/accessibility.yml @@ -1,5 +1,7 @@ --- linters: + NoUnusedDisable: + enabled: true GitHub::Accessibility::AriaLabelIsWellFormatted: enabled: true GitHub::Accessibility::AvoidBothDisabledAndAriaDisabled: @@ -30,3 +32,5 @@ linters: enabled: true GitHub::Accessibility::SvgHasAccessibleText: enabled: true + GitHub::Accessibility::NoVisuallyHiddenInteractiveElements: + enabled: true diff --git a/docs/rules/accessibility/aria-label-is-well-formatted.md b/docs/rules/accessibility/aria-label-is-well-formatted.md index ff310d5..731b02d 100644 --- a/docs/rules/accessibility/aria-label-is-well-formatted.md +++ b/docs/rules/accessibility/aria-label-is-well-formatted.md @@ -11,6 +11,12 @@ Keep the following practices in mind: - Do not use line-break characters like ` `. An accessible name should be concise to start with. - Do not set the `aria-label` to a URL. Instead, use an appropriate human-friendly description. +### Be wary of `[aria-label]` on disallowed elements ⚠️ + +You may come across a scenario where `[aria-label]` is set on a generic `div` or `span`. This should also be flagged with the [NoAriaLabelMisuse](https://github.com/github/erblint-github/blob/main/docs/rules/accessibility/no-aria-label-misuse.md). + +In this scenario, prioritize removing the `aria-label`! + ## Resources - [Staff only: Guidance on naming controls](https://github.com/github/accessibility-playbook/blob/main/content/link-and-button-guidance.mdx#guidance-on-naming-controls) diff --git a/docs/rules/accessibility/navigation-has-label.md b/docs/rules/accessibility/navigation-has-label.md index a664b10..ccd48bb 100644 --- a/docs/rules/accessibility/navigation-has-label.md +++ b/docs/rules/accessibility/navigation-has-label.md @@ -2,9 +2,19 @@ ## Rule Details -This rule enforces that a navigation landmark (a `