primer_view_components 0.0.59 → 0.0.60

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -0
  3. data/app/assets/javascripts/primer_view_components.js +1 -1
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  5. data/app/components/primer/alpha/border_box/header.html.erb +4 -0
  6. data/app/components/primer/alpha/border_box/header.rb +51 -0
  7. data/app/components/primer/alpha/tab_nav.rb +1 -2
  8. data/app/components/primer/base_component.rb +2 -2
  9. data/app/components/primer/beta/avatar.rb +18 -11
  10. data/app/components/primer/beta/breadcrumbs.rb +7 -0
  11. data/app/components/primer/border_box_component.rb +8 -12
  12. data/app/components/primer/clipboard_copy.html.erb +1 -1
  13. data/app/components/primer/component.rb +1 -0
  14. data/app/components/primer/label_component.rb +7 -7
  15. data/app/components/primer/markdown.rb +0 -10
  16. data/app/components/primer/spinner_component.html.erb +7 -4
  17. data/app/components/primer/timeline_item_component.rb +2 -2
  18. data/app/lib/primer/audited/dsl.rb +32 -0
  19. data/lib/primer/classify/cache.rb +0 -16
  20. data/lib/primer/classify/utilities.rb +13 -6
  21. data/lib/primer/classify/utilities.yml +199 -22
  22. data/lib/primer/classify.rb +2 -9
  23. data/lib/primer/view_components/engine.rb +6 -0
  24. data/lib/primer/view_components/linters/blankslate_component_migration_counter.rb +14 -0
  25. data/lib/primer/view_components/linters/subhead_component_migration_counter.rb +14 -0
  26. data/lib/primer/view_components/version.rb +1 -1
  27. data/lib/primer/view_components.rb +17 -29
  28. data/lib/rubocop/cop/primer/deprecated_arguments.rb +35 -1
  29. data/lib/rubocop/cop/primer/primer_octicon.rb +25 -4
  30. data/lib/tasks/docs.rake +2 -0
  31. data/lib/tasks/helpers/ast_processor.rb +44 -0
  32. data/lib/tasks/helpers/ast_traverser.rb +77 -0
  33. data/lib/tasks/primer_view_components.rake +47 -0
  34. data/lib/tasks/{constants.rake → static.rake} +5 -2
  35. data/lib/tasks/utilities.rake +40 -26
  36. data/static/arguments.yml +36 -5
  37. data/static/audited_at.json +61 -0
  38. data/static/classes.yml +3 -0
  39. data/static/constants.json +26 -0
  40. data/static/statuses.json +1 -0
  41. metadata +14 -7
  42. data/lib/primer/classify/grid.rb +0 -45
  43. data/lib/tasks/statuses.rake +0 -12
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Alpha
5
+ module BorderBox
6
+ # BorderBox::Header: used inside the BorderBoxComponent to render its header slot
7
+ # Optional title slot
8
+ #
9
+ # @accessibility When using `header.title`, set `tag` to one of `h1`, `h2`, `h3`, etc. based on what is appropriate for the page context. <%= link_to_heading_practices %>
10
+ class Header < Primer::Component
11
+ TITLE_TAG_FALLBACK = :h2
12
+ TITLE_TAG_OPTIONS = [:h1, TITLE_TAG_FALLBACK, :h3, :h4, :h5, :h6].freeze
13
+
14
+ # Optional Title.
15
+ #
16
+ # @param tag [Symbol] <%= one_of(Primer::Alpha::BorderBox::Header::TITLE_TAG_OPTIONS) %>
17
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
18
+ renders_one :title, lambda { |tag:, **system_arguments|
19
+ system_arguments[:tag] = fetch_or_fallback(TITLE_TAG_OPTIONS, tag, TITLE_TAG_FALLBACK)
20
+ system_arguments[:classes] = class_names(
21
+ "Box-title",
22
+ system_arguments[:classes]
23
+ )
24
+
25
+ Primer::BaseComponent.new(**system_arguments)
26
+ }
27
+
28
+ # @example default use case
29
+ #
30
+ # <%= render(Primer::Alpha::BorderBox::Header.new) do %>
31
+ # Header
32
+ # <% end %>
33
+ #
34
+ # @example with title
35
+ # <%= render(Primer::Alpha::BorderBox::Header.new) do |h| %>
36
+ # <% h.title(tag: :h3) do %>I am a title<% end %>
37
+ # <% end %>
38
+ #
39
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
40
+ def initialize(**system_arguments)
41
+ @system_arguments = system_arguments
42
+ @system_arguments[:tag] = :div
43
+ @system_arguments[:classes] = class_names(
44
+ "Box-header",
45
+ system_arguments[:classes]
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -119,9 +119,8 @@ module Primer
119
119
  @system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, TAG_DEFAULT)
120
120
  @system_arguments[:classes] = tab_nav_classes(system_arguments[:classes])
121
121
 
122
- @body_arguments = body_arguments
123
122
  @body_arguments[:tag] = BODY_TAG_DEFAULT
124
- @body_arguments[:classes] = tab_nav_body_classes(system_arguments[:classes])
123
+ @body_arguments[:classes] = tab_nav_body_classes(body_arguments[:classes])
125
124
 
126
125
  aria_label_for_page_nav(label)
127
126
  end
@@ -85,8 +85,8 @@ module Primer
85
85
  # | Name | Type | Description |
86
86
  # | :- | :- | :- |
87
87
  # | `clearfix` | Boolean | Wether to assign the `clearfix` class. |
88
- # | `col` | Integer | Number of columns. <%= one_of(Primer::Classify::Grid::COL_VALUES) %> |
89
- # | `container` | Symbol | Size of the container. <%= one_of(Primer::Classify::Grid::CONTAINER_VALUES) %> |
88
+ # | `col` | Integer | Number of columns. <%= one_of(Primer::Classify::Utilities.mappings(:col)) %> |
89
+ # | `container` | Symbol | Size of the container. <%= one_of(Primer::Classify::Utilities.mappings(:container)) %> |
90
90
  #
91
91
  # ## Layout
92
92
  #
@@ -4,7 +4,7 @@ module Primer
4
4
  module Beta
5
5
  # `Avatar` can be used to represent users and organizations on GitHub.
6
6
  #
7
- # - Use the default round avatar for users, and the `square` argument
7
+ # - Use the default circle avatar for users, and the square shape
8
8
  # for organizations or any other non-human avatars.
9
9
  # - By default, `Avatar` will render a static `<img>`. To have `Avatar` function as a link, set the `href` which will wrap the `<img>` in a `<a>`.
10
10
  # - Set `size` to update the height and width of the `Avatar` in pixels.
@@ -19,13 +19,19 @@ module Primer
19
19
  class Avatar < Primer::Component
20
20
  status :beta
21
21
 
22
+ DEFAULT_SIZE = 20
22
23
  SMALL_THRESHOLD = 24
23
24
 
25
+ DEFAULT_SHAPE = :circle
26
+ SHAPE_OPTIONS = [DEFAULT_SHAPE, :square].freeze
27
+
28
+ SIZE_OPTIONS = [16, DEFAULT_SIZE, SMALL_THRESHOLD, 32, 40, 48, 80].freeze
29
+
24
30
  # @example Default
25
31
  # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser")) %>
26
32
  #
27
33
  # @example Square
28
- # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", square: true)) %>
34
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", shape: :square)) %>
29
35
  #
30
36
  # @example Link
31
37
  # <%= render(Primer::Beta::Avatar.new(href: "#", src: "http://placekitten.com/200/200", alt: "@kittenuser profile")) %>
@@ -34,31 +40,32 @@ module Primer
34
40
  # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 16)) %>
35
41
  # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 20)) %>
36
42
  # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 24)) %>
37
- # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 28)) %>
38
43
  # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 32)) %>
39
- # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 36)) %>
44
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 40)) %>
45
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 48)) %>
46
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 80)) %>
40
47
  #
41
48
  # @param src [String] The source url of the avatar image.
42
49
  # @param alt [String] Passed through to alt on img tag.
43
- # @param size [Integer] Adds the avatar-small class if less than 24.
44
- # @param square [Boolean] Used to create a square avatar.
50
+ # @param size [Integer] <%= one_of(Primer::Beta::Avatar::SIZE_OPTIONS) %>
51
+ # @param shape [Symbol] Shape of the avatar. <%= one_of(Primer::Beta::Avatar::SHAPE_OPTIONS) %>
45
52
  # @param href [String] The URL to link to. If used, component will be wrapped by an `<a>` tag.
46
53
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
47
- def initialize(src:, alt:, size: 20, square: false, href: nil, **system_arguments)
54
+ def initialize(src:, alt:, size: DEFAULT_SIZE, shape: DEFAULT_SHAPE, href: nil, **system_arguments)
48
55
  @href = href
49
56
  @system_arguments = system_arguments
50
57
  @system_arguments[:tag] = :img
51
58
  @system_arguments[:src] = src
52
59
  @system_arguments[:alt] = alt
53
- @system_arguments[:size] = size
54
- @system_arguments[:height] = size
55
- @system_arguments[:width] = size
60
+ @system_arguments[:size] = fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)
61
+ @system_arguments[:height] = @system_arguments[:size]
62
+ @system_arguments[:width] = @system_arguments[:size]
56
63
 
57
64
  @system_arguments[:classes] = class_names(
58
65
  system_arguments[:classes],
59
66
  "avatar",
60
67
  "avatar-small" => size < SMALL_THRESHOLD,
61
- "circle" => !square,
68
+ "circle" => shape == DEFAULT_SHAPE,
62
69
  "lh-0" => href # Addresses an overflow issue with linked avatars
63
70
  )
64
71
  end
@@ -3,6 +3,13 @@
3
3
  module Primer
4
4
  module Beta
5
5
  # Use `Breadcrumbs` to display page hierarchy.
6
+ #
7
+ # #### Known issues
8
+ #
9
+ # ##### Responsiveness
10
+ #
11
+ # `Breadcrumbs` is not optimized for responsive designs.
12
+ #
6
13
  # @accessibility
7
14
  # `Breadcrumbs` renders a list of links within a `nav` element and has an implicit landmark role of `navigation`.
8
15
  # By default, the component labels the `nav` element with "Breadcrumbs" which helps distinguish the type of navigation.
@@ -24,15 +24,9 @@ module Primer
24
24
  # Optional Header.
25
25
  #
26
26
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
27
- renders_one :header, lambda { |**system_arguments|
28
- system_arguments[:tag] = :div
29
- system_arguments[:classes] = class_names(
30
- "Box-header",
31
- system_arguments[:classes]
32
- )
33
-
34
- Primer::BaseComponent.new(**system_arguments)
35
- }
27
+ # @accessibility
28
+ # When using header.title, the recommended tag is a heading tag, such as h1, h2, h3, etc.
29
+ renders_one :header, "Primer::Alpha::BorderBox::Header"
36
30
 
37
31
  # Optional Body.
38
32
  #
@@ -75,10 +69,12 @@ module Primer
75
69
  Primer::BaseComponent.new(**system_arguments)
76
70
  }
77
71
 
78
- # @example Header, body, rows, and footer
72
+ # @example Header with title, body, rows, and footer
79
73
  # <%= render(Primer::BorderBoxComponent.new) do |component| %>
80
- # <% component.header do %>
81
- # Header
74
+ # <% component.header do |h| %>
75
+ # <% h.title(tag: :h2) do %>
76
+ # Header
77
+ # <% end %>
82
78
  # <% end %>
83
79
  # <% component.body do %>
84
80
  # Body
@@ -3,6 +3,6 @@
3
3
  <%= content %>
4
4
  <% else %>
5
5
  <%= render Primer::OcticonComponent.new(:copy) %>
6
- <%= render Primer::OcticonComponent.new(:check, color: :icon_success, style: "display: none;") %>
6
+ <%= render Primer::OcticonComponent.new(:check, color: :success, style: "display: none;") %>
7
7
  <% end %>
8
8
  <% end %>
@@ -12,6 +12,7 @@ module Primer
12
12
  include JoinStyleArgumentsHelper
13
13
  include ViewHelper
14
14
  include Status::Dsl
15
+ include Audited::Dsl
15
16
 
16
17
  private
17
18
 
@@ -33,16 +33,16 @@ module Primer
33
33
 
34
34
  # @example Schemes
35
35
  # <%= render(Primer::LabelComponent.new) { "Default" } %>
36
- # <%= render(Primer::LabelComponent.new( scheme: :primary)) { "Primary" } %>
37
- # <%= render(Primer::LabelComponent.new( scheme: :secondary)) { "Secondary" } %>
38
- # <%= render(Primer::LabelComponent.new( scheme: :info)) { "Info" } %>
39
- # <%= render(Primer::LabelComponent.new( scheme: :success)) { "Success" } %>
40
- # <%= render(Primer::LabelComponent.new( scheme: :warning)) { "Warning" } %>
41
- # <%= render(Primer::LabelComponent.new( scheme: :danger)) { "Danger" } %>
36
+ # <%= render(Primer::LabelComponent.new(scheme: :primary)) { "Primary" } %>
37
+ # <%= render(Primer::LabelComponent.new(scheme: :secondary)) { "Secondary" } %>
38
+ # <%= render(Primer::LabelComponent.new(scheme: :info)) { "Info" } %>
39
+ # <%= render(Primer::LabelComponent.new(scheme: :success)) { "Success" } %>
40
+ # <%= render(Primer::LabelComponent.new(scheme: :warning)) { "Warning" } %>
41
+ # <%= render(Primer::LabelComponent.new(scheme: :danger)) { "Danger" } %>
42
42
  #
43
43
  # @example Variants
44
44
  # <%= render(Primer::LabelComponent.new) { "Default" } %>
45
- # <%= render(Primer::LabelComponent.new( variant: :large)) { "Large" } %>
45
+ # <%= render(Primer::LabelComponent.new(variant: :large)) { "Large" } %>
46
46
  #
47
47
  # @param tag [Symbol] <%= one_of(Primer::LabelComponent::TAG_OPTIONS) %>
48
48
  # @param scheme [Symbol] <%= one_of(Primer::LabelComponent::SCHEME_MAPPINGS.keys) %>
@@ -266,16 +266,6 @@ module Primer
266
266
  # </tbody>
267
267
  # </table>
268
268
  #
269
- # <hr />
270
- #
271
- # <p>Small images should be shown at their actual size.</p>
272
- #
273
- # <p><img alt="kitten" src="http://placekitten.com/g/300/200/"/></p>
274
- #
275
- # <p>Large images should always scale down and fit in the content container.</p>
276
- #
277
- # <p><img alt="kitten" src="http://placekitten.com/g/1200/800/"/></p>
278
- #
279
269
  # <pre><code>This is the final element on the page and there should be no margin below this.</code></pre>
280
270
  # <% end %>
281
271
  #
@@ -1,4 +1,7 @@
1
- <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
- <circle cx="8" cy="8" r="7" stroke="currentColor" stroke-opacity="0.25" stroke-width="2" vector-effect="non-scaling-stroke" />
3
- <path d="M15 8a7.002 7.002 0 00-7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" vector-effect="non-scaling-stroke" />
4
- <% end %>
1
+ <span role="status">
2
+ <span class="sr-only">Loading</span>
3
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
4
+ <circle cx="8" cy="8" r="7" stroke="currentColor" stroke-opacity="0.25" stroke-width="2" vector-effect="non-scaling-stroke" />
5
+ <path d="M15 8a7.002 7.002 0 00-7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" vector-effect="non-scaling-stroke" />
6
+ <% end %>
7
+ </span>
@@ -8,13 +8,13 @@ module Primer
8
8
  # Avatar to be rendered to the left of the Badge.
9
9
  #
10
10
  # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::Beta::Avatar) %>.
11
- renders_one :avatar, lambda { |src:, size: 40, square: true, **system_arguments|
11
+ renders_one :avatar, lambda { |src:, size: 40, shape: :square, **system_arguments|
12
12
  system_arguments[:classes] = class_names(
13
13
  "TimelineItem-avatar",
14
14
  system_arguments[:classes]
15
15
  )
16
16
 
17
- Primer::Beta::Avatar.new(src: src, size: size, square: square, **system_arguments)
17
+ Primer::Beta::Avatar.new(src: src, size: size, shape: shape, **system_arguments)
18
18
  }
19
19
 
20
20
  # Badge that will be connected to other TimelineItems.
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Primer
6
+ # :nodoc:
7
+ module Audited
8
+ # DSL to register when a component has passed an accessibility audit.
9
+ #
10
+ # Example:
11
+ #
12
+ # class MyComponent < ViewComponent::Base
13
+ # include Primer::Audited::Dsl
14
+ # audited_at 'YYYY-MM-DD'
15
+ # end
16
+ module Dsl
17
+ extend ActiveSupport::Concern
18
+
19
+ included do
20
+ class_attribute :audit_date, instance_writer: false
21
+ end
22
+
23
+ class_methods do
24
+ def audited_at(date = nil)
25
+ return audit_date if date.nil?
26
+
27
+ self.audit_date = date
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "flex"
4
- require_relative "grid"
5
4
 
6
5
  module Primer
7
6
  class Classify
@@ -51,21 +50,6 @@ module Primer
51
50
  values: Primer::Classify::Flex::ALIGN_ITEMS_VALUES
52
51
  )
53
52
 
54
- preload(
55
- keys: Primer::Classify::Grid::CONTAINER_KEY,
56
- values: Primer::Classify::Grid::CONTAINER_VALUES
57
- )
58
-
59
- preload(
60
- keys: Primer::Classify::Grid::CLEARFIX_KEY,
61
- values: [true]
62
- )
63
-
64
- preload(
65
- keys: Primer::Classify::Grid::COL_KEY,
66
- values: Primer::Classify::Grid::COL_VALUES
67
- )
68
-
69
53
  preload(
70
54
  keys: :text_align,
71
55
  values: [:left, :center, :right]
@@ -28,14 +28,21 @@ module Primer
28
28
  "^width" => "w",
29
29
  "^height" => "h",
30
30
  "^color-bg" => "bg",
31
- "^color-border" => "border_color"
31
+ "^color-border" => "border_color",
32
+ "^color-fg" => "color"
32
33
  }.freeze
33
34
 
34
35
  SUPPORTED_KEY_CACHE = Hash.new { |h, k| h[k] = !UTILITIES[k].nil? }
35
36
  BREAKPOINT_INDEX_CACHE = Hash.new { |h, k| h[k] = BREAKPOINTS.index(k) }
36
37
 
37
38
  class << self
39
+ attr_accessor :validate_class_names
40
+ alias validate_class_names? validate_class_names
41
+
38
42
  def classname(key, val, breakpoint = "")
43
+ # For cases when `argument: false` is passed in, treat like we would nil
44
+ return nil unless val
45
+
39
46
  if (valid = validate(key, val, breakpoint))
40
47
  valid
41
48
  else
@@ -63,7 +70,7 @@ module Primer
63
70
  # returns Boolean
64
71
  def supported_selector?(selector)
65
72
  # This method is too slow to run in production
66
- return false if ENV["RAILS_ENV"] == "production"
73
+ return false unless validate_class_names?
67
74
 
68
75
  find_selector(selector).present?
69
76
  end
@@ -87,7 +94,7 @@ module Primer
87
94
  # Extract hash from classes ie. "mr-1 mb-2 foo" => { mr: 1, mb: 2, classes: "foo" }
88
95
  def classes_to_hash(classes)
89
96
  # This method is too slow to run in production
90
- return { classes: classes } if ENV["RAILS_ENV"] == "production"
97
+ return { classes: classes } unless validate_class_names?
91
98
 
92
99
  obj = {}
93
100
  classes = classes.split
@@ -170,19 +177,19 @@ module Primer
170
177
 
171
178
  def validate(key, val, breakpoint)
172
179
  unless supported_key?(key)
173
- raise ArgumentError, "#{key} is not a valid Primer utility key" unless ENV["RAILS_ENV"] == "production"
180
+ raise ArgumentError, "#{key} is not a valid Primer utility key" if validate_class_names?
174
181
 
175
182
  return ""
176
183
  end
177
184
 
178
185
  unless breakpoint.empty? || responsive?(key, val)
179
- raise ArgumentError, "#{key} does not support responsive values" unless ENV["RAILS_ENV"] == "production"
186
+ raise ArgumentError, "#{key} does not support responsive values" if validate_class_names?
180
187
 
181
188
  return ""
182
189
  end
183
190
 
184
191
  unless supported_value?(key, val)
185
- raise ArgumentError, "#{val} is not a valid value for :#{key}. Use one of #{mappings(key)}" unless ENV["RAILS_ENV"] == "production"
192
+ raise ArgumentError, "#{val} is not a valid value for :#{key}. Use one of #{mappings(key)}" if validate_class_names?
186
193
 
187
194
  return "#{key.to_s.dasherize}-#{val.to_s.dasherize}"
188
195
  end