primer_view_components 0.0.6 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +71 -30
  3. data/README.md +1 -158
  4. data/app/components/primer/avatar_component.rb +11 -0
  5. data/app/components/primer/base_component.rb +7 -12
  6. data/app/components/primer/blankslate_component.html.erb +8 -2
  7. data/app/components/primer/blankslate_component.rb +70 -118
  8. data/app/components/primer/border_box_component.rb +16 -4
  9. data/app/components/primer/box_component.rb +2 -0
  10. data/app/components/primer/breadcrumb_component.rb +13 -20
  11. data/app/components/primer/button_component.rb +21 -4
  12. data/app/components/primer/component.rb +1 -0
  13. data/app/components/primer/counter_component.rb +5 -0
  14. data/app/components/primer/details_component.rb +4 -4
  15. data/app/components/primer/flash_component.html.erb +14 -0
  16. data/app/components/primer/flash_component.rb +73 -0
  17. data/app/components/primer/label_component.rb +17 -0
  18. data/app/components/primer/layout_component.rb +18 -1
  19. data/app/components/primer/link_component.rb +10 -0
  20. data/app/components/primer/octicon_component.rb +45 -0
  21. data/app/components/primer/popover_component.rb +38 -0
  22. data/app/components/primer/progress_bar_component.rb +29 -19
  23. data/app/components/primer/slot.rb +1 -0
  24. data/app/components/primer/spinner_component.html.erb +6 -0
  25. data/app/components/primer/spinner_component.rb +38 -0
  26. data/app/components/primer/state_component.rb +19 -7
  27. data/app/components/primer/subhead_component.rb +45 -20
  28. data/app/components/primer/text_component.rb +6 -0
  29. data/app/components/primer/timeline_item_component.rb +24 -0
  30. data/app/components/primer/view_components.rb +3 -0
  31. data/lib/primer/classify.rb +4 -0
  32. data/lib/primer/view_components/version.rb +1 -1
  33. metadata +27 -8
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
+ # BorderBox is a Box component with a border.
4
5
  class BorderBoxComponent < Primer::Component
5
6
  include ViewComponent::Slotable
6
7
 
@@ -9,6 +10,17 @@ module Primer
9
10
  with_slot :footer, class_name: "Footer"
10
11
  with_slot :row, collection: true, class_name: "Row"
11
12
 
13
+ # @example 350|Header, body, rows, and footer
14
+ # <%= render(Primer::BorderBoxComponent.new) do |component|
15
+ # component.slot(:header) { "Header" }
16
+ # component.slot(:body) { "Body" }
17
+ # component.slot(:row) { "Row one" }
18
+ # component.slot(:row) { "Row two" }
19
+ # component.slot(:row) { "Row three" }
20
+ # component.slot(:footer) { "Footer" }
21
+ # end %>
22
+ #
23
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
12
24
  def initialize(**kwargs)
13
25
  @kwargs = kwargs
14
26
  @kwargs[:tag] = :div
@@ -23,8 +35,8 @@ module Primer
23
35
  end
24
36
 
25
37
  class Header < Primer::Slot
26
-
27
38
  attr_reader :kwargs
39
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
28
40
  def initialize(**kwargs)
29
41
  @kwargs = kwargs
30
42
  @kwargs[:tag] = :div
@@ -36,8 +48,8 @@ module Primer
36
48
  end
37
49
 
38
50
  class Body < Primer::Slot
39
-
40
51
  attr_reader :kwargs
52
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
41
53
  def initialize(**kwargs)
42
54
  @kwargs = kwargs
43
55
  @kwargs[:tag] = :div
@@ -49,8 +61,8 @@ module Primer
49
61
  end
50
62
 
51
63
  class Footer < Primer::Slot
52
-
53
64
  attr_reader :kwargs
65
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
54
66
  def initialize(**kwargs)
55
67
  @kwargs = kwargs
56
68
  @kwargs[:tag] = :div
@@ -62,8 +74,8 @@ module Primer
62
74
  end
63
75
 
64
76
  class Row < Primer::Slot
65
-
66
77
  attr_reader :kwargs
78
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
67
79
  def initialize(**kwargs)
68
80
  @kwargs = kwargs
69
81
  @kwargs[:tag] = :li
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
+ # A basic wrapper component for most layout related needs.
4
5
  class BoxComponent < Primer::Component
6
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
5
7
  def initialize(**kwargs)
6
8
  @kwargs = kwargs
7
9
  @kwargs[:tag] = :div
@@ -1,31 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- ##
4
- # Breadcrumbs are used to display page hierarchy within a section of the site. All of the items in the breadcrumb "trail" are links except for the final item which is a plain string indicating the current page.
5
- #
6
- # ## Example
7
- #
8
- # The `Primer::BreadcrumbComponent` uses the [Slots API](https://github.com/github/view_component#slots-experimental) and at least one slot is required for the component to render. Each slot can accept the following parameters:
9
- #
10
- # 1. `href` (string). The URL to link to.
11
- # 2. `selected` (boolean, default=false). Flag indicating whether or not the item is selected and not rendered as a link.
12
- #
13
- # Note that if if both `href` and `selected: true` are passed in, `href` will be ignored and the item will not be rendered as a link.
14
- #
15
- # ```ruby
16
- # <%= render(Primer::BreadcrumbComponent.new) do |component| %>
17
- # <% component.slot(:item, href: "/") do %>Home<% end %>
18
- # <% component.slot(:item, href: "/about") do %>About<% end %>
19
- # <% component.slot(:item, selected: true) do %>Team<% end %>
20
- # <% end %>
21
- # ```
22
- ##
23
3
  module Primer
4
+ # Use breadcrumbs to display page hierarchy within a section of the site. All of the items in the breadcrumb "trail" are links except for the final item, which is a plain string indicating the current page.
24
5
  class BreadcrumbComponent < Primer::Component
25
6
  include ViewComponent::Slotable
26
7
 
27
8
  with_slot :item, collection: true, class_name: "BreadcrumbItem"
28
9
 
10
+ # @example 40|Basic
11
+ # <%= render(Primer::BreadcrumbComponent.new) do |component| %>
12
+ # <% component.slot(:item, href: "/") do %>Home<% end %>
13
+ # <% component.slot(:item, href: "/about") do %>About<% end %>
14
+ # <% component.slot(:item, selected: true) do %>Team<% end %>
15
+ # <% end %>
16
+ #
17
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
29
18
  def initialize(**kwargs)
30
19
  @kwargs = kwargs
31
20
  @kwargs[:tag] = :nav
@@ -36,9 +25,13 @@ module Primer
36
25
  items.any?
37
26
  end
38
27
 
28
+ # _Note: if both `href` and `selected: true` are passed in, `href` will be ignored and the item will not be rendered as a link._
39
29
  class BreadcrumbItem < Primer::Slot
40
30
  attr_reader :href, :kwargs
41
31
 
32
+ # @param href [String] The URL to link to.
33
+ # @param selected [Boolean] Whether or not the item is selected and not rendered as a link.
34
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
42
35
  def initialize(href: nil, selected: false, **kwargs)
43
36
  @href, @kwargs = href, kwargs
44
37
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
+ # Use buttons for actions (e.g. in forms). Use links for destinations, or moving from one page to another.
4
5
  class ButtonComponent < Primer::Component
5
6
  DEFAULT_BUTTON_TYPE = :default
6
7
  BUTTON_TYPE_MAPPINGS = {
@@ -25,6 +26,22 @@ module Primer
25
26
  DEFAULT_TYPE = :button
26
27
  TYPE_OPTIONS = [DEFAULT_TYPE, :reset, :submit].freeze
27
28
 
29
+ # @example 50|Button types
30
+ # <%= render(Primer::ButtonComponent.new) { "Default" } %>
31
+ # <%= render(Primer::ButtonComponent.new(button_type: :primary)) { "Primary" } %>
32
+ # <%= render(Primer::ButtonComponent.new(button_type: :danger)) { "Danger" } %>
33
+ # <%= render(Primer::ButtonComponent.new(button_type: :outline)) { "Outline" } %>
34
+ #
35
+ # @example 50|Variants
36
+ # <%= render(Primer::ButtonComponent.new(variant: :small)) { "Small" } %>
37
+ # <%= render(Primer::ButtonComponent.new(variant: :medium)) { "Medium" } %>
38
+ # <%= render(Primer::ButtonComponent.new(variant: :large)) { "Large" } %>
39
+ #
40
+ # @param button_type [Symbol] <%= one_of(Primer::ButtonComponent::BUTTON_TYPE_OPTIONS) %>
41
+ # @param variant [Symbol] <%= one_of(Primer::ButtonComponent::VARIANT_OPTIONS) %>
42
+ # @param tag [Symbol] <%= one_of(Primer::ButtonComponent::TAG_OPTIONS) %>
43
+ # @param type [Symbol] <%= one_of(Primer::ButtonComponent::TYPE_OPTIONS) %>
44
+ # @param group_item [Boolean] Whether button is part of a ButtonGroup.
28
45
  def initialize(
29
46
  button_type: DEFAULT_BUTTON_TYPE,
30
47
  variant: DEFAULT_VARIANT,
@@ -34,19 +51,19 @@ module Primer
34
51
  **kwargs
35
52
  )
36
53
  @kwargs = kwargs
37
- @kwargs[:tag] = fetch_or_fallback(TAG_OPTIONS, tag.to_sym, DEFAULT_TAG)
54
+ @kwargs[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
38
55
 
39
56
  if @kwargs[:tag] == :a
40
57
  @kwargs[:role] = :button
41
58
  else
42
- @kwargs[:type] = type.to_sym
59
+ @kwargs[:type] = type
43
60
  end
44
61
 
45
62
  @kwargs[:classes] = class_names(
46
63
  "btn",
47
64
  kwargs[:classes],
48
- BUTTON_TYPE_MAPPINGS[fetch_or_fallback(BUTTON_TYPE_OPTIONS, button_type.to_sym, DEFAULT_BUTTON_TYPE)],
49
- VARIANT_MAPPINGS[fetch_or_fallback(VARIANT_OPTIONS, variant.to_sym, DEFAULT_VARIANT)],
65
+ BUTTON_TYPE_MAPPINGS[fetch_or_fallback(BUTTON_TYPE_OPTIONS, button_type, DEFAULT_BUTTON_TYPE)],
66
+ VARIANT_MAPPINGS[fetch_or_fallback(VARIANT_OPTIONS, variant, DEFAULT_VARIANT)],
50
67
  "BtnGroup-item" => group_item
51
68
  )
52
69
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
+ # @private
4
5
  class Component < ViewComponent::Base
5
6
  include ClassNameHelper
6
7
  include FetchOrFallbackHelper
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
+ # Use Primer::CounterComponent to add a count to navigational elements and buttons.
4
5
  class CounterComponent < Primer::Component
5
6
  DEFAULT_SCHEME = :default
6
7
  SCHEME_MAPPINGS = {
@@ -9,6 +10,10 @@ module Primer
9
10
  :light_gray => "Counter Counter--gray-light",
10
11
  }.freeze
11
12
 
13
+ #
14
+ # @example 34|Default
15
+ # <%= render(Primer::CounterComponent.new(count: 25)) %>
16
+ #
12
17
  # @param count [Integer, Float::INFINITY, nil] The number to be displayed (e.x. # of issues, pull requests)
13
18
  # @param scheme [Symbol] Color scheme. One of `SCHEME_MAPPINGS.keys`.
14
19
  # @param limit [Integer] Maximum value to display. (e.x. if count == 6,000 and limit == 5000, counter will display "5,000+")
@@ -9,9 +9,9 @@ module Primer
9
9
  class DetailsComponent < Primer::Component
10
10
  include ViewComponent::Slotable
11
11
 
12
- OVERLAY_DEFAULT = :none
12
+ NO_OVERLAY = :none
13
13
  OVERLAY_MAPPINGS = {
14
- OVERLAY_DEFAULT => "",
14
+ NO_OVERLAY => "",
15
15
  :default => "details-overlay",
16
16
  :dark => "details-overlay details-overlay-dark",
17
17
  }.freeze
@@ -19,12 +19,12 @@ module Primer
19
19
  with_slot :body, class_name: "Body"
20
20
  with_slot :summary, class_name: "Summary"
21
21
 
22
- def initialize(overlay: OVERLAY_DEFAULT, reset: false, **kwargs)
22
+ def initialize(overlay: NO_OVERLAY, reset: false, **kwargs)
23
23
  @kwargs = kwargs
24
24
  @kwargs[:tag] = :details
25
25
  @kwargs[:classes] = class_names(
26
26
  kwargs[:classes],
27
- OVERLAY_MAPPINGS[fetch_or_fallback(OVERLAY_MAPPINGS.keys, overlay.to_sym, OVERLAY_DEFAULT)],
27
+ OVERLAY_MAPPINGS[fetch_or_fallback(OVERLAY_MAPPINGS.keys, overlay, NO_OVERLAY)],
28
28
  "details-reset" => reset
29
29
  )
30
30
  end
@@ -0,0 +1,14 @@
1
+ <%= render Primer::BaseComponent.new(**@kwargs) do %>
2
+ <%= render(Primer::OcticonComponent.new(icon: @icon)) if @icon %>
3
+ <%= content %>
4
+ <% if @dismissible %>
5
+ <button class="flash-close js-flash-close" type="button" aria-label="Close">
6
+ <%= render(Primer::OcticonComponent.new(icon: "x")) %>
7
+ </button>
8
+ <% end %>
9
+ <% if actions.present? %>
10
+ <%= render Primer::BaseComponent.new(**actions.kwargs) do %>
11
+ <%= actions.content %>
12
+ <% end %>
13
+ <% end %>
14
+ <% end %>
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use the Flash component to inform users of successful or pending actions.
5
+ class FlashComponent < Primer::Component
6
+ include ViewComponent::Slotable
7
+
8
+ with_slot :actions, class_name: "Actions"
9
+
10
+ DEFAULT_VARIANT = :default
11
+ VARIANT_MAPPINGS = {
12
+ DEFAULT_VARIANT => "",
13
+ :warning => "flash-warn",
14
+ :danger => "flash-error",
15
+ :success => "flash-success"
16
+ }.freeze
17
+ # @example 280|Variants
18
+ # <%= render(Primer::FlashComponent.new) { "This is a flash message!" } %>
19
+ # <%= render(Primer::FlashComponent.new(variant: :warning)) { "This is a warning flash message!" } %>
20
+ # <%= render(Primer::FlashComponent.new(variant: :danger)) { "This is a danger flash message!" } %>
21
+ # <%= render(Primer::FlashComponent.new(variant: :success)) { "This is a success flash message!" } %>
22
+ #
23
+ # @example 80|Full width
24
+ # <%= render(Primer::FlashComponent.new(full: true)) { "This is a full width flash message!" } %>
25
+ #
26
+ # @example 80|Dismissible
27
+ # <%= render(Primer::FlashComponent.new(dismissible: true)) { "This is a dismissible flash message!" } %>
28
+ #
29
+ # @example 80|Icon
30
+ # <%= render(Primer::FlashComponent.new(icon: "people")) { "This is a flash message with an icon!" } %>
31
+ #
32
+ # @example 80|With actions
33
+ # <%= render(Primer::FlashComponent.new) do |component| %>
34
+ # This is a flash message with actions!
35
+ # <% component.slot(:actions) do %>
36
+ # <%= render(Primer::ButtonComponent.new(variant: :small)) { "Take action" } %>
37
+ # <% end %>
38
+ # <% end %>
39
+ #
40
+ # @param full [Boolean] Whether the component should take up the full width of the screen.
41
+ # @param spacious [Boolean] Whether to add margin to the bottom of the component.
42
+ # @param dismissible [Boolean] Whether the component can be dismissed with an X button.
43
+ # @param icon [String] Name of Octicon icon to use.
44
+ # @param variant [Symbol] <%= one_of(Primer::FlashComponent::VARIANT_MAPPINGS.keys) %>
45
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
46
+ def initialize(full: false, spacious: false, dismissible: false, icon: nil, variant: DEFAULT_VARIANT, **kwargs)
47
+ @icon = icon
48
+ @dismissible = dismissible
49
+ @kwargs = kwargs
50
+ @kwargs[:tag] = :div
51
+ @kwargs[:classes] = class_names(
52
+ @kwargs[:classes],
53
+ "flash",
54
+ VARIANT_MAPPINGS[fetch_or_fallback(VARIANT_MAPPINGS.keys, variant, DEFAULT_VARIANT)],
55
+ "flash-full": full
56
+ )
57
+ @kwargs[:mb] ||= spacious ? 4 : nil
58
+ end
59
+
60
+ class Actions < ViewComponent::Slot
61
+ include ClassNameHelper
62
+
63
+ attr_reader :kwargs
64
+
65
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
66
+ def initialize(**kwargs)
67
+ @kwargs = kwargs
68
+ @kwargs[:tag] = :div
69
+ @kwargs[:classes] = class_names(@kwargs[:classes], "flash-action")
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
+ # Use labels to add contextual metadata to a design.
4
5
  class LabelComponent < Primer::Component
5
6
  SCHEME_MAPPINGS = {
6
7
  # gray
@@ -28,6 +29,22 @@ module Primer
28
29
  }.freeze
29
30
  VARIANT_OPTIONS = VARIANT_MAPPINGS.keys << nil
30
31
 
32
+ # @example 40|Schemes
33
+ # <%= render(Primer::LabelComponent.new(title: "Label: Label")) { "default" } %>
34
+ # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :gray)) { "gray" } %>
35
+ # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :dark_gray)) { "dark_gray" } %>
36
+ # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :yellow)) { "yellow" } %>
37
+ # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :green)) { "green" } %>
38
+ # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :purple)) { "purple" } %>
39
+ #
40
+ # @example 40|Variants
41
+ # <%= render(Primer::LabelComponent.new(title: "Label: Label")) { "Default" } %>
42
+ # <%= render(Primer::LabelComponent.new(title: "Label: Label", variant: :large)) { "Large" } %>
43
+ #
44
+ # @param title [String] `title` attribute for the component element.
45
+ # @param scheme [Symbol] <%= one_of(Primer::LabelComponent::SCHEME_OPTIONS) %>
46
+ # @param variant [Symbol] <%= one_of(Primer::LabelComponent::VARIANT_OPTIONS) %>
47
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
31
48
  def initialize(title:, scheme: nil, variant: nil, **kwargs)
32
49
  @kwargs = kwargs
33
50
  @kwargs[:bg] = :blue if scheme.nil?
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
+ # Use Layout to build a main/sidebar layout.
4
5
  class LayoutComponent < Primer::Component
5
6
  with_content_areas :main, :sidebar
6
7
 
@@ -11,9 +12,25 @@ module Primer
11
12
  DEFAULT_SIDEBAR_COL = 3
12
13
  ALLOWED_SIDEBAR_COLS = (1..(MAX_COL - 1)).to_a.freeze
13
14
 
15
+ # @example 40|Default
16
+ # <%= render(Primer::LayoutComponent.new) do |component| %>
17
+ # <% component.with(:sidebar) { "Sidebar" } %>
18
+ # <% component.with(:main) { "Main" } %>
19
+ # <% end %>
20
+ #
21
+ # @example 40|Left sidebar
22
+ # <%= render(Primer::LayoutComponent.new(side: :left)) do |component| %>
23
+ # <% component.with(:sidebar) { "Sidebar" } %>
24
+ # <% component.with(:main) { "Main" } %>
25
+ # <% end %>
26
+ #
27
+ # @param responsive [Boolean] Whether to collapse layout to a single column at smaller widths.
28
+ # @param side [Symbol] Which side to display the sidebar on. <%= one_of(Primer::LayoutComponent::ALLOWED_SIDES) %>
29
+ # @param sidebar_col [Integer] Sidebar column width.
30
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
14
31
  def initialize(responsive: false, side: DEFAULT_SIDE, sidebar_col: DEFAULT_SIDEBAR_COL, **kwargs)
15
32
  @kwargs = kwargs
16
- @side = fetch_or_fallback(ALLOWED_SIDES, side.to_sym, DEFAULT_SIDE)
33
+ @side = fetch_or_fallback(ALLOWED_SIDES, side, DEFAULT_SIDE)
17
34
  @responsive = responsive
18
35
  @kwargs[:classes] = class_names(
19
36
  "gutter-condensed gutter-lg",
@@ -1,7 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
+ # Use links for moving from one page to another. The Link component styles anchor tags with default blue styling and hover text-decoration.
4
5
  class LinkComponent < Primer::Component
6
+ # @example 40|Default
7
+ # <%= render(Primer::LinkComponent.new(href: "http://www.google.com")) { "Link" } %>
8
+ #
9
+ # @example 40|Muted
10
+ # <%= render(Primer::LinkComponent.new(href: "http://www.google.com", muted: true)) { "Link" } %>
11
+ #
12
+ # @param href [String] URL to be used for the Link
13
+ # @param muted [Boolean] Uses light gray for Link color, and blue on hover
14
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
5
15
  def initialize(href:, muted: false, **kwargs)
6
16
  @kwargs = kwargs
7
17
  @kwargs[:tag] = :a
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Renders an [Octicon](https://primer.style/octicons/) with <%= link_to_style_arguments_docs %>.
5
+ class OcticonComponent < Primer::Component
6
+ include Primer::ClassNameHelper
7
+ include OcticonsHelper
8
+
9
+ SIZE_DEFAULT = :small
10
+ SIZE_MAPPINGS = {
11
+ SIZE_DEFAULT => 16,
12
+ :medium => 32,
13
+ :large => 64,
14
+ }.freeze
15
+ SIZE_OPTIONS = SIZE_MAPPINGS.keys
16
+
17
+ # @example 25|Default
18
+ # <%= render(Primer::OcticonComponent.new(icon: "check")) %>
19
+ #
20
+ # @example 40|Medium
21
+ # <%= render(Primer::OcticonComponent.new(icon: "people", size: :medium)) %>
22
+ #
23
+ # @example 80|Large
24
+ # <%= render(Primer::OcticonComponent.new(icon: "x", size: :large)) %>
25
+ #
26
+ # @param icon [String] Name of [Octicon](https://primer.style/octicons/) to use.
27
+ # @param size [Symbol] <%= one_of(Primer::OcticonComponent::SIZE_OPTIONS) %>
28
+ # @param kwargs [Hash] <%= link_to_style_arguments_docs %>
29
+ def initialize(icon:, size: SIZE_DEFAULT, **kwargs)
30
+ @icon, @kwargs = icon, kwargs
31
+
32
+ @kwargs[:class] = Primer::Classify.call(**@kwargs)[:class]
33
+ @kwargs[:height] ||= SIZE_MAPPINGS[size]
34
+
35
+ # Filter out classify options to prevent them from becoming invalid html attributes.
36
+ # Note height and width are both classify options and valid html attributes.
37
+ octicon_helper_options = @kwargs.slice(:height, :width)
38
+ @kwargs = @kwargs.except(*Primer::Classify::VALID_KEYS, :classes).merge(octicon_helper_options)
39
+ end
40
+
41
+ def call
42
+ octicon(@icon, **@kwargs)
43
+ end
44
+ end
45
+ end