primer_view_components 0.0.31 → 0.0.36

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +94 -0
  3. data/README.md +2 -2
  4. data/app/components/primer/{auto_complete_component.rb → auto_complete.rb} +12 -12
  5. data/app/components/primer/{auto_complete_component.d.ts → auto_complete/auto_complete.d.ts} +0 -0
  6. data/app/components/primer/{auto_complete_component.html.erb → auto_complete/auto_complete.html.erb} +0 -0
  7. data/app/components/primer/{auto_complete_component.js → auto_complete/auto_complete.js} +0 -0
  8. data/app/components/primer/{auto_complete_component.ts → auto_complete/auto_complete.ts} +0 -0
  9. data/app/components/primer/auto_complete/auto_component.d.ts +1 -0
  10. data/app/components/primer/auto_complete/auto_component.js +1 -0
  11. data/app/components/primer/auto_complete/item.rb +42 -0
  12. data/app/components/primer/avatar_stack_component.rb +1 -1
  13. data/app/components/primer/base_component.rb +115 -85
  14. data/app/components/primer/blankslate_component.html.erb +1 -1
  15. data/app/components/primer/blankslate_component.rb +0 -2
  16. data/app/components/primer/border_box_component.rb +0 -2
  17. data/app/components/primer/breadcrumb_component.rb +0 -1
  18. data/app/components/primer/button_component.rb +37 -16
  19. data/app/components/primer/button_group_component.rb +3 -5
  20. data/app/components/primer/button_marketing_component.rb +12 -12
  21. data/app/components/primer/close_button.rb +30 -0
  22. data/app/components/primer/component.rb +3 -0
  23. data/app/components/primer/details_component.rb +0 -1
  24. data/app/components/primer/dropdown/menu_component.rb +0 -2
  25. data/app/components/primer/dropdown_component.rb +1 -3
  26. data/app/components/primer/dropdown_menu_component.rb +1 -1
  27. data/app/components/primer/flash_component.html.erb +2 -2
  28. data/app/components/primer/flash_component.rb +10 -12
  29. data/app/components/primer/foo_bar.d.ts +1 -0
  30. data/app/components/primer/foo_bar.js +1 -0
  31. data/app/components/primer/hidden_text_expander.rb +43 -0
  32. data/app/components/primer/layout_component.rb +0 -2
  33. data/app/components/primer/link_component.rb +9 -9
  34. data/app/components/primer/menu_component.rb +2 -4
  35. data/app/components/primer/navigation/tab_component.html.erb +11 -0
  36. data/app/components/primer/navigation/tab_component.rb +126 -0
  37. data/app/components/primer/octicon_component.rb +5 -8
  38. data/app/components/primer/popover_component.rb +0 -1
  39. data/app/components/primer/primer.d.ts +1 -1
  40. data/app/components/primer/primer.js +1 -1
  41. data/app/components/primer/primer.ts +1 -1
  42. data/app/components/primer/progress_bar_component.rb +0 -1
  43. data/app/components/primer/spinner_component.html.erb +1 -3
  44. data/app/components/primer/spinner_component.rb +1 -0
  45. data/app/components/primer/state_component.rb +13 -13
  46. data/app/components/primer/subhead_component.rb +1 -3
  47. data/app/components/primer/tab_nav_component.html.erb +8 -10
  48. data/app/components/primer/tab_nav_component.rb +63 -76
  49. data/app/components/primer/time_ago_component.rb +2 -1
  50. data/app/components/primer/timeline_item_component.rb +1 -2
  51. data/app/components/primer/{truncate_component.rb → truncate.rb} +8 -6
  52. data/app/components/primer/underline_nav_component.html.erb +19 -7
  53. data/app/components/primer/underline_nav_component.rb +114 -16
  54. data/app/lib/primer/classify.rb +5 -14
  55. data/app/lib/primer/classify/cache.rb +14 -4
  56. data/app/lib/primer/classify/spacing.rb +63 -0
  57. data/app/lib/primer/tabbed_component_helper.rb +39 -0
  58. data/app/lib/primer/view_helper.rb +2 -2
  59. data/lib/primer/view_components/version.rb +1 -1
  60. data/static/statuses.json +1 -1
  61. metadata +110 -31
  62. data/app/assets/javascripts/primer_view_components.js.map.orig +0 -5
  63. data/app/assets/javascripts/primer_view_components.js.orig +0 -6
  64. data/app/components/primer/auto_complete_item_component.rb +0 -40
  65. data/app/components/primer/slot.rb +0 -10
@@ -5,9 +5,9 @@ module Primer
5
5
  class LinkComponent < Primer::Component
6
6
  status :beta
7
7
 
8
- DEFAULT_VARIANT = :default
9
- VARIANT_MAPPINGS = {
10
- DEFAULT_VARIANT => "",
8
+ DEFAULT_SCHEME = :default
9
+ SCHEME_MAPPINGS = {
10
+ DEFAULT_SCHEME => "",
11
11
  :primary => "Link--primary",
12
12
  :secondary => "Link--secondary"
13
13
  }.freeze
@@ -21,9 +21,9 @@ module Primer
21
21
  # @example Muted
22
22
  # <%= render(Primer::LinkComponent.new(href: "#", muted: true)) { "Link" } %>
23
23
  #
24
- # @example Variants
25
- # <%= render(Primer::LinkComponent.new(href: "#", variant: :primary)) { "Primary" } %>
26
- # <%= render(Primer::LinkComponent.new(href: "#", variant: :secondary)) { "Secondary" } %>
24
+ # @example Schemes
25
+ # <%= render(Primer::LinkComponent.new(href: "#", scheme: :primary)) { "Primary" } %>
26
+ # <%= render(Primer::LinkComponent.new(href: "#", scheme: :secondary)) { "Secondary" } %>
27
27
  #
28
28
  # @example Without underline
29
29
  # <%= render(Primer::LinkComponent.new(href: "#", underline: false)) { "Link" } %>
@@ -33,17 +33,17 @@ module Primer
33
33
  #
34
34
  # @param tag [String] <%= one_of(Primer::LinkComponent::TAG_OPTIONS) %>
35
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) %>
36
+ # @param scheme [Symbol] <%= one_of(Primer::LinkComponent::SCHEME_MAPPINGS.keys) %>
37
37
  # @param muted [Boolean] Uses light gray for Link color, and blue on hover.
38
38
  # @param underline [Boolean] Whether or not to underline the link.
39
39
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
40
- def initialize(href: nil, tag: DEFAULT_TAG, variant: DEFAULT_VARIANT, muted: false, underline: true, **system_arguments)
40
+ def initialize(href: nil, tag: DEFAULT_TAG, scheme: DEFAULT_SCHEME, muted: false, underline: true, **system_arguments)
41
41
  @system_arguments = system_arguments
42
42
  @system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
43
43
  @system_arguments[:href] = href
44
44
  @system_arguments[:classes] = class_names(
45
45
  @system_arguments[:classes],
46
- VARIANT_MAPPINGS[fetch_or_fallback(VARIANT_MAPPINGS.keys, variant, DEFAULT_VARIANT)],
46
+ SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_MAPPINGS.keys, scheme, DEFAULT_SCHEME)],
47
47
  "Link" => tag == :span,
48
48
  "Link--muted" => muted,
49
49
  "no-underline" => !underline
@@ -3,8 +3,6 @@
3
3
  module Primer
4
4
  # Use menus to create vertical lists of navigational links.
5
5
  class MenuComponent < Primer::Component
6
- include ViewComponent::SlotableV2
7
-
8
6
  # Optional menu heading
9
7
  #
10
8
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
@@ -44,11 +42,11 @@ module Primer
44
42
  # Item 1
45
43
  # <% end %>
46
44
  # <% c.item(href: "#url") do %>
47
- # <%= render(Primer::OcticonComponent.new(icon: "check")) %>
45
+ # <%= render(Primer::OcticonComponent.new("check")) %>
48
46
  # With Icon
49
47
  # <% end %>
50
48
  # <% c.item(href: "#url") do %>
51
- # <%= render(Primer::OcticonComponent.new(icon: "check")) %>
49
+ # <%= render(Primer::OcticonComponent.new("check")) %>
52
50
  # With Icon and Counter
53
51
  # <%= render(Primer::CounterComponent.new(count: 25)) %>
54
52
  # <% end %>
@@ -0,0 +1,11 @@
1
+ <%= wrapper do %>
2
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
3
+ <%= icon %>
4
+ <% if text.present? %>
5
+ <%= text %>
6
+ <% else %>
7
+ <%= content %>
8
+ <% end %>
9
+ <%= counter %>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Navigation
5
+ # This component is part of navigation components such as `Primer::TabNavComponent`
6
+ # and `Primer::UnderlineNavComponent` and should not be used by itself.
7
+ class TabComponent < Primer::Component
8
+ # Panel controlled by the Tab. This will not render anything in the tab itself.
9
+ # It will provide a accessor for the Tab's parent to call and render the panel
10
+ # content in the appropriate place.
11
+ # Refer to `UnderlineNavComponent` and `TabNavComponent` implementations for examples.
12
+ #
13
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
14
+ renders_one :panel, lambda { |**system_arguments|
15
+ system_arguments[:tag] ||= :div
16
+ system_arguments[:role] ||= :tabpanel
17
+ system_arguments[:hidden] = true unless @selected
18
+
19
+ Primer::BaseComponent.new(**system_arguments)
20
+ }
21
+
22
+ # Icon to be rendered in the Tab left.
23
+ #
24
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::OcticonComponent) %>.
25
+ renders_one :icon, lambda { |icon = nil, **system_arguments|
26
+ system_arguments[:classes] = class_names(
27
+ @icon_classes,
28
+ system_arguments[:classes]
29
+ )
30
+ Primer::OcticonComponent.new(icon, **system_arguments)
31
+ }
32
+
33
+ # The Tab's text.
34
+ #
35
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::TextComponent) %>.
36
+ renders_one :text, Primer::TextComponent
37
+
38
+ # Counter to be rendered in the Tab right.
39
+ #
40
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::CounterComponent) %>.
41
+ renders_one :counter, Primer::CounterComponent
42
+
43
+ attr_reader :selected
44
+
45
+ # @example Default
46
+ # <%= render(Primer::Navigation::TabComponent.new(selected: true)) do |c| %>
47
+ # <% c.text { "Selected" } %>
48
+ # <% end %>
49
+ # <%= render(Primer::Navigation::TabComponent.new) do |c| %>
50
+ # <% c.text { "Not selected" } %>
51
+ # <% end %>
52
+ #
53
+ # @example With icons and counters
54
+ # <%= render(Primer::Navigation::TabComponent.new) do |c| %>
55
+ # <% c.icon(:star) %>
56
+ # <% c.text { "Tab" } %>
57
+ # <% end %>
58
+ # <%= render(Primer::Navigation::TabComponent.new) do |c| %>
59
+ # <% c.icon(:star) %>
60
+ # <% c.text { "Tab" } %>
61
+ # <% c.counter(count: 10) %>
62
+ # <% end %>
63
+ # <%= render(Primer::Navigation::TabComponent.new) do |c| %>
64
+ # <% c.text { "Tab" } %>
65
+ # <% c.counter(count: 10) %>
66
+ # <% end %>
67
+ #
68
+ # @example Inside a list
69
+ # <%= render(Primer::Navigation::TabComponent.new(list: true)) do |c| %>
70
+ # <% c.text { "Tab" } %>
71
+ # <% end %>
72
+ #
73
+ # @example With custom HTML
74
+ # <%= render(Primer::Navigation::TabComponent.new) do %>
75
+ # <div>
76
+ # This is my <strong>custom HTML</strong>
77
+ # </div>
78
+ # <% end %>
79
+ #
80
+ # @param list [Boolean] Whether the Tab is an item in a `<ul>` list.
81
+ # @param selected [Boolean] Whether the Tab is selected or not.
82
+ # @param with_panel [Boolean] Whether the Tab has an associated panel.
83
+ # @param icon_classes [Boolean] Classes that must always be applied to icons.
84
+ # @param wrapper_arguments [Hash] <%= link_to_system_arguments_docs %> to be used in the `<li>` wrapper when the tab is an item in a list.
85
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
86
+ def initialize(list: false, selected: false, with_panel: false, icon_classes: "", wrapper_arguments: {}, **system_arguments)
87
+ @selected = selected
88
+ @icon_classes = icon_classes
89
+ @list = list
90
+
91
+ @system_arguments = system_arguments
92
+
93
+ if with_panel
94
+ @system_arguments[:tag] ||= :button
95
+ @system_arguments[:type] = :button
96
+ @system_arguments[:role] = :tab
97
+ else
98
+ @system_arguments[:tag] ||= :a
99
+ end
100
+
101
+ @wrapper_arguments = wrapper_arguments
102
+ @wrapper_arguments[:tag] = :li
103
+ @wrapper_arguments[:display] ||= :flex
104
+
105
+ return unless @selected
106
+
107
+ if @system_arguments[:tag] == :a
108
+ @system_arguments[:"aria-current"] = :page
109
+ else
110
+ @system_arguments[:"aria-selected"] = true
111
+ end
112
+ end
113
+
114
+ def wrapper
115
+ unless @list
116
+ yield
117
+ return # returning `yield` caused a double render
118
+ end
119
+
120
+ render(Primer::BaseComponent.new(**@wrapper_arguments)) do
121
+ yield
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -5,10 +5,6 @@ module Primer
5
5
  class OcticonComponent < Primer::Component
6
6
  status :beta
7
7
 
8
- include ClassNameHelper
9
- include TestSelectorHelper
10
- include OcticonsHelper
11
-
12
8
  SIZE_DEFAULT = :small
13
9
  SIZE_MAPPINGS = {
14
10
  SIZE_DEFAULT => 16,
@@ -18,19 +14,20 @@ module Primer
18
14
  SIZE_OPTIONS = SIZE_MAPPINGS.keys
19
15
 
20
16
  # @example Default
17
+ # <%= render(Primer::OcticonComponent.new("check")) %>
21
18
  # <%= render(Primer::OcticonComponent.new(icon: "check")) %>
22
19
  #
23
20
  # @example Medium
24
- # <%= render(Primer::OcticonComponent.new(icon: "people", size: :medium)) %>
21
+ # <%= render(Primer::OcticonComponent.new("people", size: :medium)) %>
25
22
  #
26
23
  # @example Large
27
- # <%= render(Primer::OcticonComponent.new(icon: "x", size: :large)) %>
24
+ # <%= render(Primer::OcticonComponent.new("x", size: :large)) %>
28
25
  #
29
26
  # @param icon [String] Name of [Octicon](https://primer.style/octicons/) to use.
30
27
  # @param size [Symbol] <%= one_of(Primer::OcticonComponent::SIZE_MAPPINGS) %>
31
28
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
32
- def initialize(icon:, size: SIZE_DEFAULT, **system_arguments)
33
- @icon = icon
29
+ def initialize(icon_name = nil, icon: nil, size: SIZE_DEFAULT, **system_arguments)
30
+ @icon = icon_name || icon
34
31
  @system_arguments = system_arguments
35
32
 
36
33
  @system_arguments[:class] = Primer::Classify.call(**@system_arguments)[:class]
@@ -5,7 +5,6 @@ module Primer
5
5
  #
6
6
  # By default, the popover renders with absolute positioning, meaning it should usually be wrapped in an element with a relative position in order to be positioned properly. To render the popover with relative positioning, use the relative property.
7
7
  class PopoverComponent < Primer::Component
8
- include ViewComponent::SlotableV2
9
8
  status :beta
10
9
 
11
10
  CARET_DEFAULT = :top
@@ -1,3 +1,3 @@
1
- import './auto_complete_component';
1
+ import './auto_complete/auto_complete';
2
2
  import './tab_container_component';
3
3
  import './time_ago_component';
@@ -1,3 +1,3 @@
1
- import './auto_complete_component';
1
+ import './auto_complete/auto_complete';
2
2
  import './tab_container_component';
3
3
  import './time_ago_component';
@@ -1,3 +1,3 @@
1
- import './auto_complete_component'
1
+ import './auto_complete/auto_complete'
2
2
  import './tab_container_component'
3
3
  import './time_ago_component'
@@ -3,7 +3,6 @@
3
3
  module Primer
4
4
  # Use ProgressBar to visualize task completion.
5
5
  class ProgressBarComponent < Primer::Component
6
- include ViewComponent::SlotableV2
7
6
  status :beta
8
7
 
9
8
  # Use the Item slot to add an item to the progress bas
@@ -1,6 +1,4 @@
1
1
  <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
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
- <animateTransform attributeName="transform" type="rotate" from="0 8 8" to="360 8 8" dur="1s" repeatCount="indefinite" />
5
- </path>
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" />
6
4
  <% end %>
@@ -31,6 +31,7 @@ module Primer
31
31
  @system_arguments = system_arguments
32
32
  @system_arguments[:tag] = :svg
33
33
  @system_arguments[:style] ||= DEFAULT_STYLE
34
+ @system_arguments[:animation] = :rotate
34
35
  @system_arguments[:width] = SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)]
35
36
  @system_arguments[:height] = SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)]
36
37
  @system_arguments[:viewBox] = "0 0 16 16"
@@ -5,21 +5,21 @@ module Primer
5
5
  class StateComponent < Primer::Component
6
6
  status :beta
7
7
 
8
- COLOR_DEFAULT = :default
9
- NEW_COLOR_MAPPINGS = {
8
+ SCHEME_DEFAULT = :default
9
+ NEW_SCHEME_MAPPINGS = {
10
10
  open: "State--open",
11
11
  closed: "State--closed",
12
12
  merged: "State--merged"
13
13
  }.freeze
14
14
 
15
- DEPRECATED_COLOR_MAPPINGS = {
16
- COLOR_DEFAULT => "",
15
+ DEPRECATED_SCHEME_MAPPINGS = {
16
+ SCHEME_DEFAULT => "",
17
17
  :green => "State--open",
18
18
  :red => "State--closed",
19
19
  :purple => "State--merged"
20
20
  }.freeze
21
- COLOR_MAPPINGS = NEW_COLOR_MAPPINGS.merge(DEPRECATED_COLOR_MAPPINGS)
22
- COLOR_OPTIONS = COLOR_MAPPINGS.keys
21
+ SCHEME_MAPPINGS = NEW_SCHEME_MAPPINGS.merge(DEPRECATED_SCHEME_MAPPINGS)
22
+ SCHEME_OPTIONS = SCHEME_MAPPINGS.keys
23
23
 
24
24
  SIZE_DEFAULT = :default
25
25
  SIZE_MAPPINGS = {
@@ -34,24 +34,24 @@ module Primer
34
34
  # @example Default
35
35
  # <%= render(Primer::StateComponent.new(title: "title")) { "State" } %>
36
36
  #
37
- # @example Colors
37
+ # @example Schemes
38
38
  # <%= render(Primer::StateComponent.new(title: "title")) { "Default" } %>
39
- # <%= render(Primer::StateComponent.new(title: "title", color: :open)) { "Open" } %>
40
- # <%= render(Primer::StateComponent.new(title: "title", color: :closed)) { "Closed" } %>
41
- # <%= render(Primer::StateComponent.new(title: "title", color: :merged)) { "Merged" } %>
39
+ # <%= render(Primer::StateComponent.new(title: "title", scheme: :open)) { "Open" } %>
40
+ # <%= render(Primer::StateComponent.new(title: "title", scheme: :closed)) { "Closed" } %>
41
+ # <%= render(Primer::StateComponent.new(title: "title", scheme: :merged)) { "Merged" } %>
42
42
  #
43
43
  # @example Sizes
44
44
  # <%= render(Primer::StateComponent.new(title: "title")) { "Default" } %>
45
45
  # <%= render(Primer::StateComponent.new(title: "title", size: :small)) { "Small" } %>
46
46
  #
47
47
  # @param title [String] `title` HTML attribute.
48
- # @param color [Symbol] Background color. <%= one_of(Primer::StateComponent::COLOR_OPTIONS) %>
48
+ # @param scheme [Symbol] Background color. <%= one_of(Primer::StateComponent::SCHEME_OPTIONS) %>
49
49
  # @param tag [Symbol] HTML tag for element. <%= one_of(Primer::StateComponent::TAG_OPTIONS) %>
50
50
  # @param size [Symbol] <%= one_of(Primer::StateComponent::SIZE_OPTIONS) %>
51
51
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
52
52
  def initialize(
53
53
  title:,
54
- color: COLOR_DEFAULT,
54
+ scheme: SCHEME_DEFAULT,
55
55
  tag: TAG_DEFAULT,
56
56
  size: SIZE_DEFAULT,
57
57
  **system_arguments
@@ -62,7 +62,7 @@ module Primer
62
62
  @system_arguments[:classes] = class_names(
63
63
  @system_arguments[:classes],
64
64
  "State",
65
- COLOR_MAPPINGS[fetch_or_fallback(COLOR_OPTIONS, color, COLOR_DEFAULT)],
65
+ SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_OPTIONS, scheme, SCHEME_DEFAULT)],
66
66
  SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, SIZE_DEFAULT)]
67
67
  )
68
68
  end
@@ -5,8 +5,6 @@ module Primer
5
5
  class SubheadComponent < Primer::Component
6
6
  status :beta
7
7
 
8
- include ViewComponent::SlotableV2
9
-
10
8
  # The heading
11
9
  #
12
10
  # @param danger [Boolean] Whether to style the heading as dangerous.
@@ -73,7 +71,7 @@ module Primer
73
71
  # <% component.actions do %>
74
72
  # <%= render(
75
73
  # Primer::ButtonComponent.new(
76
- # tag: :a, href: "http://www.google.com", button_type: :danger
74
+ # tag: :a, href: "http://www.google.com", scheme: :danger
77
75
  # )
78
76
  # ) { "Action" } %>
79
77
  # <% end %>
@@ -1,17 +1,15 @@
1
- <%= render wrapper.new(**@system_arguments) do %>
2
- <nav role="tablist" aria-label="<%= @aria_label %>" class="tabnav-tabs">
3
- <% tabs.each do |tab| %>
4
- <%= tab %>
1
+ <%= wrapper do %>
2
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
3
+ <%= render Primer::BaseComponent.new(**@body_arguments) do %>
4
+ <% tabs.each do |tab| %>
5
+ <%= tab %>
6
+ <% end %>
5
7
  <% end %>
6
- </nav >
8
+ <% end %>
7
9
 
8
10
  <% if @with_panel %>
9
11
  <% tabs.each do |tab| %>
10
- <% if tab.panel.present? %>
11
- <div role="tabpanel" <%= "hidden" if tab.hidden? %>>
12
- <%= tab.panel %>
13
- </div>
14
- <% end %>
12
+ <%= tab.panel %>
15
13
  <% end %>
16
14
  <% end %>
17
15
  <% end %>
@@ -3,106 +3,93 @@
3
3
  module Primer
4
4
  # Use TabNav to style navigation with a tab-based selected state, typically used for navigation placed at the top of the page.
5
5
  class TabNavComponent < Primer::Component
6
- include ViewComponent::SlotableV2
6
+ include Primer::TabbedComponentHelper
7
7
 
8
- class MultipleSelectedTabsError < StandardError; end
9
- class NoSelectedTabsError < StandardError; end
10
-
11
- # Tabs to be rendered.
8
+ # Tabs to be rendered. For more information, refer to <%= link_to_component(Primer::Navigation::TabComponent) %>.
12
9
  #
13
- # @param title [String] Text to be rendered by the tab.
14
10
  # @param selected [Boolean] Whether the tab is selected.
15
11
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
16
- renders_many :tabs, lambda { |**system_arguments|
17
- return TabComponent.new(**system_arguments) unless @with_panel
12
+ renders_many :tabs, lambda { |selected: false, **system_arguments|
13
+ system_arguments[:classes] = class_names(
14
+ "tabnav-tab",
15
+ system_arguments[:classes]
16
+ )
18
17
 
19
- TabComponent.new(tag: :button, type: :button, **system_arguments)
18
+ Primer::Navigation::TabComponent.new(
19
+ selected: selected,
20
+ with_panel: @with_panel,
21
+ **system_arguments
22
+ )
20
23
  }
21
24
 
22
25
  # @example Default
23
- # <%= render(Primer::TabNavComponent.new) do |c| %>
24
- # <% c.tab(selected: true, title: "Tab 1", href: "#") %>
25
- # <% c.tab(title: "Tab 2", href: "#") %>
26
- # <% c.tab(title: "Tab 3", href: "#") %>
26
+ # <%= render(Primer::TabNavComponent.new(label: "Default")) do |c| %>
27
+ # <% c.tab(selected: true, href: "#") { "Tab 1" }%>
28
+ # <% c.tab(href: "#") { "Tab 2" } %>
29
+ # <% c.tab(href: "#") { "Tab 3" } %>
30
+ # <% end %>
31
+ #
32
+ # @example With icons and counters
33
+ # <%= render(Primer::TabNavComponent.new(label: "With icons and counters")) do |component| %>
34
+ # <% component.tab(href: "#", selected: true) do |t| %>
35
+ # <% t.icon(icon: :star) %>
36
+ # <% t.text { "Item 1" } %>
37
+ # <% end %>
38
+ # <% component.tab(href: "#") do |t| %>
39
+ # <% t.icon(icon: :star) %>
40
+ # <% t.text { "Item 2" } %>
41
+ # <% t.counter(count: 10) %>
42
+ # <% end %>
43
+ # <% component.tab(href: "#") do |t| %>
44
+ # <% t.text { "Item 3" } %>
45
+ # <% t.counter(count: 10) %>
46
+ # <% end %>
27
47
  # <% end %>
28
48
  #
29
49
  # @example With panels
30
- # <%= render(Primer::TabNavComponent.new(with_panel: true)) do |c| %>
31
- # <% c.tab(selected: true, title: "Tab 1") { "Panel 1" } %>
32
- # <% c.tab(title: "Tab 2") { "Panel 1" } %>
33
- # <% c.tab(title: "Tab 3") { "Panel 1" } %>
50
+ # <%= render(Primer::TabNavComponent.new(label: "With panels", with_panel: true)) do |c| %>
51
+ # <% c.tab(selected: true) do |t| %>
52
+ # <% t.text { "Tab 1" } %>
53
+ # <% t.panel do %>
54
+ # Panel 1
55
+ # <% end %>
56
+ # <% end %>
57
+ # <% c.tab do |t| %>
58
+ # <% t.text { "Tab 2" } %>
59
+ # <% t.panel do %>
60
+ # Panel 2
61
+ # <% end %>
62
+ # <% end %>
63
+ # <% c.tab do |t| %>
64
+ # <% t.text { "Tab 3" } %>
65
+ # <% t.panel do %>
66
+ # Panel 3
67
+ # <% end %>
68
+ # <% end %>
34
69
  # <% end %>
35
70
  #
36
- # @param aria_label [String] Used to set the `aria-label` on the top level `<nav>` element.
71
+ # @param label [String] Used to set the `aria-label` on the top level `<nav>` element.
37
72
  # @param with_panel [Boolean] Whether the TabNav should navigate through pages or panels.
38
73
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
39
- def initialize(aria_label: nil, with_panel: false, **system_arguments)
40
- @aria_label = aria_label
74
+ def initialize(label:, with_panel: false, **system_arguments)
41
75
  @with_panel = with_panel
42
76
  @system_arguments = system_arguments
43
- @system_arguments[:tag] ||= :div
44
77
 
78
+ @system_arguments[:tag] ||= :div
45
79
  @system_arguments[:classes] = class_names(
46
80
  "tabnav",
47
81
  system_arguments[:classes]
48
82
  )
49
- end
50
-
51
- def before_render
52
- validate_single_selected_tab
53
- end
54
-
55
- private
56
-
57
- def wrapper
58
- @with_panel ? Primer::TabContainerComponent : Primer::BaseComponent
59
- end
60
-
61
- def validate_single_selected_tab
62
- raise MultipleSelectedTabsError, "only one tab can be selected" if selected_tabs_count > 1
63
- raise NoSelectedTabsError, "a tab must be selected" if selected_tabs_count != 1
64
- end
65
-
66
- def selected_tabs_count
67
- @selected_tabs_count ||= tabs.count(&:selected)
68
- end
69
-
70
- # Tabs to be rendered.
71
- class TabComponent < Primer::Component
72
- attr_reader :selected
73
-
74
- def initialize(title:, selected: false, **system_arguments)
75
- @title = title
76
- @selected = selected
77
- @system_arguments = system_arguments
78
- @system_arguments[:tag] ||= :a
79
- @system_arguments[:role] = :tab
80
-
81
- if selected
82
- if @system_arguments[:tag] == :a
83
- @system_arguments[:"aria-current"] = :page
84
- else
85
- @system_arguments[:"aria-selected"] = true
86
- end
87
- end
88
-
89
- @system_arguments[:classes] = class_names(
90
- "tabnav-tab",
91
- system_arguments[:classes]
92
- )
93
- end
94
-
95
- def call
96
- render(Primer::BaseComponent.new(**@system_arguments)) { @title }
97
- end
98
83
 
99
- def panel
100
- content
101
- end
84
+ @body_arguments = {
85
+ tag: navigation_tag(with_panel),
86
+ classes: "tabnav-tabs",
87
+ aria: {
88
+ label: label
89
+ }
90
+ }
102
91
 
103
- def hidden?
104
- !@selected
105
- end
92
+ @body_arguments[:role] = :tablist if @with_panel
106
93
  end
107
94
  end
108
95
  end