Skip to content

Commit 5c37828

Browse files
author
Adrián Bolonio
authored
Merge pull request #42 from github/accessibility/issues/1293/migrate-a11y-erblint-rules-a11y-nested-interactive-elements
Migrate accessibility rule `a11y_nested_interactive_elements` from dotcom to erblint-github
2 parents 63dd8ea + a2795f4 commit 5c37828

File tree

4 files changed

+147
-3
lines changed

4 files changed

+147
-3
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ linters:
3535
enabled: true
3636
GitHub::Accessibility::LinkHasHrefCounter:
3737
enabled: true
38+
GitHub::Accessibility::NestedInteractiveElementsCounter:
39+
enabled: true
3840
GitHub::Accessibility::NoAriaLabelMisuseCounter:
3941
enabled: true
4042
GitHub::Accessibility::NoPositiveTabIndex:
@@ -54,12 +56,13 @@ linters:
5456
- [GitHub::Accessibility::DisabledAttributeCounter](./docs/rules/accessibility/disabled-attribute-counter-test)
5557
- [GitHub::Accessibility::IframeHasTitle](./docs/rules/accessibility/iframe-has-title.md)
5658
- [GitHub::Accessibility::ImageHasAlt](./docs/rules/accessibility/image-has-alt.md)
57-
- [GitHub::Accessibility::LinkHasHrefCounter](./docs/rules/accessibility/link_has_href-counter.md)
59+
- [GitHub::Accessibility::LinkHasHrefCounter](./docs/rules/accessibility/link-has-href-counter.md)
60+
- [GitHub::Accessibility::NestedInteractiveElementsCounter](./docs/rules/accessibility/nested-interactive-elements-counter.md)
5861
- [GitHub::Accessibility::NoAriaLabelMisuseCounter](./docs/rules/accessibility/no-aria-label-misuse-counter.md)
5962
- [GitHub::Accessibility::NoPositiveTabIndex](./docs/rules/accessibility/no-positive-tab-index.md)
6063
- [GitHub::Accessibility::NoRedundantImageAlt](./docs/rules/accessibility/no-redundant-image-alt.md)
6164
- [GitHub::Accessibility::NoTitleAttributeCounter](./docs/rules/accessibility/no-title-attribute-counter.md)
62-
- [GitHub::Accessibility::SvgHasAccessibleTextCounter](./docs/rules/accessibility/svg_has_accessible_text_counter.md)
65+
- [GitHub::Accessibility::SvgHasAccessibleTextCounter](./docs/rules/accessibility/svg-has-accessible-text-counter.md)
6366
6467
## Testing
6568
@@ -75,4 +78,4 @@ If you use VS Code, we highly encourage [ERB Linter extension](https://marketpla
7578
## Note
7679

7780
This repo contains several accessibility-related linting rules to help surface accessibility issues that would otherwise go undetected until a later stage. Please note that due to the limitations of static code analysis,
78-
these ERB accessibility checks are NOT enough for ensuring the accessibility of your app. This shouldn't be the only tool you use to catch accessibility issues and should be supplemented with other tools that can check the runtime browser DOM output, as well as processes like accessibility design reviews, manual audits, user testing, etc.
81+
these ERB accessibility checks are NOT enough for ensuring the accessibility of your app. This shouldn't be the only tool you use to catch accessibility issues and should be supplemented with other tools that can check the runtime browser DOM output, as well as processes like accessibility design reviews, manual audits, user testing, etc.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Nested Interactive Elements Counter
2+
3+
## Rule Details
4+
5+
Certain interactive controls such as `button`, `summary`, `input`, `select`, `textarea`, or `a` can't have interactive children. Nesting interactive elements produces invalid HTML, and ssistive technologies, such as screen readers, might ignore or respond unexpectedly to such nested controls.
6+
7+
## Resources
8+
9+
- [Deque University](https://dequeuniversity.com/rules/axe/4.2/nested-interactive)
10+
- [Accessibility Insights](https://accessibilityinsights.io/info-examples/web/nested-interactive/)
11+
12+
## Examples
13+
### **Incorrect** code for this rule 👎
14+
15+
```erb
16+
<!-- incorrect -->
17+
<button>
18+
<a href='https://github.com/'>Go to GitHub</a>
19+
</button>
20+
```
21+
22+
### **Correct** code for this rule 👍
23+
24+
```erb
25+
<!-- correct -->
26+
<button>Confirm</button>
27+
```
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../../custom_helpers"
4+
5+
module ERBLint
6+
module Linters
7+
module GitHub
8+
module Accessibility
9+
class NestedInteractiveElementsCounter < Linter
10+
include ERBLint::Linters::CustomHelpers
11+
include LinterRegistry
12+
13+
INTERACTIVE_ELEMENTS = %w[button summary input select textarea a].freeze
14+
MESSAGE = "Nesting interactive elements produces invalid HTML, and ssistive technologies, such as screen readers, might ignore or respond unexpectedly to such nested controls."
15+
16+
def run(processed_source)
17+
last_interactive_element = nil
18+
tags(processed_source).each do |tag|
19+
next unless INTERACTIVE_ELEMENTS.include?(tag.name)
20+
21+
last_interactive_element = nil if last_interactive_element && tag.name == last_interactive_element.name && tag.closing?
22+
next if tag.closing?
23+
24+
if last_interactive_element
25+
next if last_interactive_element.name == "summary" && tag.name == "a"
26+
next if tag.name == "input" && tag.attributes["type"]&.value == "hidden"
27+
28+
message = "Found <#{tag.name}> nested inside of <#{last_interactive_element.name}>.\n" + MESSAGE
29+
generate_offense(self.class, processed_source, tag, message)
30+
end
31+
32+
last_interactive_element = tag unless tag&.name == "input"
33+
end
34+
35+
counter_correct?(processed_source)
36+
end
37+
38+
def autocorrect(processed_source, offense)
39+
return unless offense.context
40+
41+
lambda do |corrector|
42+
if processed_source.file_content.include?("erblint:counter #{simple_class_name}")
43+
# update the counter if exists
44+
corrector.replace(offense.source_range, offense.context)
45+
else
46+
# add comment with counter if none
47+
corrector.insert_before(processed_source.source_buffer.source_range, "#{offense.context}\n")
48+
end
49+
end
50+
end
51+
end
52+
end
53+
end
54+
end
55+
end
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
5+
class NestedInteractiveElementsCounter < LinterTestCase
6+
def linter_class
7+
ERBLint::Linters::GitHub::Accessibility::NestedInteractiveElementsCounter
8+
end
9+
10+
def test_warns_if_there_are_nested_interactive_elements
11+
@file = "<button><a href='https://github.com/'>Go to GitHub</a></button>"
12+
@linter.run(processed_source)
13+
14+
assert_equal(2, @linter.offenses.count)
15+
error_messages = @linter.offenses.map(&:message).sort
16+
assert_match(/If you must, add <%# erblint:counter GitHub::Accessibility::NestedInteractiveElementsCounter 1 %> to bypass this check./, error_messages.first)
17+
assert_match(/Nesting interactive elements produces invalid HTML, and ssistive technologies, such as screen readers, might ignore or respond unexpectedly to such nested controls./, error_messages.last)
18+
end
19+
20+
def test_does_not_warn_if_there_are_not_nested_interactive_elements
21+
@file = "<button>Confirm</button>"
22+
@linter.run(processed_source)
23+
24+
assert_empty @linter.offenses
25+
end
26+
27+
def test_does_not_warn_if_there_are_not_nested_interactive_elements_and_has_correct_counter_comment
28+
@file = <<~ERB
29+
<%# erblint:counter GitHub::Accessibility::NestedInteractiveElementsCounter 1 %>
30+
<button><a href='https://github.com/'>Go to GitHub</a></button>
31+
ERB
32+
@linter.run(processed_source)
33+
34+
assert_equal 0, @linter.offenses.count
35+
end
36+
37+
def test_does_not_autocorrect_when_ignores_are_correct
38+
@file = <<~ERB
39+
<%# erblint:counter GitHub::Accessibility::NestedInteractiveElementsCounter 1 %>
40+
<button><a href='https://github.com/'>Go to GitHub</a></button>
41+
ERB
42+
43+
assert_equal @file, corrected_content
44+
end
45+
46+
def test_does_autocorrect_when_ignores_are_not_correct
47+
@file = <<~ERB
48+
<%# erblint:counter GitHub::Accessibility::NestedInteractiveElementsCounter 3 %>
49+
<button><a href='https://github.com/'>Go to GitHub</a></button>
50+
ERB
51+
refute_equal @file, corrected_content
52+
53+
expected_content = <<~ERB
54+
<%# erblint:counter GitHub::Accessibility::NestedInteractiveElementsCounter 1 %>
55+
<button><a href='https://github.com/'>Go to GitHub</a></button>
56+
ERB
57+
assert_equal expected_content, corrected_content
58+
end
59+
end

0 commit comments

Comments
 (0)