primer_view_components 0.0.50 → 0.0.54

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +136 -0
  3. data/app/components/primer/alpha/underline_nav.html.erb +15 -0
  4. data/app/components/primer/alpha/underline_nav.rb +143 -0
  5. data/app/components/primer/{underline_nav_component.html.erb → alpha/underline_panels.html.erb} +3 -8
  6. data/app/components/primer/alpha/underline_panels.rb +86 -0
  7. data/app/components/primer/base_component.rb +2 -2
  8. data/app/components/primer/beta/avatar_stack.rb +9 -9
  9. data/app/components/primer/{breadcrumb_component.html.erb → beta/breadcrumbs.html.erb} +0 -0
  10. data/app/components/primer/beta/breadcrumbs.rb +59 -0
  11. data/app/components/primer/beta/truncate.html.erb +5 -0
  12. data/app/components/primer/beta/truncate.rb +110 -0
  13. data/app/components/primer/border_box_component.rb +27 -1
  14. data/app/components/primer/clipboard_copy.rb +1 -1
  15. data/app/components/primer/dropdown.rb +7 -7
  16. data/app/components/primer/icon_button.rb +1 -1
  17. data/app/components/primer/navigation/tab_component.rb +7 -5
  18. data/app/components/primer/progress_bar_component.rb +0 -3
  19. data/app/components/primer/tab_nav_component.html.erb +1 -1
  20. data/app/components/primer/tab_nav_component.rb +1 -1
  21. data/app/lib/primer/fetch_or_fallback_helper.rb +2 -0
  22. data/app/lib/primer/tabbed_component_helper.rb +3 -3
  23. data/app/lib/primer/underline_nav_helper.rb +44 -0
  24. data/app/lib/primer/view_helper.rb +1 -0
  25. data/lib/primer/classify/flex.rb +1 -1
  26. data/lib/primer/classify/functional_colors.rb +1 -1
  27. data/lib/primer/classify/utilities.rb +19 -2
  28. data/lib/primer/classify/utilities.yml +2 -2
  29. data/lib/primer/classify/validation.rb +18 -0
  30. data/lib/primer/classify.rb +5 -15
  31. data/lib/primer/view_components/constants.rb +1 -1
  32. data/lib/primer/view_components/linters/argument_mappers/base.rb +34 -8
  33. data/lib/primer/view_components/linters/argument_mappers/button.rb +5 -6
  34. data/lib/primer/view_components/linters/argument_mappers/clipboard_copy.rb +4 -3
  35. data/lib/primer/view_components/linters/argument_mappers/close_button.rb +43 -0
  36. data/lib/primer/view_components/linters/argument_mappers/flash.rb +32 -0
  37. data/lib/primer/view_components/linters/argument_mappers/helpers/erb_block.rb +48 -5
  38. data/lib/primer/view_components/linters/argument_mappers/label.rb +3 -4
  39. data/lib/primer/view_components/linters/argument_mappers/system_arguments.rb +5 -7
  40. data/lib/primer/view_components/linters/autocorrectable.rb +6 -4
  41. data/lib/primer/view_components/linters/{helpers.rb → base_linter.rb} +69 -29
  42. data/lib/primer/view_components/linters/button_component_migration_counter.rb +4 -3
  43. data/lib/primer/view_components/linters/clipboard_copy_component_migration_counter.rb +3 -4
  44. data/lib/primer/view_components/linters/close_button_component_migration_counter.rb +110 -3
  45. data/lib/primer/view_components/linters/flash_component_migration_counter.rb +18 -3
  46. data/lib/primer/view_components/linters/label_component_migration_counter.rb +2 -3
  47. data/lib/primer/view_components/version.rb +1 -1
  48. data/lib/rubocop/config/default.yml +5 -0
  49. data/lib/rubocop/cop/primer/deprecated_arguments.rb +277 -0
  50. data/lib/rubocop/cop/primer/no_tag_memoize.rb +1 -0
  51. data/lib/rubocop/cop/primer/primer_octicon.rb +178 -0
  52. data/lib/rubocop/cop/primer/system_argument_instead_of_class.rb +12 -16
  53. data/lib/rubocop/cop/primer.rb +1 -2
  54. data/lib/tasks/coverage.rake +4 -0
  55. data/lib/tasks/docs.rake +7 -5
  56. data/lib/tasks/utilities.rake +5 -3
  57. data/lib/yard/docs_helper.rb +6 -3
  58. data/static/arguments.yml +62 -37
  59. data/static/classes.yml +9 -0
  60. data/static/constants.json +38 -23
  61. data/static/statuses.json +8 -5
  62. metadata +37 -15
  63. data/app/components/primer/auto_complete/auto_component.d.ts +0 -1
  64. data/app/components/primer/auto_complete/auto_component.js +0 -1
  65. data/app/components/primer/breadcrumb_component.rb +0 -57
  66. data/app/components/primer/underline_nav_component.rb +0 -187
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "helpers"
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 < Linter
11
- include Helpers
10
+ class LabelComponentMigrationCounter < BaseLinter
12
11
  include Autocorrectable
13
12
 
14
13
  TAGS = Primer::ViewComponents::Constants.get(
@@ -5,7 +5,7 @@ module Primer
5
5
  module VERSION
6
6
  MAJOR = 0
7
7
  MINOR = 0
8
- PATCH = 50
8
+ PATCH = 54
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH].join(".")
11
11
  end
@@ -10,3 +10,8 @@ Primer/SystemArgumentInsteadOfClass:
10
10
  Primer/NoTagMemoize:
11
11
  Enabled: false
12
12
 
13
+ Primer/PrimerOcticon:
14
+ Enabled: true
15
+
16
+ Primer/DeprecatedArguments:
17
+ Enabled: true
@@ -0,0 +1,277 @@
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
+ bg: {
39
+ white: "bg: :primary",
40
+ gray_light: "bg: :secondary",
41
+ gray: "bg: :tertiary",
42
+ gray_dark: "bg: :canvas_inverse",
43
+ blue_light: "bg: :info",
44
+ blue: "bg: :info_inverse",
45
+ green_light: "bg: :success",
46
+ green: "bg: :success_inverse",
47
+ yellow_light: "bg: :warning",
48
+ yellow: "bg: :warning_inverse",
49
+ red_light: "bg: :danger",
50
+ red: "bg: :danger_inverse",
51
+ gray_0: nil,
52
+ gray_1: nil,
53
+ gray_2: nil,
54
+ gray_3: nil,
55
+ gray_4: nil,
56
+ gray_5: nil,
57
+ gray_6: nil,
58
+ gray_7: nil,
59
+ gray_8: nil,
60
+ gray_9: nil,
61
+ blue_0: nil,
62
+ blue_1: nil,
63
+ blue_2: nil,
64
+ blue_3: nil,
65
+ blue_4: nil,
66
+ blue_5: nil,
67
+ blue_6: nil,
68
+ blue_7: nil,
69
+ blue_8: nil,
70
+ blue_9: nil,
71
+ green_0: nil,
72
+ green_1: nil,
73
+ green_2: nil,
74
+ green_3: nil,
75
+ green_4: nil,
76
+ green_5: nil,
77
+ green_6: nil,
78
+ green_7: nil,
79
+ green_8: nil,
80
+ green_9: nil,
81
+ yellow_0: nil,
82
+ yellow_1: nil,
83
+ yellow_2: nil,
84
+ yellow_3: nil,
85
+ yellow_4: nil,
86
+ yellow_5: nil,
87
+ yellow_6: nil,
88
+ yellow_7: nil,
89
+ yellow_8: nil,
90
+ yellow_9: nil,
91
+ red_0: nil,
92
+ red_1: nil,
93
+ red_2: nil,
94
+ red_3: nil,
95
+ red_4: nil,
96
+ red_5: nil,
97
+ red_6: nil,
98
+ red_7: nil,
99
+ red_8: nil,
100
+ red_9: nil,
101
+ purple_0: nil,
102
+ purple_1: nil,
103
+ purple_2: nil,
104
+ purple_3: nil,
105
+ purple_4: nil,
106
+ purple_5: nil,
107
+ purple_6: nil,
108
+ purple_7: nil,
109
+ purple_8: nil,
110
+ purple_9: nil,
111
+ pink_0: nil,
112
+ pink_1: nil,
113
+ pink_2: nil,
114
+ pink_3: nil,
115
+ pink_4: nil,
116
+ pink_5: nil,
117
+ pink_6: nil,
118
+ pink_7: nil,
119
+ pink_8: nil,
120
+ pink_9: nil,
121
+ orange_0: nil,
122
+ orange_1: nil,
123
+ orange_2: nil,
124
+ orange_3: nil,
125
+ orange_4: nil,
126
+ orange_5: nil,
127
+ orange_6: nil,
128
+ orange_7: nil,
129
+ orange_8: nil,
130
+ orange_9: nil
131
+ },
132
+ border_color: {
133
+ gray: "border_color: :primary",
134
+ gray_light: "border_color: :secondary",
135
+ gray_dark: "border_color: :tertiary",
136
+ blue: "border_color: :info",
137
+ green: "border_color: :success",
138
+ yellow: "border_color: :warning",
139
+ red: "border_color: :danger",
140
+ white: "border_color: :inverse"
141
+ },
142
+ color: {
143
+ blue: "color: :text_link",
144
+ gray_dark: "color: :text_primary",
145
+ gray: "color: :text_secondary",
146
+ gray_light: "color: :text_tertiary",
147
+ green: "color: :text_success",
148
+ yellow: "color: :text_warning",
149
+ red: "color: :text_danger",
150
+ gray_0: nil,
151
+ gray_1: nil,
152
+ gray_2: nil,
153
+ gray_3: nil,
154
+ gray_4: nil,
155
+ gray_5: nil,
156
+ gray_6: nil,
157
+ gray_7: nil,
158
+ gray_8: nil,
159
+ gray_9: nil,
160
+ blue_0: nil,
161
+ blue_1: nil,
162
+ blue_2: nil,
163
+ blue_3: nil,
164
+ blue_4: nil,
165
+ blue_5: nil,
166
+ blue_6: nil,
167
+ blue_7: nil,
168
+ blue_8: nil,
169
+ blue_9: nil,
170
+ green_0: nil,
171
+ green_1: nil,
172
+ green_2: nil,
173
+ green_3: nil,
174
+ green_4: nil,
175
+ green_5: nil,
176
+ green_6: nil,
177
+ green_7: nil,
178
+ green_8: nil,
179
+ green_9: nil,
180
+ yellow_0: nil,
181
+ yellow_1: nil,
182
+ yellow_2: nil,
183
+ yellow_3: nil,
184
+ yellow_4: nil,
185
+ yellow_5: nil,
186
+ yellow_6: nil,
187
+ yellow_7: nil,
188
+ yellow_8: nil,
189
+ yellow_9: nil,
190
+ red_0: nil,
191
+ red_1: nil,
192
+ red_2: nil,
193
+ red_3: nil,
194
+ red_4: nil,
195
+ red_5: nil,
196
+ red_6: nil,
197
+ red_7: nil,
198
+ red_8: nil,
199
+ red_9: nil,
200
+ purple_0: nil,
201
+ purple_1: nil,
202
+ purple_2: nil,
203
+ purple_3: nil,
204
+ purple_4: nil,
205
+ purple_5: nil,
206
+ purple_6: nil,
207
+ purple_7: nil,
208
+ purple_8: nil,
209
+ purple_9: nil,
210
+ pink_0: nil,
211
+ pink_1: nil,
212
+ pink_2: nil,
213
+ pink_3: nil,
214
+ pink_4: nil,
215
+ pink_5: nil,
216
+ pink_6: nil,
217
+ pink_7: nil,
218
+ pink_8: nil,
219
+ pink_9: nil,
220
+ orange_0: nil,
221
+ orange_1: nil,
222
+ orange_2: nil,
223
+ orange_3: nil,
224
+ orange_4: nil,
225
+ orange_5: nil,
226
+ orange_6: nil,
227
+ orange_7: nil,
228
+ orange_8: nil,
229
+ orange_9: nil
230
+ }
231
+ }.freeze
232
+
233
+ def on_send(node)
234
+ return unless valid_node?(node)
235
+ return unless node.arguments?
236
+
237
+ # we are looking for hash arguments and they are always last
238
+ kwargs = node.arguments.last
239
+
240
+ return unless kwargs.type == :hash
241
+
242
+ kwargs.pairs.each do |pair|
243
+ # Skip if we're not dealing with a symbol
244
+ next if pair.key.type != :sym
245
+ next unless pair.value.type == :sym || pair.value.type == :str
246
+
247
+ key = pair.key.value
248
+ value = pair.value.value.to_sym
249
+
250
+ next unless DEPRECATED.key?(key) && DEPRECATED[key].key?(value)
251
+
252
+ add_offense(pair, message: INVALID_MESSAGE)
253
+ end
254
+ end
255
+
256
+ def autocorrect(node)
257
+ lambda do |corrector|
258
+ replacement = DEPRECATED[node.key.value][node.value.value.to_sym]
259
+ corrector.replace(node, replacement) if replacement.present?
260
+ end
261
+ end
262
+
263
+ private
264
+
265
+ # We only verify SystemArguments if it's a `.new` call on a component or
266
+ # a ViewHleper call.
267
+ def valid_node?(node)
268
+ view_helpers.include?(node.method_name) || (node.method_name == :new && ::Primer::ViewComponents::STATUSES.key?(node.receiver.const_name))
269
+ end
270
+
271
+ def view_helpers
272
+ ::Primer::ViewHelper::HELPERS.keys.map { |key| "primer_#{key}".to_sym }
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "rubocop"
4
4
 
5
+ # :nocov:
5
6
  module RuboCop
6
7
  module Cop
7
8
  module Primer
@@ -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