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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +136 -0
- data/app/components/primer/alpha/underline_nav.html.erb +15 -0
- data/app/components/primer/alpha/underline_nav.rb +143 -0
- data/app/components/primer/{underline_nav_component.html.erb → alpha/underline_panels.html.erb} +3 -8
- data/app/components/primer/alpha/underline_panels.rb +86 -0
- data/app/components/primer/base_component.rb +2 -2
- data/app/components/primer/beta/avatar_stack.rb +9 -9
- data/app/components/primer/{breadcrumb_component.html.erb → beta/breadcrumbs.html.erb} +0 -0
- data/app/components/primer/beta/breadcrumbs.rb +59 -0
- 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.rb +1 -1
- data/app/components/primer/dropdown.rb +7 -7
- data/app/components/primer/icon_button.rb +1 -1
- data/app/components/primer/navigation/tab_component.rb +7 -5
- data/app/components/primer/progress_bar_component.rb +0 -3
- data/app/components/primer/tab_nav_component.html.erb +1 -1
- 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/tabbed_component_helper.rb +3 -3
- data/app/lib/primer/underline_nav_helper.rb +44 -0
- data/app/lib/primer/view_helper.rb +1 -0
- 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 +2 -2
- data/lib/primer/classify/validation.rb +18 -0
- data/lib/primer/classify.rb +5 -15
- data/lib/primer/view_components/constants.rb +1 -1
- data/lib/primer/view_components/linters/argument_mappers/base.rb +34 -8
- data/lib/primer/view_components/linters/argument_mappers/button.rb +5 -6
- data/lib/primer/view_components/linters/argument_mappers/clipboard_copy.rb +4 -3
- 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 +48 -5
- data/lib/primer/view_components/linters/argument_mappers/label.rb +3 -4
- data/lib/primer/view_components/linters/argument_mappers/system_arguments.rb +5 -7
- 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 +110 -3
- 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 +277 -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 +7 -5
- data/lib/tasks/utilities.rake +5 -3
- data/lib/yard/docs_helper.rb +6 -3
- data/static/arguments.yml +62 -37
- data/static/classes.yml +9 -0
- data/static/constants.json +38 -23
- data/static/statuses.json +8 -5
- metadata +37 -15
- data/app/components/primer/auto_complete/auto_component.d.ts +0 -1
- data/app/components/primer/auto_complete/auto_component.js +0 -1
- data/app/components/primer/breadcrumb_component.rb +0 -57
- data/app/components/primer/underline_nav_component.rb +0 -187
data/lib/primer/classify/flex.rb
CHANGED
@@ -89,7 +89,7 @@ module Primer
|
|
89
89
|
def justify_content(value, breakpoint)
|
90
90
|
val = fetch_or_fallback(JUSTIFY_CONTENT_VALUES, value)
|
91
91
|
|
92
|
-
formatted_value = val.to_s.gsub(/(
|
92
|
+
formatted_value = val.to_s.gsub(/(flex_|space_)/, "")
|
93
93
|
"flex#{breakpoint}-justify-#{formatted_value}"
|
94
94
|
end
|
95
95
|
|
@@ -24,7 +24,9 @@ module Primer
|
|
24
24
|
"^v-align" => "vertical_align",
|
25
25
|
"^d" => "display",
|
26
26
|
"^wb" => "word_break",
|
27
|
-
"^v" => "visibility"
|
27
|
+
"^v" => "visibility",
|
28
|
+
"^width" => "w",
|
29
|
+
"^height" => "h"
|
28
30
|
}.freeze
|
29
31
|
|
30
32
|
class << self
|
@@ -83,7 +85,7 @@ module Primer
|
|
83
85
|
return { classes: classes } if ENV["RAILS_ENV"] == "production"
|
84
86
|
|
85
87
|
obj = {}
|
86
|
-
classes = classes.split
|
88
|
+
classes = classes.split
|
87
89
|
# Loop through all classes supplied and reject ones we find a match for
|
88
90
|
# So when we're at the end of the loop we have classes left with any non-system classes.
|
89
91
|
classes.reject! do |classname|
|
@@ -112,6 +114,21 @@ module Primer
|
|
112
114
|
obj
|
113
115
|
end
|
114
116
|
|
117
|
+
def classes_to_args(classes)
|
118
|
+
classes_to_hash(classes).map do |key, value|
|
119
|
+
val = case value
|
120
|
+
when Symbol
|
121
|
+
":#{value}"
|
122
|
+
when String
|
123
|
+
value.to_json
|
124
|
+
else
|
125
|
+
value
|
126
|
+
end
|
127
|
+
|
128
|
+
"#{key}: #{val}"
|
129
|
+
end.join(", ")
|
130
|
+
end
|
131
|
+
|
115
132
|
private
|
116
133
|
|
117
134
|
def find_selector(selector)
|
@@ -85,7 +85,7 @@
|
|
85
85
|
- float-md-none
|
86
86
|
- float-lg-none
|
87
87
|
- float-xl-none
|
88
|
-
:
|
88
|
+
:w:
|
89
89
|
:fit:
|
90
90
|
- width-fit
|
91
91
|
:full:
|
@@ -96,7 +96,7 @@
|
|
96
96
|
- width-md-auto
|
97
97
|
- width-lg-auto
|
98
98
|
- width-xl-auto
|
99
|
-
:
|
99
|
+
:h:
|
100
100
|
:fit:
|
101
101
|
- height-fit
|
102
102
|
:full:
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "utilities"
|
4
|
+
|
5
|
+
module Primer
|
6
|
+
class Classify
|
7
|
+
# :nodoc:
|
8
|
+
class Validation
|
9
|
+
INVALID_CLASS_NAME_PREFIXES = /bg-|color-|text-|box-shadow-|text-|box_shadow-/.freeze
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def invalid?(class_name)
|
13
|
+
class_name.start_with?(INVALID_CLASS_NAME_PREFIXES) || Primer::Classify::Utilities.supported_selector?(class_name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/primer/classify.rb
CHANGED
@@ -7,6 +7,7 @@ require_relative "classify/functional_border_colors"
|
|
7
7
|
require_relative "classify/functional_text_colors"
|
8
8
|
require_relative "classify/grid"
|
9
9
|
require_relative "classify/utilities"
|
10
|
+
require_relative "classify/validation"
|
10
11
|
|
11
12
|
module Primer
|
12
13
|
# :nodoc:
|
@@ -14,14 +15,9 @@ module Primer
|
|
14
15
|
# Keys where we can simply translate { key: value } into ".key-value"
|
15
16
|
CONCAT_KEYS = %i[text box_shadow].freeze
|
16
17
|
|
17
|
-
INVALID_CLASS_NAME_PREFIXES =
|
18
|
-
(["bg-", "color-", "text-", "box-shadow-"] + CONCAT_KEYS.map { |k| "#{k}-" }).freeze
|
19
|
-
|
20
18
|
COLOR_KEY = :color
|
21
19
|
BG_KEY = :bg
|
22
20
|
TEXT_KEYS = %i[font_family font_style font_weight text_align text_transform].freeze
|
23
|
-
WIDTH_KEY = :width
|
24
|
-
HEIGHT_KEY = :height
|
25
21
|
BOX_SHADOW_KEY = :box_shadow
|
26
22
|
CONTAINER_KEY = :container
|
27
23
|
|
@@ -94,8 +90,6 @@ module Primer
|
|
94
90
|
BORDER_RADIUS_KEY,
|
95
91
|
COLOR_KEY,
|
96
92
|
BG_KEY,
|
97
|
-
WIDTH_KEY,
|
98
|
-
HEIGHT_KEY,
|
99
93
|
BOX_SHADOW_KEY,
|
100
94
|
CONTAINER_KEY
|
101
95
|
]
|
@@ -113,7 +107,7 @@ module Primer
|
|
113
107
|
extracted_results[:style] = [
|
114
108
|
extracted_results.delete(:styles),
|
115
109
|
style
|
116
|
-
].compact.join
|
110
|
+
].compact.join.presence
|
117
111
|
|
118
112
|
extracted_results
|
119
113
|
end
|
@@ -125,8 +119,8 @@ module Primer
|
|
125
119
|
|
126
120
|
if force_system_arguments? && !ENV["PRIMER_WARNINGS_DISABLED"]
|
127
121
|
invalid_class_names =
|
128
|
-
classes.split
|
129
|
-
memo << class_name if
|
122
|
+
classes.split.each_with_object([]) do |class_name, memo|
|
123
|
+
memo << class_name if Primer::Classify::Validation.invalid?(class_name)
|
130
124
|
end
|
131
125
|
|
132
126
|
raise ArgumentError, "Use System Arguments (https://primer.style/view-components/system-arguments) instead of Primer CSS class #{'name'.pluralize(invalid_class_names.length)} #{invalid_class_names.to_sentence}. This warning will not be raised in production. Set PRIMER_WARNINGS_DISABLED=1 to disable this warning." if invalid_class_names.any?
|
@@ -170,9 +164,7 @@ module Primer
|
|
170
164
|
def extract_value(memo, key, val, breakpoint)
|
171
165
|
return if val.nil? || val == ""
|
172
166
|
|
173
|
-
if
|
174
|
-
memo[key] = val
|
175
|
-
elsif Primer::Classify::Utilities.supported_key?(key)
|
167
|
+
if Primer::Classify::Utilities.supported_key?(key)
|
176
168
|
memo[:classes] << Primer::Classify::Utilities.classname(key, val, breakpoint)
|
177
169
|
elsif BOOLEAN_MAPPINGS.key?(key)
|
178
170
|
BOOLEAN_MAPPINGS[key][:mappings].each do |m|
|
@@ -220,8 +212,6 @@ module Primer
|
|
220
212
|
else
|
221
213
|
"color-shadow-#{val.to_s.dasherize}"
|
222
214
|
end
|
223
|
-
else
|
224
|
-
memo[:classes] << "#{key.to_s.dasherize}#{breakpoint}-#{val.to_s.dasherize}"
|
225
215
|
end
|
226
216
|
end
|
227
217
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "primer/view_components/constants"
|
4
4
|
require "primer/classify/utilities"
|
5
|
+
require "primer/classify/validation"
|
5
6
|
require_relative "conversion_error"
|
6
7
|
require_relative "system_arguments"
|
7
8
|
require_relative "helpers/erb_block"
|
@@ -46,18 +47,37 @@ module ERBLint
|
|
46
47
|
|
47
48
|
def attribute_to_args(attribute); end
|
48
49
|
|
49
|
-
def map_classes(
|
50
|
-
|
51
|
-
args = classes_to_args(system_arguments[:classes])
|
50
|
+
def map_classes(classes_node)
|
51
|
+
erb_helper.raise_if_erb_block(classes_node)
|
52
52
|
|
53
|
-
|
53
|
+
system_arguments = system_arguments_to_args(classes_node.value)
|
54
|
+
args = classes_to_args(system_arguments[:classes]&.split || [])
|
55
|
+
|
56
|
+
invalid_classes = args[:classes].select { |class_name| Primer::Classify::Validation.invalid?(class_name) }
|
57
|
+
|
58
|
+
raise ConversionError, "Cannot convert #{'class'.pluralize(invalid_classes.size)} #{invalid_classes.join(',')}" if invalid_classes.present?
|
59
|
+
|
60
|
+
# Using splat to order the arguments in Component's args -> System Args -> custom classes
|
61
|
+
res = {
|
62
|
+
**args.except(:classes),
|
63
|
+
**system_arguments.except(:classes)
|
64
|
+
}
|
65
|
+
|
66
|
+
if args[:classes].present?
|
67
|
+
res = {
|
68
|
+
**res,
|
69
|
+
classes: args[:classes].join(" ").to_json
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
res
|
54
74
|
end
|
55
75
|
|
56
|
-
# Override this with your component's mappings
|
76
|
+
# Override this with your component's mappings, it should return a hash with the component's arguments,
|
77
|
+
# including a `classes` key that will contain all classes that the mapper couldn't handle.
|
78
|
+
# @returns { classes: Array, ... }
|
57
79
|
def classes_to_args(classes)
|
58
|
-
|
59
|
-
|
60
|
-
{}
|
80
|
+
{ classes: classes }
|
61
81
|
end
|
62
82
|
|
63
83
|
def system_arguments_to_args(classes)
|
@@ -68,6 +88,12 @@ module ERBLint
|
|
68
88
|
v.is_a?(Symbol) ? ":#{v}" : v
|
69
89
|
end
|
70
90
|
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def erb_helper
|
95
|
+
@erb_helper ||= Helpers::ErbBlock.new
|
96
|
+
end
|
71
97
|
end
|
72
98
|
end
|
73
99
|
end
|
@@ -7,8 +7,6 @@ module ERBLint
|
|
7
7
|
module ArgumentMappers
|
8
8
|
# Maps classes in a button element to arguments for the Button component.
|
9
9
|
class Button < Base
|
10
|
-
require "pry"
|
11
|
-
|
12
10
|
SCHEME_MAPPINGS = Primer::ViewComponents::Constants.get(
|
13
11
|
component: "Primer::ButtonComponent",
|
14
12
|
constant: "SCHEME_MAPPINGS",
|
@@ -35,9 +33,10 @@ module ERBLint
|
|
35
33
|
def attribute_to_args(attribute)
|
36
34
|
attr_name = attribute.name
|
37
35
|
|
38
|
-
|
36
|
+
case attr_name
|
37
|
+
when "disabled"
|
39
38
|
{ disabled: true }
|
40
|
-
|
39
|
+
when "type"
|
41
40
|
# button is the default type, so we don't need to do anything.
|
42
41
|
return {} if attribute.value == "button"
|
43
42
|
|
@@ -48,7 +47,7 @@ module ERBLint
|
|
48
47
|
end
|
49
48
|
|
50
49
|
def classes_to_args(classes)
|
51
|
-
classes.
|
50
|
+
classes.each_with_object({ classes: [] }) do |class_name, acc|
|
52
51
|
next if class_name == "btn"
|
53
52
|
|
54
53
|
if SCHEME_MAPPINGS[class_name] && acc[:scheme].nil?
|
@@ -60,7 +59,7 @@ module ERBLint
|
|
60
59
|
elsif class_name == "BtnGroup-item"
|
61
60
|
acc[:group_item] = true
|
62
61
|
else
|
63
|
-
|
62
|
+
acc[:classes] << class_name
|
64
63
|
end
|
65
64
|
end
|
66
65
|
end
|
@@ -8,11 +8,12 @@ module ERBLint
|
|
8
8
|
# Maps attributes in the clipboard-copy element to arguments for the ClipboardCopy component.
|
9
9
|
class ClipboardCopy < Base
|
10
10
|
DEFAULT_TAG = "clipboard-copy"
|
11
|
-
ATTRIBUTES = %w[value].freeze
|
11
|
+
ATTRIBUTES = %w[role tabindex for value id style].freeze
|
12
12
|
|
13
13
|
def attribute_to_args(attribute)
|
14
|
-
|
15
|
-
|
14
|
+
attr_name = attribute.name
|
15
|
+
|
16
|
+
{ attr_name.to_sym => erb_helper.convert(attribute) }
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
@@ -0,0 +1,43 @@
|
|
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 close-button element to arguments for the CloseButton component.
|
9
|
+
class CloseButton < Base
|
10
|
+
ATTRIBUTES = %w[type].freeze
|
11
|
+
|
12
|
+
TYPE_OPTIONS = Primer::ViewComponents::Constants.get(
|
13
|
+
component: "Primer::CloseButton",
|
14
|
+
constant: "TYPE_OPTIONS"
|
15
|
+
).freeze
|
16
|
+
|
17
|
+
DEFAULT_TYPE = Primer::ViewComponents::Constants.get(
|
18
|
+
component: "Primer::CloseButton",
|
19
|
+
constant: "DEFAULT_TYPE"
|
20
|
+
).freeze
|
21
|
+
|
22
|
+
DEFAULT_CLASS = "close-button"
|
23
|
+
|
24
|
+
def attribute_to_args(attribute)
|
25
|
+
# button is the default type, so we don't need to do anything.
|
26
|
+
return {} if attribute.value == DEFAULT_TYPE
|
27
|
+
|
28
|
+
raise ConversionError, "CloseButton component does not support type \"#{attribute.value}\"" unless TYPE_OPTIONS.include?(attribute.value)
|
29
|
+
|
30
|
+
{ type: ":#{attribute.value}" }
|
31
|
+
end
|
32
|
+
|
33
|
+
def classes_to_args(classes)
|
34
|
+
classes.each_with_object({ classes: [] }) do |class_name, acc|
|
35
|
+
next if class_name == DEFAULT_CLASS
|
36
|
+
|
37
|
+
acc[:classes] << class_name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,32 @@
|
|
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 flash element to arguments for the Flash component.
|
9
|
+
class Flash < Base
|
10
|
+
SCHEME_MAPPINGS = Primer::ViewComponents::Constants.get(
|
11
|
+
component: "Primer::FlashComponent",
|
12
|
+
constant: "SCHEME_MAPPINGS",
|
13
|
+
symbolize: true
|
14
|
+
).freeze
|
15
|
+
|
16
|
+
def classes_to_args(classes)
|
17
|
+
classes.each_with_object({ classes: [] }) do |class_name, acc|
|
18
|
+
next if class_name == "flash"
|
19
|
+
|
20
|
+
if SCHEME_MAPPINGS[class_name] && acc[:scheme].nil?
|
21
|
+
acc[:scheme] = SCHEME_MAPPINGS[class_name]
|
22
|
+
elsif class_name == "flash-full"
|
23
|
+
acc[:full] = true
|
24
|
+
else
|
25
|
+
acc[:classes] << class_name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -8,15 +8,58 @@ module ERBLint
|
|
8
8
|
module Helpers
|
9
9
|
# provides helpers to identify and deal with ERB blocks.
|
10
10
|
class ErbBlock
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
INTERPOLATION_REGEX = /^<%=(?<rb>.*)%>$/.freeze
|
12
|
+
|
13
|
+
def raise_if_erb_block(attribute)
|
14
|
+
raise_error(attribute) if any?(attribute)
|
15
|
+
end
|
16
|
+
|
17
|
+
def convert(attribute)
|
18
|
+
raise_error(attribute) unless interpolation?(attribute)
|
19
|
+
|
20
|
+
if any?(attribute)
|
21
|
+
convert_interpolation(attribute)
|
22
|
+
else
|
23
|
+
attribute.value.to_json
|
14
24
|
end
|
25
|
+
end
|
15
26
|
|
16
|
-
|
17
|
-
|
27
|
+
private
|
28
|
+
|
29
|
+
def interpolation?(attribute)
|
30
|
+
erb_blocks(attribute).all? do |erb|
|
31
|
+
# If the blocks does not have an indicator, it's not an interpolation.
|
32
|
+
erb.children.to_a.compact.any? { |node| node.type == :indicator }
|
18
33
|
end
|
19
34
|
end
|
35
|
+
|
36
|
+
def raise_error(attribute)
|
37
|
+
raise ERBLint::Linters::ArgumentMappers::ConversionError, "Cannot convert attribute \"#{attribute.name}\" because its value contains an erb block"
|
38
|
+
end
|
39
|
+
|
40
|
+
def any?(attribute)
|
41
|
+
erb_blocks(attribute).any?
|
42
|
+
end
|
43
|
+
|
44
|
+
def basic?(attribute)
|
45
|
+
return false if erb_blocks(attribute).size != 1
|
46
|
+
|
47
|
+
attribute.value.match?(INTERPOLATION_REGEX)
|
48
|
+
end
|
49
|
+
|
50
|
+
def erb_blocks(attribute)
|
51
|
+
(attribute.value_node&.children || []).select { |n| n.try(:type) == :erb }
|
52
|
+
end
|
53
|
+
|
54
|
+
def convert_interpolation(attribute)
|
55
|
+
if basic?(attribute)
|
56
|
+
m = attribute.value.match(INTERPOLATION_REGEX)
|
57
|
+
return m[:rb].strip
|
58
|
+
end
|
59
|
+
|
60
|
+
# we use `source` instead of `value` because it does not convert encoded HTML entities.
|
61
|
+
attribute.value_node.loc.source.gsub("<%=", '#{').gsub("%>", "}")
|
62
|
+
end
|
20
63
|
end
|
21
64
|
end
|
22
65
|
end
|