primer_view_components 0.0.35 → 0.0.40

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +178 -22
  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/{auto_complete_component.rb → auto_complete.rb} +12 -12
  6. data/app/components/primer/{auto_complete_component.d.ts → auto_complete/auto_complete.d.ts} +0 -0
  7. data/app/components/primer/{auto_complete_component.html.erb → auto_complete/auto_complete.html.erb} +0 -0
  8. data/app/components/primer/{auto_complete_component.js → auto_complete/auto_complete.js} +0 -0
  9. data/app/components/primer/{auto_complete_component.ts → auto_complete/auto_complete.ts} +0 -0
  10. data/app/components/primer/auto_complete/auto_component.d.ts +1 -0
  11. data/app/components/primer/auto_complete/auto_component.js +1 -0
  12. data/app/components/primer/auto_complete/item.rb +42 -0
  13. data/app/components/primer/avatar_component.rb +22 -3
  14. data/app/components/primer/avatar_stack_component.rb +6 -3
  15. data/app/components/primer/base_button.rb +47 -0
  16. data/app/components/primer/base_component.rb +9 -8
  17. data/app/components/primer/blankslate_component.rb +5 -2
  18. data/app/components/primer/border_box_component.rb +1 -1
  19. data/app/components/primer/box_component.rb +1 -1
  20. data/app/components/primer/breadcrumb_component.rb +1 -1
  21. data/app/components/primer/button_component.html.erb +9 -0
  22. data/app/components/primer/button_component.rb +58 -21
  23. data/app/components/primer/{button_group_component.html.erb → button_group.html.erb} +0 -0
  24. data/app/components/primer/button_group.rb +61 -0
  25. data/app/components/primer/button_marketing_component.rb +4 -9
  26. data/app/components/primer/clipboard_copy.html.erb +8 -0
  27. data/app/components/primer/clipboard_copy.rb +26 -0
  28. data/app/components/primer/clipboard_copy_component.d.ts +1 -0
  29. data/app/components/primer/clipboard_copy_component.js +23 -0
  30. data/app/components/primer/clipboard_copy_component.ts +26 -0
  31. data/app/components/primer/close_button.rb +39 -0
  32. data/app/components/primer/component.rb +21 -2
  33. data/app/components/primer/counter_component.rb +6 -1
  34. data/app/components/primer/details_component.rb +1 -1
  35. data/app/components/primer/dropdown/menu_component.rb +1 -1
  36. data/app/components/primer/dropdown_component.rb +1 -1
  37. data/app/components/primer/flash_component.rb +1 -1
  38. data/app/components/primer/flex_component.rb +28 -1
  39. data/app/components/primer/flex_item_component.rb +20 -1
  40. data/app/components/primer/heading_component.rb +25 -4
  41. data/app/components/primer/hidden_text_expander.rb +41 -0
  42. data/app/components/primer/icon_button.rb +65 -0
  43. data/app/components/primer/image_crop.d.ts +1 -0
  44. data/app/components/primer/image_crop.html.erb +12 -0
  45. data/app/components/primer/image_crop.js +1 -0
  46. data/app/components/primer/image_crop.rb +36 -0
  47. data/app/components/primer/image_crop.ts +1 -0
  48. data/app/components/primer/label_component.rb +1 -1
  49. data/app/components/primer/layout_component.rb +1 -1
  50. data/app/components/primer/link_component.rb +1 -1
  51. data/app/components/primer/{markdown_component.rb → markdown.rb} +6 -5
  52. data/app/components/primer/menu_component.rb +1 -1
  53. data/app/components/primer/octicon_component.html.erb +7 -0
  54. data/app/components/primer/octicon_component.rb +46 -14
  55. data/app/components/primer/octicon_symbols_component.html.erb +3 -0
  56. data/app/components/primer/octicon_symbols_component.rb +61 -0
  57. data/app/components/primer/popover_component.rb +1 -1
  58. data/app/components/primer/primer.d.ts +3 -1
  59. data/app/components/primer/primer.js +3 -1
  60. data/app/components/primer/primer.ts +3 -1
  61. data/app/components/primer/progress_bar_component.rb +1 -1
  62. data/app/components/primer/spinner_component.rb +3 -3
  63. data/app/components/primer/state_component.rb +2 -2
  64. data/app/components/primer/subhead_component.rb +34 -4
  65. data/app/components/primer/tab_container_component.rb +1 -1
  66. data/app/components/primer/tab_nav_component.rb +1 -1
  67. data/app/components/primer/text_component.rb +6 -3
  68. data/app/components/primer/time_ago_component.rb +1 -1
  69. data/app/components/primer/timeline_item_component.rb +1 -1
  70. data/app/components/primer/tooltip_component.rb +1 -1
  71. data/app/components/primer/{truncate_component.rb → truncate.rb} +8 -6
  72. data/app/components/primer/underline_nav_component.rb +1 -1
  73. data/app/lib/primer/classify.rb +9 -34
  74. data/app/lib/primer/classify/cache.rb +20 -15
  75. data/app/lib/primer/classify/flex.rb +111 -0
  76. data/app/lib/primer/classify/functional_border_colors.rb +1 -2
  77. data/app/lib/primer/fetch_or_fallback_helper.rb +2 -2
  78. data/app/lib/primer/octicon/cache.rb +42 -0
  79. data/lib/primer/view_components.rb +1 -1
  80. data/lib/primer/view_components/version.rb +1 -1
  81. data/lib/tasks/coverage.rake +14 -0
  82. data/lib/tasks/docs.rake +312 -0
  83. data/lib/tasks/statuses.rake +12 -0
  84. data/lib/yard/docs_helper.rb +57 -0
  85. data/static/statuses.json +52 -1
  86. metadata +53 -15
  87. data/app/assets/javascripts/primer_view_components.js.map.orig +0 -5
  88. data/app/assets/javascripts/primer_view_components.js.orig +0 -6
  89. data/app/components/primer/auto_complete_item_component.rb +0 -40
  90. data/app/components/primer/button_group_component.rb +0 -35
@@ -0,0 +1,8 @@
1
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
+ <% if content.present? %>
3
+ <%= content %>
4
+ <% else %>
5
+ <%= render Primer::OcticonComponent.new("clippy") %>
6
+ <%= render Primer::OcticonComponent.new("check", color: :icon_success, style: "display: none;") %>
7
+ <% end %>
8
+ <% end %>
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use `ClipboardCopy` to copy element text content or input values to the clipboard.
5
+ class ClipboardCopy < Primer::Component
6
+ status :alpha
7
+
8
+ # @example Default
9
+ # <%= render(Primer::ClipboardCopy.new(value: "Text to copy", label: "Copy text to the system clipboard")) %>
10
+ #
11
+ # @example With text instead of icons
12
+ # <%= render(Primer::ClipboardCopy.new(value: "Text to copy", label: "Copy text to the system clipboard")) do %>
13
+ # Click to copy!
14
+ # <% end %>
15
+ #
16
+ # @param label [String] String that will be read to screenreaders when the component is focused
17
+ # @param value [String] Text to copy into the users clipboard when they click the component
18
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
19
+ def initialize(label:, value:, **system_arguments)
20
+ @system_arguments = system_arguments
21
+ @system_arguments[:tag] = "clipboard-copy"
22
+ @system_arguments[:value] = value
23
+ @system_arguments[:"aria-label"] = label
24
+ end
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ import '@github/clipboard-copy-element';
@@ -0,0 +1,23 @@
1
+ import '@github/clipboard-copy-element';
2
+ function toggleSVG(svg) {
3
+ if (svg.style.display === '' || svg.style.display === 'block') {
4
+ svg.style.display = 'none';
5
+ }
6
+ else {
7
+ svg.style.display = 'block';
8
+ }
9
+ }
10
+ // Toggle a copy button.
11
+ function toggleCopyButton(button) {
12
+ const [clippyIcon, checkIcon] = button.querySelectorAll('.octicon');
13
+ if (!clippyIcon || !checkIcon)
14
+ return;
15
+ toggleSVG(clippyIcon);
16
+ toggleSVG(checkIcon);
17
+ }
18
+ document.addEventListener('clipboard-copy', function ({ target }) {
19
+ if (!(target instanceof HTMLElement))
20
+ return;
21
+ toggleCopyButton(target);
22
+ setTimeout(toggleCopyButton, 2000, target);
23
+ });
@@ -0,0 +1,26 @@
1
+ import '@github/clipboard-copy-element'
2
+
3
+ function toggleSVG(svg: SVGElement) {
4
+ if (svg.style.display === '' || svg.style.display === 'block') {
5
+ svg.style.display = 'none'
6
+ } else {
7
+ svg.style.display = 'block'
8
+ }
9
+ }
10
+
11
+ // Toggle a copy button.
12
+ function toggleCopyButton(button: HTMLElement) {
13
+ const [clippyIcon, checkIcon] = button.querySelectorAll<SVGElement>('.octicon')
14
+
15
+ if (!clippyIcon || !checkIcon) return
16
+
17
+ toggleSVG(clippyIcon)
18
+ toggleSVG(checkIcon)
19
+ }
20
+
21
+ document.addEventListener('clipboard-copy', function ({target}) {
22
+ if (!(target instanceof HTMLElement)) return
23
+ toggleCopyButton(target)
24
+
25
+ setTimeout(toggleCopyButton, 2000, target)
26
+ })
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use `CloseButton` to render an `×` without default button styles.
5
+ #
6
+ # @accessibility
7
+ # `CloseButton` has a default `aria-label` of "Close" to provides assistive technologies with an accessible label.
8
+ # You may choose to override this label with something more descriptive via [system_arguments][0].
9
+ # [0]: https://primer.style/view-components/system-arguments#html-attributes
10
+ class CloseButton < Primer::Component
11
+ status :beta
12
+
13
+ DEFAULT_TYPE = :button
14
+ TYPE_OPTIONS = [DEFAULT_TYPE, :submit].freeze
15
+
16
+ # @example Default
17
+ # <%= render(Primer::CloseButton.new) %>
18
+ #
19
+ # @param type [Symbol] <%= one_of(Primer::CloseButton::TYPE_OPTIONS) %>
20
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
21
+ def initialize(type: DEFAULT_TYPE, **system_arguments)
22
+ @system_arguments = system_arguments
23
+ @system_arguments[:tag] = :button
24
+ @system_arguments[:block] = false
25
+ @system_arguments[:type] = fetch_or_fallback(TYPE_OPTIONS, type, DEFAULT_TYPE)
26
+ @system_arguments[:classes] = class_names(
27
+ "close-button",
28
+ system_arguments[:classes]
29
+ )
30
+ @system_arguments[:"aria-label"] ||= "Close"
31
+ end
32
+
33
+ def call
34
+ render(Primer::BaseButton.new(**@system_arguments)) do
35
+ render(Primer::OcticonComponent.new("x"))
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "octicons_helper/helper"
4
3
  require "view_component/version"
5
4
 
6
5
  module Primer
@@ -9,10 +8,30 @@ module Primer
9
8
  include ViewComponent::SlotableV2 unless ViewComponent::VERSION::STRING.to_f >= 2.28
10
9
  include ClassNameHelper
11
10
  include FetchOrFallbackHelper
12
- include OcticonsHelper
13
11
  include TestSelectorHelper
14
12
  include JoinStyleArgumentsHelper
15
13
  include ViewHelper
16
14
  include Status::Dsl
15
+
16
+ private
17
+
18
+ def deprecated_component_warning(new_class: nil, version: nil)
19
+ return if Rails.env.production? || silence_deprecations?
20
+
21
+ message = "#{self.class.name} is deprecated"
22
+ message += " and will be removed in v#{version}." if version
23
+ message += " Use #{new_class.name} instead." if new_class
24
+
25
+ ActiveSupport::Deprecation.warn(message)
26
+ end
27
+
28
+ def validate_aria_label
29
+ aria_label = @system_arguments[:"aria-label"] || @system_arguments.dig(:aria, :label)
30
+ raise ArgumentError, "`aria-label` is required." if aria_label.nil? && !Rails.env.production?
31
+ end
32
+
33
+ def silence_deprecations?
34
+ Rails.application.config.primer_view_components.silence_deprecations
35
+ end
17
36
  end
18
37
  end
@@ -1,7 +1,12 @@
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
+ # Use `Counter` to add a count to navigational elements and buttons.
5
+ #
6
+ # @accessibility
7
+ # Always use `Counter` with adjacent text that provides supplementary information regarding what the count is for. For instance, `Counter`
8
+ # should be accompanied with text such as `issues` or `pull requests`.
9
+ #
5
10
  class CounterComponent < Primer::Component
6
11
  status :beta
7
12
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
- # Use DetailsComponent to reveal content after clicking a button.
4
+ # Use `DetailsComponent` to reveal content after clicking a button.
5
5
  class DetailsComponent < Primer::Component
6
6
  status :beta
7
7
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Primer
4
4
  module Dropdown
5
- # This component is part of `Primer::DropdownComponent` and should not be
5
+ # This component is part of `Dropdown` and should not be
6
6
  # used as a standalone component.
7
7
  class MenuComponent < Primer::Component
8
8
  SCHEME_DEFAULT = :default
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
- # Dropdowns are lightweight context menus for housing navigation and actions.
4
+ # `Dropdown` is a lightweight context menu for housing navigation and actions.
5
5
  # They're great for instances where you don't need the full power (and code) of the select menu.
6
6
  class DropdownComponent < Primer::Component
7
7
  # Required trigger for the dropdown. Only accepts a content.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
- # Use the Flash component to inform users of successful or pending actions.
4
+ # Use `Flash` to inform users of successful or pending actions.
5
5
  class FlashComponent < Primer::Component
6
6
  status :beta
7
7
 
@@ -1,11 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
- # Use FlexComponent to make an element lay out its content using the flexbox model.
4
+ # Use `Flex` to make an element lay out its content using the flexbox model.
5
5
  # Before using these utilities, you should be familiar with CSS3 Flexible Box
6
6
  # spec. If you are not, check out MDN's guide [Using CSS Flexible
7
7
  # Boxes](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox).
8
+ #
9
+ # @deprecated
10
+ # Use <%= link_to_component(Primer::BoxComponent) %> instead.
11
+ #
12
+ # **Before**:
13
+ #
14
+ # ```erb
15
+ # <%%= render Primer::FlexComponent.new(justify_content: :center) %>
16
+ # <%%= render Primer::FlexComponent.new(inline: true) %>
17
+ # <%%= render Primer::FlexComponent.new(flex_wrap: true) %>
18
+ # <%%= render Primer::FlexComponent.new(align_items: :start) %>
19
+ # <%%= render Primer::FlexComponent.new(direction: :column) %>
20
+ # ```
21
+ #
22
+ # **After**:
23
+ #
24
+ # ```erb
25
+ # <%%= render Primer::BoxComponent.new(display: :flex, justify_content: :center) %>
26
+ # <%%= render Primer::BoxComponent.new(display: :inline_flex) %>
27
+ # <%%= render Primer::BoxComponent.new(display: :flex, flex_wrap: :wrap) %>
28
+ # <%%= render Primer::BoxComponent.new(display: :flex, align_items: :start) %>
29
+ # <%%= render Primer::BoxComponent.new(display: :flex, direction: :column) %>
30
+ # ```
8
31
  class FlexComponent < Primer::Component
32
+ status :deprecated
33
+
9
34
  JUSTIFY_CONTENT_DEFAULT = nil
10
35
  JUSTIFY_CONTENT_MAPPINGS = {
11
36
  flex_start: "flex-justify-start",
@@ -77,6 +102,8 @@ module Primer
77
102
  direction: nil,
78
103
  **system_arguments
79
104
  )
105
+ deprecated_component_warning(new_class: Primer::BoxComponent, version: "0.0.40")
106
+
80
107
  @align_items = fetch_or_fallback(ALIGN_ITEMS_OPTIONS, align_items, ALIGN_ITEMS_DEFAULT)
81
108
  @justify_content = fetch_or_fallback(JUSTIFY_CONTENT_OPTIONS, justify_content, JUSTIFY_CONTENT_DEFAULT)
82
109
  @flex_wrap = fetch_or_fallback(FLEX_WRAP_OPTIONS, flex_wrap, FLEX_WRAP_DEFAULT)
@@ -1,9 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
- # Use FlexItemComponent to specify the ability of a flex item to alter its
4
+ # Use `FlexItem` to specify the ability of a flex item to alter its
5
5
  # dimensions to fill available space
6
+ #
7
+ # @deprecated
8
+ # Use <%= link_to_component(Primer::BoxComponent) %> instead.
9
+ #
10
+ # **Before**:
11
+ #
12
+ # ```erb
13
+ # <%%= render Primer::FlexItemComponent.new(flex_auto: :auto) %>
14
+ # ```
15
+ #
16
+ # **After**:
17
+ #
18
+ # ```erb
19
+ # <%%= render Primer::BoxComponent.new(flex: :auto) %>
20
+ # ```
6
21
  class FlexItemComponent < Primer::Component
22
+ status :deprecated
23
+
7
24
  FLEX_AUTO_DEFAULT = false
8
25
  FLEX_AUTO_ALLOWED_VALUES = [FLEX_AUTO_DEFAULT, true].freeze
9
26
 
@@ -21,6 +38,8 @@ module Primer
21
38
  # @param flex_auto [Boolean] Fills available space and auto-sizes based on the content. Defaults to <%= Primer::FlexItemComponent::FLEX_AUTO_DEFAULT %>
22
39
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
23
40
  def initialize(flex_auto: FLEX_AUTO_DEFAULT, **system_arguments)
41
+ deprecated_component_warning(new_class: Primer::BoxComponent, version: "0.0.40")
42
+
24
43
  @system_arguments = system_arguments
25
44
  @system_arguments[:classes] =
26
45
  class_names(
@@ -1,19 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
- # Use the Heading component to wrap a component that will create a heading element
4
+ # `Heading` should be used to communicate page organization and hierarchy.
5
+ #
6
+ # - Set tag to one of `:h1`, `:h2`, `:h3`, `:h4`, `:h5`, `:h6` based on what is appropriate for the page context.
7
+ # - Use `Heading` as the title of a section or sub section.
8
+ # - Do not use `Heading` for styling alone. For simply styling text, consider using <%= link_to_component(Primer::TextComponent) %> with relevant <%= link_to_typography_docs %>
9
+ # such as `font_size` and `font_weight`.
10
+ # - Do not jump heading levels. For instance, do not follow a `<h1>` with an `<h3>`. Heading levels should increase by one in ascending order.
11
+ #
12
+ # @accessibility
13
+ # While sighted users rely on visual cues such as font size changes to determine what the heading is, assistive technology users rely on programatic cues that can be read out.
14
+ # When text on a page is visually implied to be a heading, ensure that it is coded as a heading. Additionally, visually implied heading level and coded heading level should be
15
+ # consistent. [See WCAG success criteria: 1.3.1: Info and Relationships](https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html)
16
+ #
17
+ # Headings allow assistive technology users to quickly navigate around a page. Navigation to text that is not meant to be a heading can be a confusing experience.
18
+ # <%= link_to_heading_practices %>
5
19
  class HeadingComponent < Primer::Component
6
20
  status :beta
7
21
 
22
+ TAG_FALLBACK = :h2
23
+ TAG_OPTIONS = [:h1, TAG_FALLBACK, :h3, :h4, :h5, :h6].freeze
24
+
8
25
  # @example Default
9
- # <%= render(Primer::HeadingComponent.new) { "H1 Text" } %>
26
+ # <%= render(Primer::HeadingComponent.new(tag: :h1)) { "H1 Text" } %>
10
27
  # <%= render(Primer::HeadingComponent.new(tag: :h2)) { "H2 Text" } %>
11
28
  # <%= render(Primer::HeadingComponent.new(tag: :h3)) { "H3 Text" } %>
29
+ # <%= render(Primer::HeadingComponent.new(tag: :h4)) { "H4 Text" } %>
30
+ # <%= render(Primer::HeadingComponent.new(tag: :h5)) { "H5 Text" } %>
31
+ # <%= render(Primer::HeadingComponent.new(tag: :h6)) { "H6 Text" } %>
12
32
  #
33
+ # @param tag [String] <%= one_of(Primer::HeadingComponent::TAG_OPTIONS) %>
13
34
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
14
- def initialize(**system_arguments)
35
+ def initialize(tag:, **system_arguments)
15
36
  @system_arguments = system_arguments
16
- @system_arguments[:tag] ||= :h1
37
+ @system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, TAG_FALLBACK)
17
38
  end
18
39
 
19
40
  def call
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use `HiddenTextExpander` to indicate and toggle hidden text.
5
+ class HiddenTextExpander < Primer::Component
6
+ # @example Default
7
+ # <%= render(Primer::HiddenTextExpander.new) %>
8
+ #
9
+ # @example Inline
10
+ # <%= render(Primer::HiddenTextExpander.new(inline: true)) %>
11
+ #
12
+ # @example Styling the button
13
+ # <%= render(Primer::HiddenTextExpander.new(button_arguments: { p: 1, classes: "my-custom-class" })) %>
14
+ #
15
+ # @param inline [Boolean] Whether or not the expander is inline.
16
+ # @param button_arguments [Hash] <%= link_to_system_arguments_docs %> for the button element.
17
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
18
+ def initialize(inline: false, button_arguments: {}, **system_arguments)
19
+ @system_arguments = system_arguments
20
+ @system_arguments[:tag] ||= :span
21
+ @system_arguments[:classes] = class_names(
22
+ "hidden-text-expander",
23
+ @system_arguments[:classes],
24
+ "inline" => inline
25
+ )
26
+
27
+ @button_arguments = button_arguments
28
+ @button_arguments[:"aria-expanded"] = false
29
+ @button_arguments[:classes] = class_names(
30
+ "ellipsis-expander",
31
+ button_arguments[:classes]
32
+ )
33
+ end
34
+
35
+ def call
36
+ render(Primer::BaseComponent.new(**@system_arguments)) do
37
+ render(Primer::BaseButton.new(**@button_arguments)) { "&hellip;".html_safe }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use `IconButton` to render Icon-only buttons without the default button styles.
5
+ #
6
+ # @accessibility
7
+ # `IconButton` requires an `aria-label`, which will provide assistive technologies with an accessible label.
8
+ # The `aria-label` should describe the action to be invoked rather than the icon itself. For instance,
9
+ # if your `IconButton` renders a magnifying glass icon and invokves a search action, the `aria-label` should be
10
+ # `"Search"` instead of `"Magnifying glass"`.
11
+ # [Learn more about best functional image practices (WAI Images)](https://www.w3.org/WAI/tutorials/images/functional)
12
+ class IconButton < Primer::Component
13
+ status :beta
14
+
15
+ DEFAULT_SCHEME = :default
16
+ SCHEME_MAPPINGS = {
17
+ DEFAULT_SCHEME => "",
18
+ :danger => "btn-octicon-danger"
19
+ }.freeze
20
+ SCHEME_OPTIONS = SCHEME_MAPPINGS.keys
21
+ # @example Default
22
+ #
23
+ # <%= render(Primer::IconButton.new(icon: :search, "aria-label": "Search")) %>
24
+ #
25
+ # @example Schemes
26
+ #
27
+ # <%= render(Primer::IconButton.new(icon: :search, "aria-label": "Search")) %>
28
+ # <%= render(Primer::IconButton.new(icon: :trash, "aria-label": "Delete", scheme: :danger)) %>
29
+ #
30
+ # @example In a BorderBox
31
+ #
32
+ # <%= render(Primer::BorderBoxComponent.new) do |component| %>
33
+ # <% component.body do %>
34
+ # <%= render(Primer::TextComponent.new(pr: 2)) { "Body" } %>
35
+ # <%= render(Primer::IconButton.new(icon: :pencil, box: true, "aria-label": "Edit")) %>
36
+ # <% end %>
37
+ # <% end %>
38
+ #
39
+ # @param scheme [Symbol] <%= one_of(Primer::IconButton::SCHEME_OPTIONS) %>
40
+ # @param icon [String] Name of <%= link_to_octicons %> to use.
41
+ # @param tag [Symbol] <%= one_of(Primer::BaseButton::TAG_OPTIONS) %>
42
+ # @param type [Symbol] <%= one_of(Primer::BaseButton::TYPE_OPTIONS) %>
43
+ # @param box [Boolean] Whether the button is in a <%= link_to_component(Primer::BorderBoxComponent) %>. If `true`, the button will have the `Box-btn-octicon` class.
44
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
45
+ def initialize(scheme: DEFAULT_SCHEME, icon:, box: false, **system_arguments)
46
+ @icon = icon
47
+
48
+ @system_arguments = system_arguments
49
+ @system_arguments[:classes] = class_names(
50
+ "btn-octicon",
51
+ SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_OPTIONS, scheme, DEFAULT_SCHEME)],
52
+ system_arguments[:classes],
53
+ "Box-btn-octicon" => box
54
+ )
55
+
56
+ validate_aria_label
57
+ end
58
+
59
+ def call
60
+ render(Primer::BaseButton.new(**@system_arguments)) do
61
+ render(Primer::OcticonComponent.new(icon: @icon))
62
+ end
63
+ end
64
+ end
65
+ end