primer_view_components 0.0.59 → 0.0.60

Sign up to get free protection for your applications and to get access to all the features.
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