primer_view_components 0.0.48 → 0.0.49

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -0
  3. data/app/components/primer/beta/avatar.rb +1 -1
  4. data/app/components/primer/{avatar_stack_component.html.erb → beta/avatar_stack.html.erb} +0 -0
  5. data/app/components/primer/beta/avatar_stack.rb +92 -0
  6. data/app/components/primer/image_crop.html.erb +4 -4
  7. data/app/components/primer/navigation/tab_component.rb +15 -1
  8. data/app/components/primer/tab_nav_component.rb +4 -3
  9. data/app/components/primer/truncate.rb +1 -1
  10. data/app/components/primer/underline_nav_component.rb +3 -2
  11. data/lib/primer/classify/utilities.rb +33 -12
  12. data/lib/primer/view_components.rb +34 -6
  13. data/lib/primer/view_components/constants.rb +55 -0
  14. data/lib/primer/view_components/linters/argument_mappers/base.rb +39 -0
  15. data/lib/primer/view_components/linters/argument_mappers/button.rb +35 -44
  16. data/lib/primer/view_components/linters/argument_mappers/clipboard_copy.rb +25 -0
  17. data/lib/primer/view_components/linters/argument_mappers/label.rb +56 -0
  18. data/lib/primer/view_components/linters/autocorrectable.rb +30 -0
  19. data/lib/primer/view_components/linters/button_component_migration_counter.rb +9 -23
  20. data/lib/primer/view_components/linters/clipboard_copy_component_migration_counter.rb +21 -0
  21. data/lib/primer/view_components/linters/helpers.rb +42 -41
  22. data/lib/primer/view_components/linters/label_component_migration_counter.rb +25 -0
  23. data/lib/primer/view_components/version.rb +1 -1
  24. data/lib/tasks/constants.rake +12 -0
  25. data/lib/tasks/docs.rake +24 -23
  26. data/lib/tasks/utilities.rake +2 -10
  27. data/lib/yard/docs_helper.rb +12 -3
  28. data/static/arguments.yml +977 -0
  29. data/static/assets/view-components.svg +18 -0
  30. data/static/classes.yml +174 -0
  31. data/static/constants.json +628 -0
  32. data/static/statuses.json +1 -1
  33. metadata +16 -4
  34. data/app/components/primer/avatar_stack_component.rb +0 -90
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "conversion_error"
4
+ require_relative "system_arguments"
5
+ require "primer/view_components/constants"
6
+
7
+ module ERBLint
8
+ module Linters
9
+ module ArgumentMappers
10
+ # Provides the base interface to implement an `ArgumentMapper`.
11
+ # Override attribute_to_args in a child class to customize its mapping behavior.
12
+ class Base
13
+ DEFAULT_TAG = nil
14
+
15
+ def initialize(tag)
16
+ @tag = tag
17
+ end
18
+
19
+ def to_s
20
+ to_args.map { |k, v| "#{k}: #{v}" }.join(", ")
21
+ end
22
+
23
+ def to_args
24
+ args = {}
25
+
26
+ args[:tag] = ":#{@tag.name}" unless self.class::DEFAULT_TAG.nil? || @tag.name == self.class::DEFAULT_TAG
27
+
28
+ @tag.attributes.each do |attribute|
29
+ args.merge!(attribute_to_args(attribute))
30
+ end
31
+
32
+ args
33
+ end
34
+
35
+ def attribute_to_args(attribute); end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,62 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "conversion_error"
4
- require_relative "system_arguments"
3
+ require_relative "base"
5
4
 
6
5
  module ERBLint
7
6
  module Linters
8
7
  module ArgumentMappers
9
8
  # Maps classes in a button element to arguments for the Button component.
10
- class Button
11
- SCHEME_MAPPINGS = {
12
- "btn-primary" => ":primary",
13
- "btn-danger" => ":danger",
14
- "btn-outline" => ":outline",
15
- "btn-invisible" => ":invisible",
16
- "btn-link" => ":link"
17
- }.freeze
9
+ class Button < Base
10
+ require "pry"
18
11
 
19
- VARIANT_MAPPINGS = {
20
- "btn-sm" => ":small",
21
- "btn-large" => ":large"
22
- }.freeze
12
+ SCHEME_MAPPINGS = Primer::ViewComponents::Constants.get(
13
+ component: "Primer::ButtonComponent",
14
+ constant: "SCHEME_MAPPINGS",
15
+ symbolize: true
16
+ ).freeze
23
17
 
24
- TYPE_OPTIONS = %w[button reset submit].freeze
18
+ VARIANT_MAPPINGS = Primer::ViewComponents::Constants.get(
19
+ component: "Primer::ButtonComponent",
20
+ constant: "VARIANT_MAPPINGS",
21
+ symbolize: true
22
+ ).freeze
25
23
 
26
- def initialize(tag)
27
- @tag = tag
28
- end
29
-
30
- def to_s
31
- to_args.map { |k, v| "#{k}: #{v}" }.join(", ")
32
- end
33
-
34
- def to_args
35
- args = {}
24
+ TYPE_OPTIONS = Primer::ViewComponents::Constants.get(
25
+ component: "Primer::BaseButton",
26
+ constant: "TYPE_OPTIONS"
27
+ ).freeze
28
+ DEFAULT_TAG = Primer::ViewComponents::Constants.get(
29
+ component: "Primer::BaseButton",
30
+ constant: "DEFAULT_TAG"
31
+ ).freeze
36
32
 
37
- args[:tag] = ":#{@tag.name}" unless @tag.name == "button"
33
+ def attribute_to_args(attribute)
34
+ attr_name = attribute.name
38
35
 
39
- @tag.attributes.each do |attribute|
40
- attr_name = attribute.name
36
+ if attr_name == "class"
37
+ classes_to_args(attribute)
38
+ elsif attr_name == "disabled"
39
+ { disabled: true }
40
+ elsif attr_name == "type"
41
+ # button is the default type, so we don't need to do anything.
42
+ return {} if attribute.value == "button"
41
43
 
42
- if attr_name == "class"
43
- args = args.merge(classes_to_args(attribute))
44
- elsif attr_name == "disabled"
45
- args[:disabled] = true
46
- elsif attr_name == "type"
47
- # button is the default type, so we don't need to do anything.
48
- next if attribute.value == "button"
44
+ raise ConversionError, "Button component does not support type \"#{attribute.value}\"" unless TYPE_OPTIONS.include?(attribute.value)
49
45
 
50
- raise ConversionError, "Button component does not support type \"#{attribute.value}\"" unless TYPE_OPTIONS.include?(attribute.value)
51
-
52
- args[:type] = ":#{attribute.value}"
53
- else
54
- # Assume the attribute is a system argument.
55
- args.merge!(SystemArguments.new(attribute).to_args)
56
- end
46
+ { type: ":#{attribute.value}" }
47
+ else
48
+ # Assume the attribute is a system argument.
49
+ SystemArguments.new(attribute).to_args
57
50
  end
58
-
59
- args
60
51
  end
61
52
 
62
53
  def classes_to_args(classes)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module ArgumentMappers
8
+ # Maps attributes in the clipboard-copy element to arguments for the ClipboardCopy component.
9
+ class ClipboardCopy < Base
10
+ DEFAULT_TAG = "clipboard-copy"
11
+
12
+ def attribute_to_args(attribute)
13
+ attr_name = attribute.name
14
+
15
+ if attr_name == "value"
16
+ { value: attribute.value.to_json }
17
+ else
18
+ # Assume the attribute is a system argument.
19
+ SystemArguments.new(attribute).to_args
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module ArgumentMappers
8
+ # Maps classes in a label element to arguments for the Label component.
9
+ class Label < Base
10
+ SCHEME_MAPPINGS = Primer::ViewComponents::Constants.get(
11
+ component: "Primer::LabelComponent",
12
+ constant: "SCHEME_MAPPINGS",
13
+ symbolize: true
14
+ ).freeze
15
+
16
+ VARIANT_MAPPINGS = Primer::ViewComponents::Constants.get(
17
+ component: "Primer::LabelComponent",
18
+ constant: "VARIANT_MAPPINGS",
19
+ symbolize: true
20
+ ).freeze
21
+
22
+ DEFAULT_TAG = Primer::ViewComponents::Constants.get(
23
+ component: "Primer::LabelComponent",
24
+ constant: "DEFAULT_TAG"
25
+ ).freeze
26
+
27
+ def attribute_to_args(attribute)
28
+ attr_name = attribute.name
29
+
30
+ if attr_name == "class"
31
+ classes_to_args(attribute)
32
+ elsif attr_name == "title"
33
+ { title: attribute.value.to_json }
34
+ else
35
+ # Assume the attribute is a system argument.
36
+ SystemArguments.new(attribute).to_args
37
+ end
38
+ end
39
+
40
+ def classes_to_args(classes)
41
+ classes.value.split(" ").each_with_object({}) do |class_name, acc|
42
+ next if class_name == "Label"
43
+
44
+ if SCHEME_MAPPINGS[class_name] && acc[:scheme].nil?
45
+ acc[:scheme] = SCHEME_MAPPINGS[class_name]
46
+ elsif VARIANT_MAPPINGS[class_name] && acc[:variant].nil?
47
+ acc[:variant] = VARIANT_MAPPINGS[class_name]
48
+ else
49
+ raise ConversionError, "Cannot convert class \"#{class_name}\""
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "argument_mappers/conversion_error"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ # Helper methods for autocorrectable ERB linters.
8
+ module Autocorrectable
9
+ def map_arguments(tag)
10
+ self.class::ARGUMENT_MAPPER.new(tag).to_s
11
+ rescue ArgumentMappers::ConversionError
12
+ nil
13
+ end
14
+
15
+ def correction(args)
16
+ return nil if args.nil?
17
+
18
+ correction = "<%= render #{self.class::COMPONENT}.new"
19
+ correction += "(#{args})" if args.present?
20
+ "#{correction} do %>"
21
+ end
22
+
23
+ def message(args)
24
+ return self.class::MESSAGE if args.nil?
25
+
26
+ "#{self.class::MESSAGE}\n\nTry using:\n\n#{correction(args)}\n\nInstead of:\n"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "helpers"
4
+ require_relative "autocorrectable"
4
5
  require_relative "argument_mappers/button"
5
6
 
6
7
  module ERBLint
@@ -8,32 +9,17 @@ module ERBLint
8
9
  # Counts the number of times a HTML button is used instead of the component.
9
10
  class ButtonComponentMigrationCounter < Linter
10
11
  include Helpers
12
+ include Autocorrectable
13
+
14
+ TAGS = Primer::ViewComponents::Constants.get(
15
+ component: "Primer::BaseButton",
16
+ constant: "TAG_OPTIONS"
17
+ ).freeze
11
18
 
12
- TAGS = %w[button summary a].freeze
13
19
  CLASSES = %w[btn btn-link].freeze
14
20
  MESSAGE = "We are migrating buttons to use [Primer::ButtonComponent](https://primer.style/view-components/components/button), please try to use that instead of raw HTML."
15
-
16
- private
17
-
18
- def map_arguments(tag)
19
- ArgumentMappers::Button.new(tag).to_s
20
- rescue ArgumentMappers::ConversionError
21
- nil
22
- end
23
-
24
- def correction(args)
25
- return nil if args.nil?
26
-
27
- correction = "<%= render Primer::ButtonComponent.new"
28
- correction += "(#{args})" if args.present?
29
- "#{correction} do %>"
30
- end
31
-
32
- def message(args)
33
- return MESSAGE if args.nil?
34
-
35
- "#{MESSAGE}\n\nTry using:\n\n#{correction(args)}\n\nInstead of:\n"
36
- end
21
+ ARGUMENT_MAPPER = ArgumentMappers::Button
22
+ COMPONENT = "Primer::ButtonComponent"
37
23
  end
38
24
  end
39
25
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers"
4
+ require_relative "autocorrectable"
5
+ require_relative "argument_mappers/clipboard_copy"
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 ClipboardCopyComponentMigrationCounter < Linter
11
+ include Helpers
12
+ include Autocorrectable
13
+
14
+ TAGS = %w[clipboard-copy].freeze
15
+ CLASSES = %w[].freeze
16
+ 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
+ ARGUMENT_MAPPER = ArgumentMappers::ClipboardCopy
18
+ COMPONENT = "Primer::ClipboardCopy"
19
+ end
20
+ end
21
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "json"
4
4
  require "openssl"
5
+ require "primer/view_components/constants"
5
6
 
6
7
  module ERBLint
7
8
  module Linters
@@ -17,60 +18,60 @@ module ERBLint
17
18
 
18
19
  def self.included(base)
19
20
  base.include(ERBLint::LinterRegistry)
21
+ end
20
22
 
21
- define_method "run" do |processed_source|
22
- @total_offenses = 0
23
- @offenses_not_corrected = 0
24
- tags = tags(processed_source)
25
- tag_tree = build_tag_tree(tags)
23
+ def run(processed_source)
24
+ @total_offenses = 0
25
+ @offenses_not_corrected = 0
26
+ tags = tags(processed_source)
27
+ tag_tree = build_tag_tree(tags)
26
28
 
27
- tags.each do |tag|
28
- next if tag.closing?
29
- next unless self.class::TAGS&.include?(tag.name)
29
+ tags.each do |tag|
30
+ next if tag.closing?
31
+ next unless self.class::TAGS&.include?(tag.name)
30
32
 
31
- classes = tag.attributes["class"]&.value&.split(" ") || []
33
+ classes = tag.attributes["class"]&.value&.split(" ") || []
32
34
 
33
- tag_tree[tag][:offense] = false
35
+ tag_tree[tag][:offense] = false
34
36
 
35
- next unless self.class::CLASSES.blank? || (classes & self.class::CLASSES).any?
37
+ next unless self.class::CLASSES.blank? || (classes & self.class::CLASSES).any?
36
38
 
37
- args = map_arguments(tag)
38
- correction = correction(args)
39
+ args = map_arguments(tag)
40
+ correction = correction(args)
39
41
 
40
- tag_tree[tag][:offense] = true
41
- tag_tree[tag][:correctable] = !correction.nil?
42
- tag_tree[tag][:message] = message(args)
43
- tag_tree[tag][:correction] = correction
44
- end
42
+ tag_tree[tag][:offense] = true
43
+ tag_tree[tag][:correctable] = !correction.nil?
44
+ tag_tree[tag][:message] = message(args)
45
+ tag_tree[tag][:correction] = correction
46
+ end
45
47
 
46
- tag_tree.each do |tag, h|
47
- next unless h[:offense]
48
-
49
- @total_offenses += 1
50
- # We always fix the offenses using blocks. The closing tag corresponds to `<% end %>`.
51
- if h[:correctable]
52
- add_offense(tag.loc, h[:message], h[:correction])
53
- add_offense(h[:closing].loc, h[:message], "<% end %>")
54
- else
55
- @offenses_not_corrected += 1
56
- generate_offense(self.class, processed_source, tag, h[:message])
57
- end
48
+ tag_tree.each do |tag, h|
49
+ next unless h[:offense]
50
+
51
+ @total_offenses += 1
52
+ # We always fix the offenses using blocks. The closing tag corresponds to `<% end %>`.
53
+ if h[:correctable]
54
+ add_offense(tag.loc, h[:message], h[:correction])
55
+ add_offense(h[:closing].loc, h[:message], "<% end %>")
56
+ else
57
+ @offenses_not_corrected += 1
58
+ generate_offense(self.class, processed_source, tag, h[:message])
58
59
  end
60
+ end
59
61
 
60
- counter_correct?(processed_source)
62
+ counter_correct?(processed_source)
61
63
 
62
- dump_data(processed_source) if ENV["DUMP_LINT_DATA"] == "1"
63
- end
64
+ dump_data(processed_source) if ENV["DUMP_LINT_DATA"] == "1"
65
+ end
64
66
 
65
- define_method "autocorrect" do |processed_source, offense|
66
- return unless offense.context
67
+ def autocorrect(processed_source, offense)
68
+ return unless offense.context
67
69
 
68
- lambda do |corrector|
69
- if offense.context.include?(counter_disable)
70
- correct_counter(corrector, processed_source, offense)
71
- else
72
- corrector.replace(offense.source_range, offense.context)
73
- end
70
+ lambda do |corrector|
71
+ if offense.context.include?(counter_disable)
72
+ correct_counter(corrector, processed_source, offense)
73
+ else
74
+ corrector.replace(offense.source_range, offense.context)
74
75
  end
75
76
  end
76
77
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers"
4
+ require_relative "autocorrectable"
5
+ require_relative "argument_mappers/label"
6
+
7
+ module ERBLint
8
+ module Linters
9
+ # Counts the number of times a HTML label is used instead of the component.
10
+ class LabelComponentMigrationCounter < Linter
11
+ include Helpers
12
+ include Autocorrectable
13
+
14
+ TAGS = Primer::ViewComponents::Constants.get(
15
+ component: "Primer::LabelComponent",
16
+ constant: "TAG_OPTIONS"
17
+ ).freeze
18
+
19
+ CLASSES = %w[Label].freeze
20
+ MESSAGE = "We are migrating labels to use [Primer::LabelComponent](https://primer.style/view-components/components/label), please try to use that instead of raw HTML."
21
+ ARGUMENT_MAPPER = ArgumentMappers::Label
22
+ COMPONENT = "Primer::LabelComponent"
23
+ end
24
+ end
25
+ end