primer_view_components 0.0.25 → 0.0.30

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -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/assets/javascripts/primer_view_components.js.map.orig +5 -0
  6. data/app/assets/javascripts/primer_view_components.js.orig +6 -0
  7. data/app/components/primer/auto_complete_component.d.ts +1 -0
  8. data/app/components/primer/auto_complete_component.html.erb +5 -0
  9. data/app/components/primer/auto_complete_component.js +1 -0
  10. data/app/components/primer/auto_complete_component.rb +98 -0
  11. data/app/components/primer/auto_complete_component.ts +1 -0
  12. data/app/components/primer/auto_complete_item_component.rb +40 -0
  13. data/app/components/primer/avatar_component.rb +8 -9
  14. data/app/components/primer/base_component.rb +6 -14
  15. data/app/components/primer/blankslate_component.html.erb +1 -5
  16. data/app/components/primer/blankslate_component.rb +2 -0
  17. data/app/components/primer/border_box_component.rb +32 -6
  18. data/app/components/primer/box_component.rb +3 -5
  19. data/app/components/primer/breadcrumb_component.rb +1 -0
  20. data/app/components/primer/component.rb +1 -13
  21. data/app/components/primer/counter_component.rb +17 -9
  22. data/app/components/primer/details_component.rb +3 -3
  23. data/app/components/primer/dropdown_menu_component.rb +2 -4
  24. data/app/components/primer/flash_component.html.erb +2 -2
  25. data/app/components/primer/flash_component.rb +2 -4
  26. data/app/components/primer/flex_component.rb +16 -16
  27. data/app/components/primer/heading_component.rb +1 -1
  28. data/app/components/primer/label_component.rb +5 -11
  29. data/app/components/primer/layout_component.html.erb +3 -9
  30. data/app/components/primer/layout_component.rb +30 -5
  31. data/app/components/primer/link_component.rb +37 -9
  32. data/app/components/primer/octicon_component.rb +4 -7
  33. data/app/components/primer/popover_component.html.erb +3 -7
  34. data/app/components/primer/popover_component.rb +58 -62
  35. data/app/components/primer/primer.d.ts +3 -0
  36. data/app/components/primer/primer.js +2 -0
  37. data/app/components/primer/primer.ts +2 -0
  38. data/app/components/primer/progress_bar_component.rb +6 -5
  39. data/app/components/primer/spinner_component.rb +2 -4
  40. data/app/components/primer/state_component.rb +2 -4
  41. data/app/components/primer/subhead_component.rb +2 -4
  42. data/app/components/primer/tab_container_component.d.ts +1 -0
  43. data/app/components/primer/text_component.rb +3 -1
  44. data/app/components/primer/time_ago_component.d.ts +1 -0
  45. data/app/components/primer/time_ago_component.js +1 -0
  46. data/app/components/primer/time_ago_component.rb +47 -0
  47. data/app/components/primer/time_ago_component.ts +1 -0
  48. data/app/components/primer/timeline_item_component.rb +2 -1
  49. data/app/components/primer/underline_nav_component.html.erb +5 -5
  50. data/app/components/primer/underline_nav_component.rb +24 -5
  51. data/app/lib/primer/classify.rb +13 -16
  52. data/app/lib/primer/classify/cache.rb +8 -3
  53. data/app/lib/primer/classify/functional_background_colors.rb +61 -0
  54. data/app/lib/primer/classify/functional_border_colors.rb +51 -0
  55. data/app/lib/primer/classify/functional_colors.rb +68 -0
  56. data/app/lib/primer/classify/functional_text_colors.rb +62 -0
  57. data/app/lib/primer/fetch_or_fallback_helper.rb +17 -4
  58. data/app/lib/primer/status/dsl.rb +43 -0
  59. data/app/lib/primer/test_selector_helper.rb +20 -0
  60. data/app/lib/primer/view_helper.rb +10 -12
  61. data/lib/primer/view_components/engine.rb +4 -0
  62. data/lib/primer/view_components/version.rb +1 -1
  63. data/static/statuses.json +1 -1
  64. metadata +37 -4
  65. data/app/lib/primer/view_helper/dsl.rb +0 -34
@@ -3,11 +3,13 @@
3
3
  module Primer
4
4
  # A basic wrapper component for most layout related needs.
5
5
  class BoxComponent < Primer::Component
6
+ status :stable
7
+
6
8
  # @example Default
7
9
  # <%= render(Primer::BoxComponent.new) { "Your content here" } %>
8
10
  #
9
11
  # @example Color and padding
10
- # <%= render(Primer::BoxComponent.new(bg: :gray, p: 3)) { "Hello world" } %>
12
+ # <%= render(Primer::BoxComponent.new(bg: :tertiary, p: 3)) { "Hello world" } %>
11
13
  #
12
14
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
13
15
  def initialize(**system_arguments)
@@ -18,9 +20,5 @@ module Primer
18
20
  def call
19
21
  render(Primer::BaseComponent.new(**@system_arguments)) { content }
20
22
  end
21
-
22
- def self.status
23
- Primer::Component::STATUSES[:stable]
24
- end
25
23
  end
26
24
  end
@@ -4,6 +4,7 @@ module Primer
4
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.
5
5
  class BreadcrumbComponent < Primer::Component
6
6
  include ViewComponent::SlotableV2
7
+ status :beta
7
8
 
8
9
  # _Note: if both `href` and `selected: true` are passed in, `href` will be ignored and the item will not be rendered as a link._
9
10
  #
@@ -9,19 +9,7 @@ module Primer
9
9
  include FetchOrFallbackHelper
10
10
  include OcticonsHelper
11
11
  include JoinStyleArgumentsHelper
12
- include ViewHelper::Dsl
13
12
  include ViewHelper
14
-
15
- # sourced from https://primer.style/doctocat/usage/front-matter#status
16
- STATUSES = {
17
- alpha: :alpha,
18
- beta: :beta,
19
- stable: :stable,
20
- deprecated: :deprecated
21
- }.freeze
22
-
23
- def self.status
24
- STATUSES[:alpha]
25
- end
13
+ include Status::Dsl
26
14
  end
27
15
  end
@@ -3,19 +3,30 @@
3
3
  module Primer
4
4
  # Use Primer::CounterComponent to add a count to navigational elements and buttons.
5
5
  class CounterComponent < Primer::Component
6
+ status :beta
7
+
6
8
  DEFAULT_SCHEME = :default
7
9
  SCHEME_MAPPINGS = {
8
- DEFAULT_SCHEME => "Counter",
9
- :gray => "Counter Counter--gray",
10
- :light_gray => "Counter Counter--gray-light"
10
+ DEFAULT_SCHEME => "",
11
+ :primary => "Counter--primary",
12
+ :secondary => "Counter--secondary",
13
+ # deprecated
14
+ :gray => "Counter--primary",
15
+ :light_gray => "Counter--secondary"
11
16
  }.freeze
17
+ DEPRECATED_SCHEME_OPTIONS = [:gray, :light_gray].freeze
18
+ SCHEME_OPTIONS = (SCHEME_MAPPINGS.keys - DEPRECATED_SCHEME_OPTIONS).freeze
12
19
 
13
20
  #
14
21
  # @example Default
15
22
  # <%= render(Primer::CounterComponent.new(count: 25)) %>
16
23
  #
24
+ # @example Schemes
25
+ # <%= render(Primer::CounterComponent.new(count: 25, scheme: :primary)) %>
26
+ # <%= render(Primer::CounterComponent.new(count: 25, scheme: :secondary)) %>
27
+ #
17
28
  # @param count [Integer, Float::INFINITY, nil] The number to be displayed (e.x. # of issues, pull requests)
18
- # @param scheme [Symbol] Color scheme. One of `SCHEME_MAPPINGS.keys`.
29
+ # @param scheme [Symbol] Color scheme. <%= one_of(Primer::CounterComponent::SCHEME_OPTIONS) %>
19
30
  # @param limit [Integer, nil] Maximum value to display. Pass `nil` for no limit. (e.x. if `count` == 6,000 and `limit` == 5000, counter will display "5,000+")
20
31
  # @param hide_if_zero [Boolean] If true, a `hidden` attribute is added to the counter if `count` is zero.
21
32
  # @param text [String] Text to display instead of count.
@@ -41,8 +52,9 @@ module Primer
41
52
  @system_arguments[:title] = title
42
53
  @system_arguments[:tag] = :span
43
54
  @system_arguments[:classes] = class_names(
55
+ "Counter",
44
56
  @system_arguments[:classes],
45
- SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_MAPPINGS.keys, scheme, DEFAULT_SCHEME)]
57
+ SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_OPTIONS, scheme, DEFAULT_SCHEME, deprecated_values: DEPRECATED_SCHEME_OPTIONS)]
46
58
  )
47
59
  @system_arguments[:hidden] = true if count == 0 && hide_if_zero # rubocop:disable Style/NumericPredicate
48
60
  end
@@ -51,10 +63,6 @@ module Primer
51
63
  render(Primer::BaseComponent.new(**@system_arguments)) { value }
52
64
  end
53
65
 
54
- def self.status
55
- Primer::Component::STATUSES[:beta]
56
- end
57
-
58
66
  private
59
67
 
60
68
  def title
@@ -4,6 +4,7 @@ module Primer
4
4
  # Use DetailsComponent to reveal content after clicking a button.
5
5
  class DetailsComponent < Primer::Component
6
6
  include ViewComponent::SlotableV2
7
+ status :beta
7
8
 
8
9
  NO_OVERLAY = :none
9
10
  OVERLAY_MAPPINGS = {
@@ -16,12 +17,11 @@ module Primer
16
17
  #
17
18
  # @param button [Boolean] Whether to render the Summary as a button or not.
18
19
  # @param kwargs [Hash] The same arguments as <%= link_to_system_arguments_docs %>.
19
- renders_one :summary, lambda { |button = true, **system_arguments|
20
- system_arguments = system_arguments
20
+ renders_one :summary, lambda { |button: true, **system_arguments|
21
21
  system_arguments[:tag] = :summary
22
22
  system_arguments[:role] = "button"
23
23
 
24
- Primer::BaseComponent.new(**system_arguments) unless button
24
+ return Primer::BaseComponent.new(**system_arguments) unless button
25
25
 
26
26
  Primer::ButtonComponent.new(**system_arguments)
27
27
  }
@@ -5,6 +5,8 @@ module Primer
5
5
  # They're great for instances where you don't need the full power (and code)
6
6
  # of the select menu.
7
7
  class DropdownMenuComponent < Primer::Component
8
+ status :deprecated
9
+
8
10
  SCHEME_DEFAULT = :default
9
11
  SCHEME_MAPPINGS = {
10
12
  SCHEME_DEFAULT => "",
@@ -14,10 +16,6 @@ module Primer
14
16
  DIRECTION_DEFAULT = :se
15
17
  DIRECTION_OPTIONS = [DIRECTION_DEFAULT, :sw, :w, :e, :ne, :s].freeze
16
18
 
17
- def self.status
18
- STATUSES[:deprecated]
19
- end
20
-
21
19
  # @example With a header
22
20
  # <div>
23
21
  # <%= render(Primer::DetailsComponent.new(overlay: :default, reset: true, position: :relative)) do |c| %>
@@ -1,9 +1,9 @@
1
1
  <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
- <%= primer(:octicon, icon: @icon) if @icon %>
2
+ <%= primer_octicon icon: @icon if @icon %>
3
3
  <%= content %>
4
4
  <% if @dismissible %>
5
5
  <button class="flash-close js-flash-close" type="button" aria-label="Close">
6
- <%= primer(:octicon, icon: "x") %>
6
+ <%= primer_octicon icon: "x" %>
7
7
  </button>
8
8
  <% end %>
9
9
 
@@ -3,6 +3,8 @@
3
3
  module Primer
4
4
  # Use the Flash component to inform users of successful or pending actions.
5
5
  class FlashComponent < Primer::Component
6
+ status :beta
7
+
6
8
  include ViewComponent::SlotableV2
7
9
 
8
10
  # Optional action content showed on the right side of the component.
@@ -64,9 +66,5 @@ module Primer
64
66
  )
65
67
  @system_arguments[:mb] ||= spacious ? 4 : nil
66
68
  end
67
-
68
- def self.status
69
- Primer::Component::STATUSES[:beta]
70
- end
71
69
  end
72
70
  end
@@ -36,31 +36,31 @@ module Primer
36
36
  ALLOWED_DIRECTIONS = [DEFAULT_DIRECTION, :column, :column_reverse, :row, :row_reverse].freeze
37
37
 
38
38
  # @example Default
39
- # <%= render(Primer::FlexComponent.new(bg: :gray)) do %>
40
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 1" } %>
41
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 2" } %>
42
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 3" } %>
39
+ # <%= render(Primer::FlexComponent.new(bg: :tertiary)) do %>
40
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 1" } %>
41
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 2" } %>
42
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 3" } %>
43
43
  # <% end %>
44
44
  #
45
45
  # @example Justify center
46
- # <%= render(Primer::FlexComponent.new(justify_content: :center, bg: :gray)) do %>
47
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 1" } %>
48
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 2" } %>
49
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 3" } %>
46
+ # <%= render(Primer::FlexComponent.new(justify_content: :center, bg: :tertiary)) do %>
47
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 1" } %>
48
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 2" } %>
49
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 3" } %>
50
50
  # <% end %>
51
51
  #
52
52
  # @example Align end
53
- # <%= render(Primer::FlexComponent.new(align_items: :end, bg: :gray)) do %>
54
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 1" } %>
55
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 2" } %>
56
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 3" } %>
53
+ # <%= render(Primer::FlexComponent.new(align_items: :end, bg: :tertiary)) do %>
54
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 1" } %>
55
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 2" } %>
56
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 3" } %>
57
57
  # <% end %>
58
58
  #
59
59
  # @example Direction column
60
- # <%= render(Primer::FlexComponent.new(direction: :column, bg: :gray)) do %>
61
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 1" } %>
62
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 2" } %>
63
- # <%= render(Primer::BoxComponent.new(p: 5, bg: :gray_light, classes: "border")) { "Item 3" } %>
60
+ # <%= render(Primer::FlexComponent.new(direction: :column, bg: :tertiary)) do %>
61
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 1" } %>
62
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 2" } %>
63
+ # <%= render(Primer::BoxComponent.new(p: 5, bg: :secondary, classes: "border")) { "Item 3" } %>
64
64
  # <% end %>
65
65
  #
66
66
  # @param justify_content [Symbol] Use this param to distribute space between and around flex items along the main axis of the container. <%= one_of(Primer::FlexComponent::JUSTIFY_CONTENT_OPTIONS) %>
@@ -3,7 +3,7 @@
3
3
  module Primer
4
4
  # Use the Heading component to wrap a component that will create a heading element
5
5
  class HeadingComponent < Primer::Component
6
- view_helper :heading
6
+ status :beta
7
7
 
8
8
  # @example Default
9
9
  # <%= render(Primer::HeadingComponent.new) { "H1 Text" } %>
@@ -3,6 +3,8 @@
3
3
  module Primer
4
4
  # Use labels to add contextual metadata to a design.
5
5
  class LabelComponent < Primer::Component
6
+ status :beta
7
+
6
8
  SCHEME_MAPPINGS = {
7
9
  primary: "Label--primary",
8
10
  secondary: "Label--secondary",
@@ -14,7 +16,8 @@ module Primer
14
16
  orange: "Label--orange",
15
17
  purple: "Label--purple"
16
18
  }.freeze
17
- SCHEME_OPTIONS = SCHEME_MAPPINGS.keys << nil
19
+ DEPRECATED_SCHEME_OPTIONS = [:orange, :purple].freeze
20
+ SCHEME_OPTIONS = ([*SCHEME_MAPPINGS.keys, nil] - DEPRECATED_SCHEME_OPTIONS).freeze
18
21
 
19
22
  VARIANT_MAPPINGS = {
20
23
  large: "Label--large",
@@ -35,23 +38,18 @@ module Primer
35
38
  # <%= render(Primer::LabelComponent.new(title: "Label: Label")) { "Default" } %>
36
39
  # <%= render(Primer::LabelComponent.new(title: "Label: Label", variant: :large)) { "Large" } %>
37
40
  #
38
- # @example Deprecated schemes
39
- # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :orange)) { "Orange" } %>
40
- # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :purple)) { "Purple" } %>
41
- #
42
41
  # @param title [String] `title` attribute for the component element.
43
42
  # @param scheme [Symbol] <%= one_of(Primer::LabelComponent::SCHEME_MAPPINGS.keys) %>
44
43
  # @param variant [Symbol] <%= one_of(Primer::LabelComponent::VARIANT_OPTIONS) %>
45
44
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
46
45
  def initialize(title:, scheme: nil, variant: nil, **system_arguments)
47
46
  @system_arguments = system_arguments
48
- @system_arguments[:bg] = :blue if scheme.nil?
49
47
  @system_arguments[:tag] ||= :span
50
48
  @system_arguments[:title] = title
51
49
  @system_arguments[:classes] = class_names(
52
50
  "Label",
53
51
  system_arguments[:classes],
54
- SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_OPTIONS, scheme)],
52
+ SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_OPTIONS, scheme, deprecated_values: DEPRECATED_SCHEME_OPTIONS)],
55
53
  VARIANT_MAPPINGS[fetch_or_fallback(VARIANT_OPTIONS, variant)]
56
54
  )
57
55
  end
@@ -59,9 +57,5 @@ module Primer
59
57
  def call
60
58
  render(Primer::BaseComponent.new(**@system_arguments)) { content }
61
59
  end
62
-
63
- def self.status
64
- Primer::Component::STATUSES[:beta]
65
- end
66
60
  end
67
61
  end
@@ -1,17 +1,11 @@
1
1
  <%= render(Primer::FlexComponent.new(**@system_arguments)) do %>
2
2
  <% if @side == :left %>
3
- <%= render Primer::BaseComponent.new(tag: :div, classes: "flex-shrink-0", col: (@responsive ? [12, nil, @sidebar_col] : @sidebar_col), mb: (@responsive ? [4, nil, 0] : nil)) do %>
4
- <%= sidebar %>
5
- <% end %>
3
+ <%= sidebar %>
6
4
  <% end %>
7
5
 
8
- <%= render Primer::BaseComponent.new(tag: :div, classes: "flex-shrink-0", col: (@responsive ? [12, nil, @main_col] : @main_col), mb: (@responsive ? [4, nil, 0] : nil)) do %>
9
- <%= main %>
10
- <% end %>
6
+ <%= main %>
11
7
 
12
8
  <% if @side == :right %>
13
- <%= render Primer::BaseComponent.new(tag: :div, classes: "flex-shrink-0", col: (@responsive ? [12, nil, @sidebar_col] : @sidebar_col)) do %>
14
- <%= sidebar %>
15
- <% end %>
9
+ <%= sidebar %>
16
10
  <% end %>
17
11
  <% end %>
@@ -3,7 +3,32 @@
3
3
  module Primer
4
4
  # Use Layout to build a main/sidebar layout.
5
5
  class LayoutComponent < Primer::Component
6
- with_content_areas :main, :sidebar
6
+ include ViewComponent::SlotableV2
7
+
8
+ # The main content
9
+ #
10
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
11
+ renders_one :main, lambda { |**system_arguments|
12
+ system_arguments[:classes] = class_names("flex-shrink-0", system_arguments[:classes])
13
+ system_arguments[:col] = (@responsive ? [12, nil, @main_col] : @main_col)
14
+ system_arguments[:mb] = (@responsive ? [4, nil, 0] : nil)
15
+
16
+ Primer::BaseComponent.new(tag: :div, **system_arguments)
17
+ }
18
+
19
+ # The sidebar content
20
+ #
21
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
22
+ renders_one :sidebar, lambda { |**system_arguments|
23
+ system_arguments[:classes] = class_names("flex-shrink-0", system_arguments[:classes])
24
+ system_arguments[:col] = (@responsive ? [12, nil, @sidebar_col] : @sidebar_col)
25
+
26
+ if @side == :left
27
+ system_arguments[:mb] = (@responsive ? [4, nil, 0] : nil)
28
+ end
29
+
30
+ Primer::BaseComponent.new(tag: :div, **system_arguments)
31
+ }
7
32
 
8
33
  DEFAULT_SIDE = :right
9
34
  ALLOWED_SIDES = [DEFAULT_SIDE, :left].freeze
@@ -14,14 +39,14 @@ module Primer
14
39
 
15
40
  # @example Default
16
41
  # <%= render(Primer::LayoutComponent.new) do |component| %>
17
- # <% component.with(:sidebar) { "Sidebar" } %>
18
- # <% component.with(:main) { "Main" } %>
42
+ # <% component.sidebar { "Sidebar" } %>
43
+ # <% component.main { "Main" } %>
19
44
  # <% end %>
20
45
  #
21
46
  # @example Left sidebar
22
47
  # <%= render(Primer::LayoutComponent.new(side: :left)) do |component| %>
23
- # <% component.with(:sidebar) { "Sidebar" } %>
24
- # <% component.with(:main) { "Main" } %>
48
+ # <% component.sidebar { "Sidebar" } %>
49
+ # <% component.main { "Main" } %>
25
50
  # <% end %>
26
51
  #
27
52
  # @param responsive [Boolean] Whether to collapse layout to a single column at smaller widths.
@@ -3,22 +3,50 @@
3
3
  module Primer
4
4
  # Use links for moving from one page to another. The Link component styles anchor tags with default blue styling and hover text-decoration.
5
5
  class LinkComponent < Primer::Component
6
+ status :beta
7
+
8
+ DEFAULT_VARIANT = :default
9
+ VARIANT_MAPPINGS = {
10
+ DEFAULT_VARIANT => "",
11
+ :primary => "Link--primary",
12
+ :secondary => "Link--secondary"
13
+ }.freeze
14
+
15
+ DEFAULT_TAG = :a
16
+ TAG_OPTIONS = [DEFAULT_TAG, :span].freeze
17
+
6
18
  # @example Default
7
- # <%= render(Primer::LinkComponent.new(href: "http://www.google.com")) { "Link" } %>
19
+ # <%= render(Primer::LinkComponent.new(href: "#")) { "Link" } %>
8
20
  #
9
21
  # @example Muted
10
- # <%= render(Primer::LinkComponent.new(href: "http://www.google.com", muted: true)) { "Link" } %>
22
+ # <%= render(Primer::LinkComponent.new(href: "#", muted: true)) { "Link" } %>
23
+ #
24
+ # @example Variants
25
+ # <%= render(Primer::LinkComponent.new(href: "#", variant: :primary)) { "Primary" } %>
26
+ # <%= render(Primer::LinkComponent.new(href: "#", variant: :secondary)) { "Secondary" } %>
27
+ #
28
+ # @example Without underline
29
+ # <%= render(Primer::LinkComponent.new(href: "#", underline: false)) { "Link" } %>
30
+ #
31
+ # @example Span as link
32
+ # <%= render(Primer::LinkComponent.new(tag: :span)) { "Span as a link" } %>
11
33
  #
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
34
+ # @param tag [String] <%= one_of(Primer::LinkComponent::TAG_OPTIONS) %>
35
+ # @param href [String] URL to be used for the Link. Required if tag is `:a`. If the requirements are not met an error will be raised in non production environments. In production, an empty link element will be rendered.
36
+ # @param variant [Symbol] <%= one_of(Primer::LinkComponent::VARIANT_MAPPINGS.keys) %>
37
+ # @param muted [Boolean] Uses light gray for Link color, and blue on hover.
38
+ # @param underline [Boolean] Whether or not to underline the link.
14
39
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
15
- def initialize(href:, muted: false, **system_arguments)
40
+ def initialize(href: nil, tag: DEFAULT_TAG, variant: DEFAULT_VARIANT, muted: false, underline: true, **system_arguments)
16
41
  @system_arguments = system_arguments
17
- @system_arguments[:tag] = :a
42
+ @system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
18
43
  @system_arguments[:href] = href
19
44
  @system_arguments[:classes] = class_names(
20
45
  @system_arguments[:classes],
21
- "muted-link" => fetch_or_fallback_boolean(muted, false)
46
+ VARIANT_MAPPINGS[fetch_or_fallback(VARIANT_MAPPINGS.keys, variant, DEFAULT_VARIANT)],
47
+ "Link" => tag == :span,
48
+ "Link--muted" => muted,
49
+ "no-underline" => !underline
22
50
  )
23
51
  end
24
52
 
@@ -26,8 +54,8 @@ module Primer
26
54
  render(Primer::BaseComponent.new(**@system_arguments)) { content }
27
55
  end
28
56
 
29
- def self.status
30
- Primer::Component::STATUSES[:beta]
57
+ def before_render
58
+ raise ArgumentError, "href is required when using <a> tag" if @system_arguments[:tag] == :a && @system_arguments[:href].nil? && !Rails.env.production?
31
59
  end
32
60
  end
33
61
  end
@@ -3,9 +3,10 @@
3
3
  module Primer
4
4
  # Renders an [Octicon](https://primer.style/octicons/) with <%= link_to_system_arguments_docs %>.
5
5
  class OcticonComponent < Primer::Component
6
- view_helper :octicon
6
+ status :beta
7
7
 
8
- include Primer::ClassNameHelper
8
+ include ClassNameHelper
9
+ include TestSelectorHelper
9
10
  include OcticonsHelper
10
11
 
11
12
  SIZE_DEFAULT = :small
@@ -38,15 +39,11 @@ module Primer
38
39
  # Filter out classify options to prevent them from becoming invalid html attributes.
39
40
  # Note height and width are both classify options and valid html attributes.
40
41
  octicon_helper_options = @system_arguments.slice(:height, :width)
41
- @system_arguments = @system_arguments.except(*Primer::Classify::VALID_KEYS, :classes).merge(octicon_helper_options)
42
+ @system_arguments = add_test_selector(@system_arguments).except(*Primer::Classify::VALID_KEYS, :classes).merge(octicon_helper_options)
42
43
  end
43
44
 
44
45
  def call
45
46
  octicon(@icon, { **@system_arguments })
46
47
  end
47
-
48
- def self.status
49
- Primer::Component::STATUSES[:beta]
50
- end
51
48
  end
52
49
  end