primer_view_components 0.0.51 → 0.0.52

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/app/components/primer/beta/avatar_stack.rb +9 -9
  4. data/app/components/primer/beta/truncate.html.erb +5 -0
  5. data/app/components/primer/beta/truncate.rb +110 -0
  6. data/app/components/primer/border_box_component.rb +27 -1
  7. data/app/components/primer/clipboard_copy.rb +1 -1
  8. data/app/components/primer/dropdown.rb +7 -7
  9. data/app/components/primer/icon_button.rb +1 -1
  10. data/app/components/primer/navigation/tab_component.rb +1 -1
  11. data/app/components/primer/progress_bar_component.rb +0 -3
  12. data/app/lib/primer/fetch_or_fallback_helper.rb +2 -0
  13. data/app/lib/primer/tabbed_component_helper.rb +1 -1
  14. data/app/lib/primer/view_helper.rb +1 -0
  15. data/lib/primer/classify.rb +4 -6
  16. data/lib/primer/classify/flex.rb +1 -1
  17. data/lib/primer/classify/functional_colors.rb +1 -1
  18. data/lib/primer/classify/utilities.rb +16 -1
  19. data/lib/primer/classify/validation.rb +18 -0
  20. data/lib/primer/view_components/linters/argument_mappers/base.rb +33 -7
  21. data/lib/primer/view_components/linters/argument_mappers/button.rb +5 -6
  22. data/lib/primer/view_components/linters/argument_mappers/clipboard_copy.rb +1 -2
  23. data/lib/primer/view_components/linters/argument_mappers/helpers/erb_block.rb +48 -5
  24. data/lib/primer/view_components/linters/argument_mappers/label.rb +3 -4
  25. data/lib/primer/view_components/linters/argument_mappers/system_arguments.rb +5 -7
  26. data/lib/primer/view_components/linters/autocorrectable.rb +2 -2
  27. data/lib/primer/view_components/linters/clipboard_copy_component_migration_counter.rb +1 -1
  28. data/lib/primer/view_components/linters/helpers.rb +8 -4
  29. data/lib/primer/view_components/version.rb +1 -1
  30. data/lib/rubocop/config/default.yml +5 -0
  31. data/lib/rubocop/cop/primer.rb +1 -2
  32. data/lib/rubocop/cop/primer/deprecated_arguments.rb +173 -0
  33. data/lib/rubocop/cop/primer/no_tag_memoize.rb +1 -0
  34. data/lib/rubocop/cop/primer/primer_octicon.rb +178 -0
  35. data/lib/rubocop/cop/primer/system_argument_instead_of_class.rb +3 -17
  36. data/lib/tasks/coverage.rake +4 -0
  37. data/lib/tasks/docs.rake +3 -2
  38. data/lib/tasks/utilities.rake +5 -3
  39. data/lib/yard/docs_helper.rb +4 -3
  40. data/static/arguments.yml +7 -0
  41. data/static/classes.yml +8 -0
  42. data/static/constants.json +13 -1
  43. data/static/statuses.json +3 -1
  44. metadata +9 -4
@@ -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
@@ -5,6 +5,7 @@ require "primer/classify/utilities"
5
5
  require "primer/view_components/statuses"
6
6
  require_relative "../../../../app/lib/primer/view_helper"
7
7
 
8
+ # :nocov:
8
9
  module RuboCop
9
10
  module Cop
10
11
  module Primer
@@ -48,8 +49,8 @@ module RuboCop
48
49
 
49
50
  def autocorrect(node)
50
51
  lambda do |corrector|
51
- system_arguments = ::Primer::Classify::Utilities.classes_to_hash(node.value.value)
52
- corrector.replace(node.loc.expression, arguments_as_string(system_arguments))
52
+ args = ::Primer::Classify::Utilities.classes_to_args(node.value.value)
53
+ corrector.replace(node.loc.expression, args)
53
54
  end
54
55
  end
55
56
 
@@ -64,21 +65,6 @@ module RuboCop
64
65
  def view_helpers
65
66
  ::Primer::ViewHelper::HELPERS.keys.map { |key| "primer_#{key}".to_sym }
66
67
  end
67
-
68
- def arguments_as_string(system_arguments)
69
- system_arguments.map do |key, value|
70
- val = case value
71
- when Symbol
72
- ":#{value}"
73
- when String
74
- value.to_json
75
- else
76
- value
77
- end
78
-
79
- "#{key}: #{val}"
80
- end.join(", ")
81
- end
82
68
  end
83
69
  end
84
70
  end
@@ -9,6 +9,10 @@ namespace :coverage do
9
9
 
10
10
  SimpleCov.collate Dir["simplecov-resultset-*/.resultset.json"], "rails" do
11
11
  formatter SimpleCov::Formatter::Console
12
+
13
+ add_group "Ignored Code" do |src_file|
14
+ File.readlines(src_file.filename).grep(/:nocov:/).any?
15
+ end
12
16
  end
13
17
  end
14
18
  end
data/lib/tasks/docs.rake CHANGED
@@ -74,6 +74,7 @@ namespace :docs do
74
74
  Primer::TimelineItemComponent,
75
75
  Primer::Tooltip,
76
76
  Primer::Truncate,
77
+ Primer::Beta::Truncate,
77
78
  Primer::UnderlineNavComponent
78
79
  ]
79
80
 
@@ -238,7 +239,7 @@ namespace :docs do
238
239
  f.puts
239
240
  html = view_context.render(inline: code)
240
241
  html.scan(/class="([^"]*)"/) do |classnames|
241
- classes_found_in_examples.concat(classnames[0].split(" ").reject { |c| c.starts_with?("octicon", "js", "my-") }.map { ".#{_1}"})
242
+ classes_found_in_examples.concat(classnames[0].split.reject { |c| c.starts_with?("octicon", "js", "my-") }.map { ".#{_1}" })
242
243
  end
243
244
  f.puts("<Example src=\"#{html.tr('"', "\'").delete("\n")}\" />")
244
245
  f.puts
@@ -382,7 +383,7 @@ namespace :docs do
382
383
  end
383
384
 
384
385
  def pretty_default_value(tag, component)
385
- params = tag.object.parameters.find { |param| [tag.name.to_s, tag.name.to_s + ":"].include?(param[0]) }
386
+ params = tag.object.parameters.find { |param| [tag.name.to_s, "#{tag.name}:"].include?(param[0]) }
386
387
  default = tag.defaults&.first || params&.second
387
388
 
388
389
  return "N/A" unless default
@@ -8,6 +8,7 @@ namespace :utilities do
8
8
  require "primer/classify/utilities"
9
9
 
10
10
  # Keys that are looked for to be included in the utilities.yml file
11
+ # rubocop:disable Lint/ConstantDefinitionInBlock
11
12
  SUPPORTED_KEYS = %i[
12
13
  anim
13
14
  d
@@ -23,6 +24,7 @@ namespace :utilities do
23
24
  ].freeze
24
25
 
25
26
  BREAKPOINTS = [nil, "sm", "md", "lg", "xl"].freeze
27
+ # rubocop:enable Lint/ConstantDefinitionInBlock
26
28
 
27
29
  css_data =
28
30
  JSON.parse(
@@ -38,7 +40,7 @@ namespace :utilities do
38
40
  css_data.each do |selector|
39
41
  selector.sub!(/^./, "")
40
42
  # Next if selector has ancestors or sibling selectors
41
- next if selector.match?(/[:><~\[\.]/)
43
+ next if selector.match?(/[:><~\[.]/)
42
44
  next unless SUPPORTED_KEYS.any? { |key| selector.start_with?("#{key}-") }
43
45
 
44
46
  # Dupe so we still have the selector at the end of slicing it up
@@ -50,7 +52,7 @@ namespace :utilities do
50
52
  next unless classname.match?(Regexp.new(k))
51
53
 
52
54
  key = v
53
- classname.sub!(Regexp.new(k + "-"), "")
55
+ classname.sub!(Regexp.new("#{k}-"), "")
54
56
  end
55
57
 
56
58
  # If we didn't find a replacement, grab the first text before hyphen
@@ -66,7 +68,7 @@ namespace :utilities do
66
68
  end
67
69
 
68
70
  # Change the rest from hypens to underscores
69
- classname.sub!(/\-/, "_")
71
+ classname.sub!(/-/, "_")
70
72
 
71
73
  # convert padding/margin negative values ie n7 to -7
72
74
  classname.sub!(/^n/, "-") if classname.match?(/^n[0-9]/)
@@ -6,8 +6,9 @@ module YARD
6
6
  def one_of(enumerable, lower: false, sort: true)
7
7
  # Sort the array if requested
8
8
  if sort
9
- compare = ->(a, b) { a.class == b.class ? a <=> b : a.class.to_s <=> b.class.to_s }
10
- enumerable = enumerable.sort { |a, b| compare.call(a, b) }
9
+ enumerable = enumerable.sort do |a, b|
10
+ a.instance_of?(b.class) ? a <=> b : a.class.to_s <=> b.class.to_s
11
+ end
11
12
  end
12
13
 
13
14
  values =
@@ -58,7 +59,7 @@ module YARD
58
59
  def status_module_and_short_name(component)
59
60
  name_with_status = component.name.gsub(/Primer::|Component/, "")
60
61
 
61
- m = name_with_status.match(/(?<status>Beta|Alpha|Deprecated)?(::)?(?<name>.*)/)
62
+ m = name_with_status.match(/(?<status>Beta|Alpha|Deprecated)?(?<_colons>::)?(?<name>.*)/)
62
63
  [m[:status]&.downcase, m[:name].gsub("::", "")]
63
64
  end
64
65
 
data/static/arguments.yml CHANGED
@@ -143,6 +143,13 @@
143
143
  type: Hash
144
144
  default: N/A
145
145
  description: "[System arguments](/system-arguments)"
146
+ - component: Truncate
147
+ source: https://github.com/primer/view_components/tree/main/app/components/primer/beta/truncate.rb
148
+ parameters:
149
+ - name: system_arguments
150
+ type: Hash
151
+ default: N/A
152
+ description: "[System arguments](/system-arguments)"
146
153
  - component: Blankslate
147
154
  source: https://github.com/primer/view_components/tree/main/app/components/primer/blankslate_component.rb
148
155
  parameters:
data/static/classes.yml CHANGED
@@ -10,6 +10,9 @@
10
10
  - ".Box-footer"
11
11
  - ".Box-header"
12
12
  - ".Box-row"
13
+ - ".Box-row--blue"
14
+ - ".Box-row--gray"
15
+ - ".Box-row--yellow"
13
16
  - ".BtnGroup"
14
17
  - ".BtnGroup-item"
15
18
  - ".Counter"
@@ -49,6 +52,10 @@
49
52
  - ".TimelineItem-avatar"
50
53
  - ".TimelineItem-badge"
51
54
  - ".TimelineItem-body"
55
+ - ".Truncate"
56
+ - ".Truncate-text"
57
+ - ".Truncate-text--expandable"
58
+ - ".Truncate-text--primary"
52
59
  - ".UnderlineNav"
53
60
  - ".UnderlineNav--right"
54
61
  - ".UnderlineNav-actions"
@@ -168,6 +175,7 @@
168
175
  - ".tabnav-tabs"
169
176
  - ".text-bold"
170
177
  - ".text-left"
178
+ - ".text-normal"
171
179
  - ".tooltipped"
172
180
  - ".tooltipped-n"
173
181
  - ".tooltipped-no-delay"
@@ -85,16 +85,28 @@
85
85
  "Primer::Beta::Text": {
86
86
  "DEFAULT_TAG": "span"
87
87
  },
88
+ "Primer::Beta::Truncate": {
89
+ "TruncateText": "Primer::Beta::Truncate::TruncateText"
90
+ },
91
+ "Primer::Beta::Truncate::TruncateText": {
92
+ },
88
93
  "Primer::BlankslateComponent": {
89
94
  },
90
95
  "Primer::BorderBoxComponent": {
91
96
  "DEFAULT_PADDING": "default",
97
+ "DEFAULT_ROW_SCHEME": "default",
92
98
  "PADDING_MAPPINGS": {
93
99
  "default": "",
94
100
  "condensed": "Box--condensed",
95
101
  "spacious": "Box--spacious"
96
102
  },
97
- "PADDING_SUGGESTION": "Perhaps you could consider using :padding options of default, condensed, and spacious?"
103
+ "PADDING_SUGGESTION": "Perhaps you could consider using :padding options of default, condensed, and spacious?",
104
+ "ROW_SCHEME_MAPPINGS": {
105
+ "default": "",
106
+ "neutral": "Box-row--gray",
107
+ "info": "Box-row--blue",
108
+ "warning": "Box-row--yellow"
109
+ }
98
110
  },
99
111
  "Primer::BoxComponent": {
100
112
  },
data/static/statuses.json CHANGED
@@ -8,6 +8,8 @@
8
8
  "Primer::Beta::Avatar": "beta",
9
9
  "Primer::Beta::AvatarStack": "beta",
10
10
  "Primer::Beta::Text": "beta",
11
+ "Primer::Beta::Truncate": "beta",
12
+ "Primer::Beta::Truncate::TruncateText": "alpha",
11
13
  "Primer::BlankslateComponent": "beta",
12
14
  "Primer::BorderBoxComponent": "beta",
13
15
  "Primer::BoxComponent": "stable",
@@ -15,7 +17,7 @@
15
17
  "Primer::BreadcrumbComponent::ItemComponent": "alpha",
16
18
  "Primer::ButtonComponent": "beta",
17
19
  "Primer::ButtonGroup": "beta",
18
- "Primer::ClipboardCopy": "alpha",
20
+ "Primer::ClipboardCopy": "beta",
19
21
  "Primer::CloseButton": "beta",
20
22
  "Primer::CounterComponent": "beta",
21
23
  "Primer::DetailsComponent": "beta",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: primer_view_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.51
4
+ version: 0.0.52
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: 2021-08-05 00:00:00.000000000 Z
11
+ date: 2021-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionview
@@ -218,14 +218,14 @@ dependencies:
218
218
  requirements:
219
219
  - - '='
220
220
  - !ruby/object:Gem::Version
221
- version: '0.82'
221
+ version: 1.13.0
222
222
  type: :development
223
223
  prerelease: false
224
224
  version_requirements: !ruby/object:Gem::Requirement
225
225
  requirements:
226
226
  - - '='
227
227
  - !ruby/object:Gem::Version
228
- version: '0.82'
228
+ version: 1.13.0
229
229
  - !ruby/object:Gem::Dependency
230
230
  name: rubocop-github
231
231
  requirement: !ruby/object:Gem::Requirement
@@ -367,6 +367,8 @@ files:
367
367
  - app/components/primer/beta/avatar_stack.html.erb
368
368
  - app/components/primer/beta/avatar_stack.rb
369
369
  - app/components/primer/beta/text.rb
370
+ - app/components/primer/beta/truncate.html.erb
371
+ - app/components/primer/beta/truncate.rb
370
372
  - app/components/primer/blankslate_component.html.erb
371
373
  - app/components/primer/blankslate_component.rb
372
374
  - app/components/primer/border_box_component.html.erb
@@ -476,6 +478,7 @@ files:
476
478
  - lib/primer/classify/grid.rb
477
479
  - lib/primer/classify/utilities.rb
478
480
  - lib/primer/classify/utilities.yml
481
+ - lib/primer/classify/validation.rb
479
482
  - lib/primer/view_components.rb
480
483
  - lib/primer/view_components/constants.rb
481
484
  - lib/primer/view_components/engine.rb
@@ -498,7 +501,9 @@ files:
498
501
  - lib/primer/view_components/version.rb
499
502
  - lib/rubocop/config/default.yml
500
503
  - lib/rubocop/cop/primer.rb
504
+ - lib/rubocop/cop/primer/deprecated_arguments.rb
501
505
  - lib/rubocop/cop/primer/no_tag_memoize.rb
506
+ - lib/rubocop/cop/primer/primer_octicon.rb
502
507
  - lib/rubocop/cop/primer/system_argument_instead_of_class.rb
503
508
  - lib/tasks/constants.rake
504
509
  - lib/tasks/coverage.rake