primer_view_components 0.0.49 → 0.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +158 -0
- data/app/components/primer/base_component.rb +2 -2
- data/app/components/primer/beta/avatar_stack.rb +9 -9
- data/app/components/primer/beta/truncate.html.erb +5 -0
- data/app/components/primer/beta/truncate.rb +110 -0
- data/app/components/primer/border_box_component.rb +27 -1
- data/app/components/primer/clipboard_copy.html.erb +2 -2
- data/app/components/primer/clipboard_copy.rb +1 -1
- data/app/components/primer/dropdown.rb +7 -7
- data/app/components/primer/icon_button.rb +1 -1
- data/app/components/primer/label_component.rb +13 -12
- data/app/components/primer/navigation/tab_component.rb +1 -1
- data/app/components/primer/progress_bar_component.rb +0 -3
- data/app/components/primer/tab_nav_component.rb +1 -1
- data/app/lib/primer/fetch_or_fallback_helper.rb +2 -0
- data/app/lib/primer/octicon/cache.rb +1 -1
- data/app/lib/primer/tabbed_component_helper.rb +1 -1
- data/app/lib/primer/view_helper.rb +1 -0
- data/lib/primer/classify/cache.rb +0 -5
- data/lib/primer/classify/flex.rb +1 -1
- data/lib/primer/classify/functional_colors.rb +1 -1
- data/lib/primer/classify/utilities.rb +19 -2
- data/lib/primer/classify/utilities.yml +16 -0
- data/lib/primer/classify/validation.rb +18 -0
- data/lib/primer/classify.rb +4 -18
- data/lib/primer/view_components/constants.rb +1 -1
- data/lib/primer/view_components/linters/argument_mappers/base.rb +63 -2
- data/lib/primer/view_components/linters/argument_mappers/button.rb +7 -11
- data/lib/primer/view_components/linters/argument_mappers/clipboard_copy.rb +2 -6
- data/lib/primer/view_components/linters/argument_mappers/close_button.rb +43 -0
- data/lib/primer/view_components/linters/argument_mappers/flash.rb +32 -0
- data/lib/primer/view_components/linters/argument_mappers/helpers/erb_block.rb +67 -0
- data/lib/primer/view_components/linters/argument_mappers/label.rb +5 -12
- data/lib/primer/view_components/linters/argument_mappers/system_arguments.rb +6 -5
- data/lib/primer/view_components/linters/autocorrectable.rb +6 -4
- data/lib/primer/view_components/linters/{helpers.rb → base_linter.rb} +69 -29
- data/lib/primer/view_components/linters/button_component_migration_counter.rb +4 -3
- data/lib/primer/view_components/linters/clipboard_copy_component_migration_counter.rb +3 -4
- data/lib/primer/view_components/linters/close_button_component_migration_counter.rb +123 -0
- data/lib/primer/view_components/linters/flash_component_migration_counter.rb +18 -3
- data/lib/primer/view_components/linters/label_component_migration_counter.rb +2 -3
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/rubocop/config/default.yml +5 -0
- data/lib/rubocop/cop/primer/deprecated_arguments.rb +173 -0
- data/lib/rubocop/cop/primer/no_tag_memoize.rb +1 -0
- data/lib/rubocop/cop/primer/primer_octicon.rb +178 -0
- data/lib/rubocop/cop/primer/system_argument_instead_of_class.rb +12 -16
- data/lib/rubocop/cop/primer.rb +1 -2
- data/lib/tasks/coverage.rake +4 -0
- data/lib/tasks/docs.rake +3 -2
- data/lib/tasks/utilities.rake +7 -3
- data/lib/yard/docs_helper.rb +6 -3
- data/static/arguments.yml +7 -4
- data/static/classes.yml +8 -0
- data/static/constants.json +13 -1
- data/static/statuses.json +3 -1
- metadata +32 -9
@@ -1,18 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "base_linter"
|
4
4
|
require_relative "autocorrectable"
|
5
5
|
require_relative "argument_mappers/clipboard_copy"
|
6
6
|
|
7
7
|
module ERBLint
|
8
8
|
module Linters
|
9
9
|
# Counts the number of times a HTML clipboard-copy is used instead of the component.
|
10
|
-
class ClipboardCopyComponentMigrationCounter <
|
11
|
-
include Helpers
|
10
|
+
class ClipboardCopyComponentMigrationCounter < BaseLinter
|
12
11
|
include Autocorrectable
|
13
12
|
|
14
13
|
TAGS = %w[clipboard-copy].freeze
|
15
|
-
|
14
|
+
REQUIRED_ARGUMENTS = [/for|value/, "aria-label"].freeze
|
16
15
|
MESSAGE = "We are migrating clipboard-copy to use [Primer::ClipboardCopy](https://primer.style/view-components/components/clipboardcopy), please try to use that instead of raw HTML."
|
17
16
|
ARGUMENT_MAPPER = ArgumentMappers::ClipboardCopy
|
18
17
|
COMPONENT = "Primer::ClipboardCopy"
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_linter"
|
4
|
+
require_relative "autocorrectable"
|
5
|
+
require_relative "argument_mappers/close_button"
|
6
|
+
|
7
|
+
module ERBLint
|
8
|
+
module Linters
|
9
|
+
# Counts the number of times a HTML clipboard-copy is used instead of the component.
|
10
|
+
class CloseButtonComponentMigrationCounter < BaseLinter
|
11
|
+
include Autocorrectable
|
12
|
+
|
13
|
+
TAGS = %w[button].freeze
|
14
|
+
CLASSES = %w[close-button].freeze
|
15
|
+
MESSAGE = "We are migrating close-button to use [Primer::CloseButton](https://primer.style/view-components/components/closebutton), please try to use that instead of raw HTML."
|
16
|
+
ARGUMENT_MAPPER = ArgumentMappers::CloseButton
|
17
|
+
COMPONENT = "Primer::CloseButton"
|
18
|
+
|
19
|
+
ALLOWED_OCTICON_ARGS = %w[icon aria-label aria].freeze
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def map_arguments(tag, tag_tree)
|
24
|
+
# We can only autocorrect cases where the tag only has an octicon as content.
|
25
|
+
return if tag_tree[:children].size != 1
|
26
|
+
|
27
|
+
nodes = tag_tree[:children].first.children
|
28
|
+
erb_nodes = nodes.select { |node| node.try(:type) == :erb }
|
29
|
+
|
30
|
+
# Don't correct if there are multiple ERB nodes.
|
31
|
+
return if erb_nodes.size != 1
|
32
|
+
|
33
|
+
_, _, code_node = *erb_nodes.first
|
34
|
+
code = code_node.children.first.strip
|
35
|
+
ast = erb_ast(code)
|
36
|
+
|
37
|
+
# We'll only autocorrect cases where the only content is an octicon.
|
38
|
+
if ast.method_name == :primer_octicon || ast.method_name == :octicon
|
39
|
+
octicon_kwargs = ast.arguments[1]
|
40
|
+
icon = icon(ast.arguments)
|
41
|
+
elsif ast.method_name == :render && code.include?("Primer::OcticonComponent")
|
42
|
+
octicon_kwargs = ast.arguments.first.arguments.last
|
43
|
+
icon = icon(ast.arguments.first.arguments)
|
44
|
+
else
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
# Don't autocorrect if using a custom icon
|
49
|
+
return unless icon == :x
|
50
|
+
# Don't autocorrect if the octicon has custom arguments
|
51
|
+
return if custom_attributes?(octicon_kwargs)
|
52
|
+
|
53
|
+
octicon_aria_label = aria_label_from_octicon(octicon_kwargs)
|
54
|
+
tag_aria_label = tag.attributes.each.find { |a| a.name == "aria-label" }
|
55
|
+
|
56
|
+
# Can't autocorrect if there is no aria-label.
|
57
|
+
return if octicon_aria_label.blank? && tag_aria_label.blank?
|
58
|
+
|
59
|
+
args = ARGUMENT_MAPPER.new(tag).to_s
|
60
|
+
|
61
|
+
# Argument mapper will add the `aria-label` if the tag has it.
|
62
|
+
return args if tag_aria_label.present?
|
63
|
+
|
64
|
+
aria_label_arg = "\"aria-label\": #{octicon_aria_label}"
|
65
|
+
|
66
|
+
return aria_label_arg if args.blank?
|
67
|
+
|
68
|
+
"#{args}, #{aria_label_arg}"
|
69
|
+
rescue ArgumentMappers::ConversionError
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Overriding the basic correction since this component does not uses content blocks.
|
74
|
+
def correction(args)
|
75
|
+
return if args.nil?
|
76
|
+
|
77
|
+
correction = "<%= render #{self.class::COMPONENT}.new"
|
78
|
+
correction += "(#{args})" if args.present?
|
79
|
+
"#{correction} %>"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Overriding the basic correction since this component will rewrite the whole tag block.
|
83
|
+
def add_correction(tag, tag_tree)
|
84
|
+
offense_loc = tag.loc.with(end_pos: tag_tree[:closing].loc.to_range.last)
|
85
|
+
add_offense(offense_loc, tag_tree[:message], tag_tree[:correction])
|
86
|
+
end
|
87
|
+
|
88
|
+
# Extracts the aria-label value from the octicon kwargs.
|
89
|
+
# It can either be in `"aria-label": "value"`` or `aria: { label: "value" } }`.
|
90
|
+
def aria_label_from_octicon(kwargs)
|
91
|
+
return if kwargs.blank? || kwargs.type != :hash || kwargs.pairs.blank?
|
92
|
+
|
93
|
+
aria_label = kwargs.pairs.find { |x| x.key.value == :"aria-label" }
|
94
|
+
|
95
|
+
return aria_label.value.source if aria_label
|
96
|
+
|
97
|
+
aria_hash = kwargs.pairs.find { |x| x.key.value == :aria }
|
98
|
+
|
99
|
+
return if aria_hash.blank?
|
100
|
+
|
101
|
+
aria_label = aria_hash.value.pairs.find { |x| x.key.value == :label }
|
102
|
+
|
103
|
+
aria_label&.value&.source
|
104
|
+
end
|
105
|
+
|
106
|
+
def custom_attributes?(kwargs)
|
107
|
+
return false if kwargs.blank? || kwargs.type != :hash || kwargs.pairs.blank?
|
108
|
+
|
109
|
+
(kwargs.keys.map { |key| key.value.to_s } - ALLOWED_OCTICON_ARGS).present?
|
110
|
+
end
|
111
|
+
|
112
|
+
def erb_ast(code)
|
113
|
+
RuboCop::AST::ProcessedSource.new(code, RUBY_VERSION.to_f).ast
|
114
|
+
end
|
115
|
+
|
116
|
+
def icon(args)
|
117
|
+
return args.first.value.to_sym if args.first.type == :sym || args.first.type == :str
|
118
|
+
|
119
|
+
args.last.pairs.find { |x| x.key.value == :icon }.value.value.to_sym
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -1,16 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "base_linter"
|
4
|
+
require_relative "autocorrectable"
|
5
|
+
require_relative "argument_mappers/flash"
|
4
6
|
|
5
7
|
module ERBLint
|
6
8
|
module Linters
|
7
9
|
# Counts the number of times a HTML flash is used instead of the component.
|
8
|
-
class FlashComponentMigrationCounter <
|
9
|
-
include
|
10
|
+
class FlashComponentMigrationCounter < BaseLinter
|
11
|
+
include Autocorrectable
|
10
12
|
|
11
13
|
TAGS = %w[div].freeze
|
12
14
|
CLASSES = %w[flash].freeze
|
13
15
|
MESSAGE = "We are migrating flashes to use [Primer::FlashComponent](https://primer.style/view-components/components/flash), please try to use that instead of raw HTML."
|
16
|
+
ARGUMENT_MAPPER = ArgumentMappers::Flash
|
17
|
+
COMPONENT = "Primer::FlashComponent"
|
18
|
+
|
19
|
+
def map_arguments(tag, tag_tree)
|
20
|
+
# We can only autocorrect elements with simple text as content.
|
21
|
+
return nil if tag_tree[:children].size != 1
|
22
|
+
# Hash children indicates that there are tags in the content.
|
23
|
+
return nil if tag_tree[:children].first.is_a?(Hash)
|
24
|
+
|
25
|
+
ARGUMENT_MAPPER.new(tag).to_s
|
26
|
+
rescue ArgumentMappers::ConversionError
|
27
|
+
nil
|
28
|
+
end
|
14
29
|
end
|
15
30
|
end
|
16
31
|
end
|
@@ -1,14 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "base_linter"
|
4
4
|
require_relative "autocorrectable"
|
5
5
|
require_relative "argument_mappers/label"
|
6
6
|
|
7
7
|
module ERBLint
|
8
8
|
module Linters
|
9
9
|
# Counts the number of times a HTML label is used instead of the component.
|
10
|
-
class LabelComponentMigrationCounter <
|
11
|
-
include Helpers
|
10
|
+
class LabelComponentMigrationCounter < BaseLinter
|
12
11
|
include Autocorrectable
|
13
12
|
|
14
13
|
TAGS = Primer::ViewComponents::Constants.get(
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rubocop"
|
4
|
+
require "primer/view_components/statuses"
|
5
|
+
require_relative "../../../../app/lib/primer/view_helper"
|
6
|
+
|
7
|
+
# :nocov:
|
8
|
+
module RuboCop
|
9
|
+
module Cop
|
10
|
+
module Primer
|
11
|
+
# This cop ensures that components don't use deprecated arguments
|
12
|
+
#
|
13
|
+
# bad
|
14
|
+
# Component.new(foo: :deprecated)
|
15
|
+
#
|
16
|
+
# good
|
17
|
+
# Component.new(foo: :bar)
|
18
|
+
class DeprecatedArguments < RuboCop::Cop::Cop
|
19
|
+
INVALID_MESSAGE = <<~STR
|
20
|
+
Avoid using deprecated arguments: https://primer.style/view-components/deprecated.
|
21
|
+
STR
|
22
|
+
|
23
|
+
# This is a hash of deprecated arguments and their replacements.
|
24
|
+
#
|
25
|
+
# * The top level key is the argument.
|
26
|
+
# * The second level key is the value.
|
27
|
+
# * The seceond level value is a string of the full replacement. e.g. "new_argument: :new_value"
|
28
|
+
# If the value is nil, then there is no replacement.
|
29
|
+
#
|
30
|
+
# e.g.
|
31
|
+
# DEPRECATED = {
|
32
|
+
# argument: {
|
33
|
+
# value: "new_argument: :new_value"
|
34
|
+
# }
|
35
|
+
# }
|
36
|
+
#
|
37
|
+
DEPRECATED = {
|
38
|
+
color: {
|
39
|
+
blue: "color: :text_link",
|
40
|
+
gray_dark: "color: :text_primary",
|
41
|
+
gray: "color: :text_secondary",
|
42
|
+
gray_light: "color: :text_tertiary",
|
43
|
+
green: "color: :text_success",
|
44
|
+
yellow: "color: :text_warning",
|
45
|
+
red: "color: :text_danger",
|
46
|
+
gray_0: nil,
|
47
|
+
gray_1: nil,
|
48
|
+
gray_2: nil,
|
49
|
+
gray_3: nil,
|
50
|
+
gray_4: nil,
|
51
|
+
gray_5: nil,
|
52
|
+
gray_6: nil,
|
53
|
+
gray_7: nil,
|
54
|
+
gray_8: nil,
|
55
|
+
gray_9: nil,
|
56
|
+
blue_0: nil,
|
57
|
+
blue_1: nil,
|
58
|
+
blue_2: nil,
|
59
|
+
blue_3: nil,
|
60
|
+
blue_4: nil,
|
61
|
+
blue_5: nil,
|
62
|
+
blue_6: nil,
|
63
|
+
blue_7: nil,
|
64
|
+
blue_8: nil,
|
65
|
+
blue_9: nil,
|
66
|
+
green_0: nil,
|
67
|
+
green_1: nil,
|
68
|
+
green_2: nil,
|
69
|
+
green_3: nil,
|
70
|
+
green_4: nil,
|
71
|
+
green_5: nil,
|
72
|
+
green_6: nil,
|
73
|
+
green_7: nil,
|
74
|
+
green_8: nil,
|
75
|
+
green_9: nil,
|
76
|
+
yellow_0: nil,
|
77
|
+
yellow_1: nil,
|
78
|
+
yellow_2: nil,
|
79
|
+
yellow_3: nil,
|
80
|
+
yellow_4: nil,
|
81
|
+
yellow_5: nil,
|
82
|
+
yellow_6: nil,
|
83
|
+
yellow_7: nil,
|
84
|
+
yellow_8: nil,
|
85
|
+
yellow_9: nil,
|
86
|
+
red_0: nil,
|
87
|
+
red_1: nil,
|
88
|
+
red_2: nil,
|
89
|
+
red_3: nil,
|
90
|
+
red_4: nil,
|
91
|
+
red_5: nil,
|
92
|
+
red_6: nil,
|
93
|
+
red_7: nil,
|
94
|
+
red_8: nil,
|
95
|
+
red_9: nil,
|
96
|
+
purple_0: nil,
|
97
|
+
purple_1: nil,
|
98
|
+
purple_2: nil,
|
99
|
+
purple_3: nil,
|
100
|
+
purple_4: nil,
|
101
|
+
purple_5: nil,
|
102
|
+
purple_6: nil,
|
103
|
+
purple_7: nil,
|
104
|
+
purple_8: nil,
|
105
|
+
purple_9: nil,
|
106
|
+
pink_0: nil,
|
107
|
+
pink_1: nil,
|
108
|
+
pink_2: nil,
|
109
|
+
pink_3: nil,
|
110
|
+
pink_4: nil,
|
111
|
+
pink_5: nil,
|
112
|
+
pink_6: nil,
|
113
|
+
pink_7: nil,
|
114
|
+
pink_8: nil,
|
115
|
+
pink_9: nil,
|
116
|
+
orange_0: nil,
|
117
|
+
orange_1: nil,
|
118
|
+
orange_2: nil,
|
119
|
+
orange_3: nil,
|
120
|
+
orange_4: nil,
|
121
|
+
orange_5: nil,
|
122
|
+
orange_6: nil,
|
123
|
+
orange_7: nil,
|
124
|
+
orange_8: nil,
|
125
|
+
orange_9: nil
|
126
|
+
}
|
127
|
+
}.freeze
|
128
|
+
|
129
|
+
def on_send(node)
|
130
|
+
return unless valid_node?(node)
|
131
|
+
return unless node.arguments?
|
132
|
+
|
133
|
+
# we are looking for hash arguments and they are always last
|
134
|
+
kwargs = node.arguments.last
|
135
|
+
|
136
|
+
return unless kwargs.type == :hash
|
137
|
+
|
138
|
+
kwargs.pairs.each do |pair|
|
139
|
+
# Skip if we're not dealing with a symbol
|
140
|
+
next if pair.key.type != :sym
|
141
|
+
next unless pair.value.type == :sym || pair.value.type == :str
|
142
|
+
|
143
|
+
key = pair.key.value
|
144
|
+
value = pair.value.value.to_sym
|
145
|
+
|
146
|
+
next unless DEPRECATED.key?(key) && DEPRECATED[key].key?(value)
|
147
|
+
|
148
|
+
add_offense(pair, message: INVALID_MESSAGE)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def autocorrect(node)
|
153
|
+
lambda do |corrector|
|
154
|
+
replacement = DEPRECATED[node.key.value][node.value.value.to_sym]
|
155
|
+
corrector.replace(node, replacement) if replacement.present?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
# We only verify SystemArguments if it's a `.new` call on a component or
|
162
|
+
# a ViewHleper call.
|
163
|
+
def valid_node?(node)
|
164
|
+
view_helpers.include?(node.method_name) || (node.method_name == :new && ::Primer::ViewComponents::STATUSES.key?(node.receiver.const_name))
|
165
|
+
end
|
166
|
+
|
167
|
+
def view_helpers
|
168
|
+
::Primer::ViewHelper::HELPERS.keys.map { |key| "primer_#{key}".to_sym }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rubocop"
|
4
|
+
require "primer/classify/utilities"
|
5
|
+
require "primer/classify/validation"
|
6
|
+
|
7
|
+
# :nocov:
|
8
|
+
module RuboCop
|
9
|
+
module Cop
|
10
|
+
module Primer
|
11
|
+
# This cop ensures that components use System Arguments instead of CSS classes.
|
12
|
+
#
|
13
|
+
# bad
|
14
|
+
# octicon(:icon)
|
15
|
+
# octicon("icon")
|
16
|
+
# octicon("icon-with-daashes")
|
17
|
+
# octicon(@ivar)
|
18
|
+
# octicon(condition > "icon" : "other-icon")
|
19
|
+
#
|
20
|
+
# good
|
21
|
+
# primer_octicon(:icon)
|
22
|
+
# primer_octicon(:"icon-with-daashes")
|
23
|
+
# primer_octicon(@ivar)
|
24
|
+
# primer_octicon(condition > "icon" : "other-icon")
|
25
|
+
class PrimerOcticon < RuboCop::Cop::Cop
|
26
|
+
INVALID_MESSAGE = <<~STR
|
27
|
+
Replace the octicon helper with primer_octicon. See https://primer.style/view-components/components/octicon for details.
|
28
|
+
STR
|
29
|
+
|
30
|
+
SIZE_ATTRIBUTES = %w[height width size].freeze
|
31
|
+
STRING_ATTRIBUTES = %w[aria- data-].freeze
|
32
|
+
VALID_ATTRIBUTES = [*SIZE_ATTRIBUTES, *STRING_ATTRIBUTES, "class"].freeze
|
33
|
+
|
34
|
+
STRING_ATTRIBUTE_REGEX = Regexp.union(STRING_ATTRIBUTES).freeze
|
35
|
+
ATTRIBUTE_REGEX = Regexp.union(VALID_ATTRIBUTES).freeze
|
36
|
+
INVALID_ATTRIBUTE = -1
|
37
|
+
|
38
|
+
def on_send(node)
|
39
|
+
return unless node.method_name == :octicon
|
40
|
+
return unless node.arguments?
|
41
|
+
|
42
|
+
kwargs = kwargs(node)
|
43
|
+
|
44
|
+
return unless kwargs.type == :hash
|
45
|
+
|
46
|
+
attributes = kwargs.keys.map(&:value)
|
47
|
+
|
48
|
+
# Don't convert unknown attributes
|
49
|
+
return unless attributes.all? { |attribute| attribute.match?(ATTRIBUTE_REGEX) }
|
50
|
+
# Can't convert size
|
51
|
+
return if octicon_size_attributes(kwargs) == INVALID_ATTRIBUTE
|
52
|
+
|
53
|
+
# find class pair
|
54
|
+
classes = classes(kwargs)
|
55
|
+
|
56
|
+
return if classes == INVALID_ATTRIBUTE
|
57
|
+
|
58
|
+
# check if classes are convertible
|
59
|
+
if classes.present?
|
60
|
+
system_arguments = ::Primer::Classify::Utilities.classes_to_hash(classes)
|
61
|
+
invalid_classes = (system_arguments[:classes]&.split(" ") || []).select { |class_name| ::Primer::Classify::Validation.invalid?(class_name) }
|
62
|
+
|
63
|
+
# Uses system argument that can't be converted
|
64
|
+
return if invalid_classes.present?
|
65
|
+
end
|
66
|
+
|
67
|
+
add_offense(node, message: INVALID_MESSAGE)
|
68
|
+
end
|
69
|
+
|
70
|
+
def autocorrect(node)
|
71
|
+
lambda do |corrector|
|
72
|
+
kwargs = kwargs(node)
|
73
|
+
|
74
|
+
# Converting arguments for the component
|
75
|
+
classes = classes(kwargs)
|
76
|
+
size_attributes = transform_sizes(kwargs)
|
77
|
+
args = arguments_as_string(node, size_attributes, classes)
|
78
|
+
|
79
|
+
corrector.replace(node.loc.expression, "primer_octicon(#{args})")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def transform_sizes(kwargs)
|
86
|
+
attributes = octicon_size_attributes(kwargs)
|
87
|
+
|
88
|
+
attributes.transform_values do |size|
|
89
|
+
if size.between?(10, 16)
|
90
|
+
""
|
91
|
+
elsif size.between?(22, 26)
|
92
|
+
":medium"
|
93
|
+
else
|
94
|
+
size
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def octicon_size_attributes(kwargs)
|
100
|
+
kwargs.pairs.each_with_object({}) do |pair, h|
|
101
|
+
next unless SIZE_ATTRIBUTES.include?(pair.key.value.to_s)
|
102
|
+
|
103
|
+
# We only support string or int values.
|
104
|
+
case pair.value.type
|
105
|
+
when :int
|
106
|
+
h[pair.key.value] = pair.value.source.to_i
|
107
|
+
when :str
|
108
|
+
h[pair.key.value] = pair.value.value.to_i
|
109
|
+
else
|
110
|
+
return INVALID_ATTRIBUTE
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def classes(kwargs)
|
116
|
+
# find class pair
|
117
|
+
class_arg = kwargs.pairs.find { |kwarg| kwarg.key.value == :class }
|
118
|
+
|
119
|
+
return if class_arg.blank?
|
120
|
+
return INVALID_ATTRIBUTE unless class_arg.value.type == :str
|
121
|
+
|
122
|
+
class_arg.value.value
|
123
|
+
end
|
124
|
+
|
125
|
+
def arguments_as_string(node, size_attributes, classes)
|
126
|
+
args = icon(node.arguments.first)
|
127
|
+
size_args = size_attributes_to_string(size_attributes)
|
128
|
+
string_args = string_args_to_string(node)
|
129
|
+
|
130
|
+
args = "#{args}, #{size_attributes_to_string(size_attributes)}" if size_args.present?
|
131
|
+
args = "#{args}, #{::Primer::Classify::Utilities.classes_to_args(classes)}" if classes.present?
|
132
|
+
args = "#{args}, #{string_args}" if string_args.present?
|
133
|
+
|
134
|
+
args
|
135
|
+
end
|
136
|
+
|
137
|
+
def size_attributes_to_string(size_attributes)
|
138
|
+
# No arguments if they map to the default size
|
139
|
+
return if size_attributes.blank? || size_attributes.values.all?(&:blank?)
|
140
|
+
# Return mapped argument to `size`
|
141
|
+
return "size: :medium" if size_attributes.values.any?(":medium")
|
142
|
+
|
143
|
+
size_attributes.map do |key, value|
|
144
|
+
"#{key}: #{value}"
|
145
|
+
end.join(", ")
|
146
|
+
end
|
147
|
+
|
148
|
+
def string_args_to_string(node)
|
149
|
+
kwargs = kwargs(node)
|
150
|
+
|
151
|
+
args = kwargs.pairs.each_with_object([]) do |pair, acc|
|
152
|
+
next unless pair.key.value.to_s.match?(STRING_ATTRIBUTE_REGEX)
|
153
|
+
|
154
|
+
key = pair.key.value.to_s == "data-test-selector" ? "test_selector" : "\"#{pair.key.value}\""
|
155
|
+
acc << "#{key}: #{pair.value.source}"
|
156
|
+
end
|
157
|
+
|
158
|
+
args.join(",")
|
159
|
+
end
|
160
|
+
|
161
|
+
def kwargs(node)
|
162
|
+
return node.arguments.last if node.arguments.size > 1
|
163
|
+
|
164
|
+
OpenStruct.new(keys: [], pairs: [], type: :hash)
|
165
|
+
end
|
166
|
+
|
167
|
+
def icon(node)
|
168
|
+
return node.source unless node.type == :str
|
169
|
+
return ":#{node.value}" unless node.value.include?("-")
|
170
|
+
|
171
|
+
# If the icon contains `-` we need to cast the string as a symbole
|
172
|
+
# E.g: `arrow-down` becomes `:"arrow-down"`
|
173
|
+
":#{node.source}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|