-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add lint rule against .tooltipped and introduce accessibility.yml con…
…fig (#2086)
- Loading branch information
Showing
10 changed files
with
166 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@primer/view-components": minor | ||
--- | ||
|
||
Add lint rule against .tooltipped and introduce accessibility.yml config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Accessibility-focused lint rule configurations, tracked by accessibility scorecard. | ||
--- | ||
linters: | ||
NoUnusedDisable: | ||
enabled: true | ||
Primer::Accessibility::TooltippedMigration: | ||
enabled: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
lib/primer/view_components/linters/helpers/rule_helpers.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# frozen_string_literal: true | ||
|
||
module ERBLint | ||
module Linters | ||
module Helpers | ||
# Helpers for writing rules | ||
module RuleHelpers | ||
def tags(processed_source) | ||
processed_source.parser.nodes_with_type(:tag).map { |tag_node| BetterHtml::Tree::Tag.from_node(tag_node) } | ||
end | ||
|
||
def generate_offense(klass, processed_source, tag, message = nil, replacement = nil) | ||
message ||= klass::MESSAGE | ||
klass_name = klass.name.demodulize | ||
offense = ["#{klass_name}:#{message}", tag.node.loc.source].join("\n") | ||
add_offense(processed_source.to_source_range(tag.loc), offense, replacement) | ||
end | ||
|
||
# Generate offense for ERB node tag | ||
def generate_node_offense(klass, processed_source, node, message = nil) | ||
message ||= klass::MESSAGE | ||
offense = ["#{klass.name}:#{message}", node.loc.source].join("\n") | ||
add_offense(processed_source.to_source_range(node.loc), offense) | ||
end | ||
|
||
def erb_nodes(processed_source) | ||
processed_source.parser.ast.descendants(:erb) | ||
end | ||
|
||
def extract_ruby_from_erb_node(erb_node) | ||
return unless erb_node.type == :erb | ||
|
||
_, _, code_node = *erb_node | ||
code_node.loc.source.strip | ||
end | ||
end | ||
end | ||
end | ||
end |
37 changes: 37 additions & 0 deletions
37
lib/primer/view_components/linters/tooltipped_migration.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative "helpers/rule_helpers" | ||
module ERBLint | ||
module Linters | ||
module Primer | ||
module Accessibility | ||
# Flag when `.tooltipped` is being used and offer alternatives. | ||
class TooltippedMigration < Linter | ||
include LinterRegistry | ||
include Helpers::RuleHelpers | ||
|
||
MIGRATE_TO_NEWER_TOOLTIP = ".tooltipped has been deprecated. Tooltip should never be set on a non-interactive element (e.g. <span>, <div>, <li>). " \ | ||
"Find alternatives in https://primer.style/design/guides/accessibility/tooltip-alternatives/. " \ | ||
"If you're setting a tooltip on an interactive element, use the latest tooltip in https://primer.style/view-components." | ||
TOOLTIPPED_RUBY_PATTERN = /classes:.*tooltipped|class:.*tooltipped/.freeze | ||
|
||
def run(processed_source) | ||
# HTML tags | ||
tags(processed_source).each do |tag| | ||
next if tag.closing? | ||
|
||
classes = tag.attributes["class"]&.value | ||
generate_offense(self.class, processed_source, tag, MIGRATE_TO_NEWER_TOOLTIP) if classes&.include?("tooltipped") | ||
end | ||
|
||
# ERB nodes | ||
erb_nodes(processed_source).each do |node| | ||
code = extract_ruby_from_erb_node(node) | ||
generate_node_offense(self.class, processed_source, node, MIGRATE_TO_NEWER_TOOLTIP) if code.match?(TOOLTIPPED_RUBY_PATTERN) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# frozen_string_literal: true | ||
|
||
require "lib/erblint_test_case" | ||
|
||
class LinterConfigWorksTest < ErblintTestCase | ||
# The ability to share rules and configs from other gems in erb_lint is not well-documented. | ||
# This test validates that our recommended setup works. | ||
def test_asserts_recommended_setup_works | ||
erb_lint_config = ERBLint::RunnerConfig.new(file_loader.yaml(".erb-lint.yml"), file_loader) | ||
|
||
rules_enabled_in_accessibility_config = 0 | ||
erb_lint_config.to_hash["linters"].each do |linter| | ||
rules_enabled_in_accessibility_config += 1 if linter[0].include?("Primer::Accessibility") && linter[1]["enabled"] == true | ||
end | ||
known_linter_names ||= ERBLint::LinterRegistry.linters.map(&:simple_name) | ||
known_linter_names_count = known_linter_names.count { |linter| linter.include?("Primer::Accessibility") } | ||
assert_equal 1, rules_enabled_in_accessibility_config | ||
assert_equal 1, known_linter_names_count | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# frozen_string_literal: true | ||
|
||
require "lib/erblint_test_case" | ||
|
||
class TooltippedMigrationTest < ErblintTestCase | ||
def linter_class | ||
ERBLint::Linters::Primer::Accessibility::TooltippedMigration | ||
end | ||
|
||
def test_warns_if_tooltipped_class_is_used_on_html_tag | ||
@file = "<li class='tooltipped'>List item</li>" | ||
@linter.run(processed_source) | ||
assert_equal 1, @linter.offenses.count | ||
assert_match(/.tooltipped has been deprecated./, @linter.offenses.first.message) | ||
end | ||
|
||
def test_warns_if_tooltipped_class_is_used_on_view_component | ||
@file = <<~HTML | ||
<%= content_tag :span, | ||
class: "tooltipped tooltipped-nw cursor-pointer" do %> | ||
HTML | ||
@linter.run(processed_source) | ||
assert_equal 1, @linter.offenses.count | ||
assert_match(/.tooltipped has been deprecated./, @linter.offenses.first.message) | ||
end | ||
|
||
def test_warns_if_tooltipped_class_is_used_on_ruby | ||
@file = '<%= render SomeComponent.new(classes: "tooltipped tooltipped-multiline tooltipped-s") do %>' | ||
@linter.run(processed_source) | ||
assert_equal 1, @linter.offenses.count | ||
assert_match(/.tooltipped has been deprecated./, @linter.offenses.first.message) | ||
end | ||
|
||
def test_does_not_warn_if_tooltipped_is_not_used | ||
@file = "<%= render SomeComponent.new do %>" | ||
@linter.run(processed_source) | ||
assert_equal 0, @linter.offenses.count | ||
end | ||
|
||
def test_does_not_warn_if_inline_disable_comment | ||
@file = <<~HTML | ||
<%= render SomeComponent.new(classes: "tooltipped") do %><%# erblint:disable Primer::Accessibility::TooltippedMigration %> | ||
HTML | ||
@linter.run_and_update_offense_status(processed_source) | ||
offenses = @linter.offenses.reject(&:disabled?) # Reject offenses marked as disabled. (This happens at the runner level so we repliace it here) | ||
assert_equal 0, offenses.count | ||
end | ||
end |