erblint-github 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -2
- data/lib/erblint-github/linters/custom_helpers.rb +2 -20
- data/lib/erblint-github/linters/github/accessibility/aria_label_is_well_formatted.rb +37 -0
- data/lib/erblint-github/linters/github/accessibility/navigation_has_label.rb +33 -0
- metadata +8 -7
- data/lib/erblint-github/linters/github/accessibility/landmark_has_label.rb +0 -68
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b2a29a79e8d05ae92a33bff53b8a210d3ae35658e4e331fb3f7b7c9ca259147
|
4
|
+
data.tar.gz: 6e551b63af42992248a0b1965e50d9954c44e3596bc5feed0ba6a2eb7399d1bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62bd97d7ece9d2d9082d1424e086b542f25a5b736ee7664636811376cd87b08c2420cbb1c3fef0ab0b769511c6e520d8769c51380647a373efa95b241d8db393
|
7
|
+
data.tar.gz: a8c9be52a3b99be842df8b5d34388f3ee8ff2629e369deaaefdea206520440a1e3a5fc18e1a74b5fa2fd774ffd2a6586476ad8dbfb965a3ea9b8dd5dd053b099
|
data/README.md
CHANGED
@@ -23,6 +23,8 @@ require "erblint-github/linters"
|
|
23
23
|
```yaml
|
24
24
|
---
|
25
25
|
linters:
|
26
|
+
GitHub::Accessibility::AriaLabelIsWellFormatted:
|
27
|
+
enabled: true
|
26
28
|
GitHub::Accessibility::AvoidBothDisabledAndAriaDisabled:
|
27
29
|
enabled: true
|
28
30
|
GitHub::Accessibility::AvoidGenericLinkText:
|
@@ -33,7 +35,7 @@ linters:
|
|
33
35
|
enabled: true
|
34
36
|
GitHub::Accessibility::ImageHasAlt:
|
35
37
|
enabled: true
|
36
|
-
GitHub::Accessibility::
|
38
|
+
GitHub::Accessibility::NavigationHasLabel:
|
37
39
|
enabled: true
|
38
40
|
GitHub::Accessibility::LinkHasHref:
|
39
41
|
enabled: true
|
@@ -55,10 +57,11 @@ linters:
|
|
55
57
|
|
56
58
|
## Rules
|
57
59
|
|
60
|
+
- [GitHub::Accessibility::AriaLabelIsWellFormatted](./docs/rules/accessibility/aria-label-is-well-formatted.md)
|
58
61
|
- [GitHub::Accessibility::AvoidBothDisabledAndAriaDisabled](./docs/rules/accessibility/avoid-both-disabled-and-aria-disabled.md)
|
59
62
|
- [GitHub::Accessibility::AvoidGenericLinkText](./docs/rules/accessibility/avoid-generic-link-text.md)
|
60
63
|
- [GitHub::Accessibility::DisabledAttribute](./docs/rules/accessibility/disabled-attribute.md)
|
61
|
-
- [GitHub::Accessibility::
|
64
|
+
- [GitHub::Accessibility::NavigationHasLabel](./docs/rules/accessibility/navigation-has-label.md)
|
62
65
|
- [GitHub::Accessibility::LinkHasHref](./docs/rules/accessibility/link-has-href.md)
|
63
66
|
- [GitHub::Accessibility::NestedInteractiveElements](./docs/rules/accessibility/nested-interactive-elements.md)
|
64
67
|
- [GitHub::Accessibility::IframeHasTitle](./docs/rules/accessibility/iframe-has-title.md)
|
@@ -8,24 +8,6 @@ module ERBLint
|
|
8
8
|
module CustomHelpers
|
9
9
|
INTERACTIVE_ELEMENTS = %w[button summary input select textarea a].freeze
|
10
10
|
|
11
|
-
def rule_disabled?(processed_source)
|
12
|
-
processed_source.parser.ast.descendants(:erb).each do |node|
|
13
|
-
indicator_node, _, code_node, = *node
|
14
|
-
indicator = indicator_node&.loc&.source
|
15
|
-
comment = code_node&.loc&.source&.strip
|
16
|
-
rule_name = simple_class_name
|
17
|
-
|
18
|
-
if indicator == "#" && comment.start_with?("erblint:disable") && comment.match(rule_name)
|
19
|
-
if @offenses.any?
|
20
|
-
clear_offenses
|
21
|
-
else
|
22
|
-
add_offense(processed_source.to_source_range(code_node.loc),
|
23
|
-
"Unused erblint:disable comment for #{rule_name}")
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
11
|
def counter_correct?(processed_source)
|
30
12
|
comment_node = nil
|
31
13
|
expected_count = 0
|
@@ -52,10 +34,10 @@ module ERBLint
|
|
52
34
|
first_offense = @offenses[0]
|
53
35
|
|
54
36
|
if comment_node.nil?
|
55
|
-
add_offense(processed_source.to_source_range(first_offense.source_range), "#{rule_name}: If you must, add <%# erblint:
|
37
|
+
add_offense(processed_source.to_source_range(first_offense.source_range), "#{rule_name}: If you must, add <%# erblint:disable #{rule_name} %> at the end of the offending line to bypass this check. See https://github.com/shopify/erb-lint#disable-rule-at-offense-level", "<%# erblint:disable #{rule_name} %>")
|
56
38
|
else
|
57
39
|
clear_offenses
|
58
|
-
add_offense(processed_source.to_source_range(comment_node.loc), "Incorrect erblint:counter number for #{rule_name}. Expected: #{expected_count}, actual: #{offenses_count}.", "<%# erblint:counter #{rule_name} #{offenses_count} %>") if expected_count != offenses_count
|
40
|
+
add_offense(processed_source.to_source_range(comment_node.loc), "Incorrect erblint:counter number for #{rule_name}. Expected: #{expected_count}, actual: #{offenses_count}.", "<%# erblint:counter #{rule_name}Counter #{offenses_count} %>") if expected_count != offenses_count
|
59
41
|
end
|
60
42
|
end
|
61
43
|
|
@@ -0,0 +1,37 @@
|
|
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 AriaLabelIsWellFormatted < Linter
|
10
|
+
include ERBLint::Linters::CustomHelpers
|
11
|
+
include LinterRegistry
|
12
|
+
|
13
|
+
MESSAGE = "[aria-label] text should be formatted the same as you would visual text. Use sentence case."
|
14
|
+
|
15
|
+
class ConfigSchema < LinterConfig
|
16
|
+
property :exceptions, accepts: array_of?(String),
|
17
|
+
default: -> { [] }
|
18
|
+
end
|
19
|
+
self.config_schema = ConfigSchema
|
20
|
+
|
21
|
+
def run(processed_source)
|
22
|
+
tags(processed_source).each do |tag|
|
23
|
+
next if tag.closing?
|
24
|
+
|
25
|
+
aria_label = possible_attribute_values(tag, "aria-label").join
|
26
|
+
next if aria_label.empty?
|
27
|
+
|
28
|
+
if aria_label.match?(/^[a-z]/) && !@config.exceptions.include?(aria_label)
|
29
|
+
generate_offense(self.class, processed_source, tag)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,33 @@
|
|
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 NavigationHasLabel < Linter
|
10
|
+
include ERBLint::Linters::CustomHelpers
|
11
|
+
include LinterRegistry
|
12
|
+
|
13
|
+
MESSAGE = "The navigation landmark should have a unique accessible name via `aria-label` or `aria-labelledby`. Remember that the name does not need to include `navigation` or `nav` since it will already be announced."
|
14
|
+
|
15
|
+
def run(processed_source)
|
16
|
+
tags(processed_source).each do |tag|
|
17
|
+
next if tag.closing?
|
18
|
+
next unless possible_attribute_values(tag, "role").include?("navigation") || tag.name == "nav"
|
19
|
+
if possible_attribute_values(tag, "aria-label").empty? && possible_attribute_values(tag, "aria-labelledby").empty?
|
20
|
+
message = MESSAGE
|
21
|
+
if tag.name != "nav"
|
22
|
+
message += "Additionally, you can safely drop the `role='navigation'` and replace it with the native HTML `nav` element."
|
23
|
+
end
|
24
|
+
generate_offense(self.class, processed_source, tag, message)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erblint-github
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: erb_lint
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 5.
|
33
|
+
version: 5.18.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 5.
|
40
|
+
version: 5.18.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: mocha
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - '='
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 1.
|
75
|
+
version: 1.49.0
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 1.
|
82
|
+
version: 1.49.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rubocop-github
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -105,13 +105,14 @@ files:
|
|
105
105
|
- README.md
|
106
106
|
- lib/erblint-github/linters.rb
|
107
107
|
- lib/erblint-github/linters/custom_helpers.rb
|
108
|
+
- lib/erblint-github/linters/github/accessibility/aria_label_is_well_formatted.rb
|
108
109
|
- lib/erblint-github/linters/github/accessibility/avoid_both_disabled_and_aria_disabled.rb
|
109
110
|
- lib/erblint-github/linters/github/accessibility/avoid_generic_link_text.rb
|
110
111
|
- lib/erblint-github/linters/github/accessibility/disabled_attribute.rb
|
111
112
|
- lib/erblint-github/linters/github/accessibility/iframe_has_title.rb
|
112
113
|
- lib/erblint-github/linters/github/accessibility/image_has_alt.rb
|
113
|
-
- lib/erblint-github/linters/github/accessibility/landmark_has_label.rb
|
114
114
|
- lib/erblint-github/linters/github/accessibility/link_has_href.rb
|
115
|
+
- lib/erblint-github/linters/github/accessibility/navigation_has_label.rb
|
115
116
|
- lib/erblint-github/linters/github/accessibility/nested_interactive_elements.rb
|
116
117
|
- lib/erblint-github/linters/github/accessibility/no_aria_hidden_on_focusable.rb
|
117
118
|
- lib/erblint-github/linters/github/accessibility/no_aria_label_misuse.rb
|
@@ -1,68 +0,0 @@
|
|
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 LandmarkHasLabel < Linter
|
10
|
-
include ERBLint::Linters::CustomHelpers
|
11
|
-
include LinterRegistry
|
12
|
-
|
13
|
-
LANDMARK_ROLES = %w[complementary navigation region search].freeze
|
14
|
-
LANDMARK_TAGS = %w[aside nav section].freeze
|
15
|
-
MESSAGE = "Landmark elements should have an aria-label attribute, or aria-labelledby if a heading elements exists in the landmark."
|
16
|
-
ROLE_TAG_MAPPING = { "complementary" => "aside", "navigation" => "nav", "region" => "section" }.freeze
|
17
|
-
|
18
|
-
def get_additional_message(tag, roles)
|
19
|
-
role_matched = (roles & ROLE_TAG_MAPPING.keys).first
|
20
|
-
if role_matched
|
21
|
-
tag_matched = ROLE_TAG_MAPPING[role_matched]
|
22
|
-
|
23
|
-
if tag.name == tag_matched
|
24
|
-
"The <#{tag_matched}> element will automatically communicate a role of '#{role_matched}'. You can safely drop the role attribute."
|
25
|
-
else
|
26
|
-
replace_message = if tag.name == "div"
|
27
|
-
"If possible replace this tag with a <#{tag_matched}>."
|
28
|
-
else
|
29
|
-
"Wrapping this element in a <#{tag_matched}> and setting a label on it is reccomended."
|
30
|
-
end
|
31
|
-
|
32
|
-
"The <#{tag_matched}> element will automatically communicate a role of '#{role_matched}'. #{replace_message}"
|
33
|
-
end
|
34
|
-
elsif roles.include?("search") && tag.name != "form"
|
35
|
-
"The 'search' role works best when applied to a <form> element. If possible replace this tag with a <form>."
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class ConfigSchema < LinterConfig
|
40
|
-
property :counter_enabled, accepts: [true, false], default: false, reader: :counter_enabled?
|
41
|
-
end
|
42
|
-
self.config_schema = ConfigSchema
|
43
|
-
|
44
|
-
def run(processed_source)
|
45
|
-
tags(processed_source).each do |tag|
|
46
|
-
next if tag.closing?
|
47
|
-
|
48
|
-
possible_roles = possible_attribute_values(tag, "role")
|
49
|
-
next unless LANDMARK_TAGS.include?(tag.name) && (possible_roles & LANDMARK_ROLES).empty?
|
50
|
-
next if tag.attributes["aria-label"]&.value&.present? || tag.attributes["aria-labelledby"]&.value&.present?
|
51
|
-
|
52
|
-
message = get_additional_message(tag, possible_roles)
|
53
|
-
if message
|
54
|
-
generate_offense(self.class, processed_source, tag, "#{MESSAGE}\n#{message}")
|
55
|
-
else
|
56
|
-
generate_offense(self.class, processed_source, tag)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
if @config.counter_enabled?
|
61
|
-
counter_correct?(processed_source)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|