primer_view_components 0.0.51 → 0.0.52

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.
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