primer_view_components 0.0.47 → 0.0.51

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +113 -0
  3. data/app/components/primer/base_component.rb +2 -2
  4. data/app/components/primer/beta/auto_complete.rb +2 -2
  5. data/app/components/primer/beta/avatar.rb +1 -1
  6. data/app/components/primer/{avatar_stack_component.html.erb → beta/avatar_stack.html.erb} +0 -0
  7. data/app/components/primer/beta/avatar_stack.rb +92 -0
  8. data/app/components/primer/clipboard_copy.html.erb +2 -2
  9. data/app/components/primer/component.rb +5 -1
  10. data/app/components/primer/image_crop.html.erb +4 -4
  11. data/app/components/primer/label_component.rb +13 -12
  12. data/app/components/primer/navigation/tab_component.rb +30 -2
  13. data/app/components/primer/tab_nav_component.rb +4 -3
  14. data/app/components/primer/truncate.rb +1 -1
  15. data/app/components/primer/underline_nav_component.rb +3 -2
  16. data/app/lib/primer/octicon/cache.rb +1 -1
  17. data/lib/primer/classify.rb +0 -10
  18. data/lib/primer/classify/cache.rb +0 -5
  19. data/lib/primer/classify/utilities.rb +35 -12
  20. data/lib/primer/classify/utilities.yml +16 -0
  21. data/lib/primer/view_components.rb +34 -6
  22. data/lib/primer/view_components/constants.rb +55 -0
  23. data/lib/primer/view_components/linters/argument_mappers/base.rb +74 -0
  24. data/lib/primer/view_components/linters/argument_mappers/button.rb +32 -44
  25. data/lib/primer/view_components/linters/argument_mappers/clipboard_copy.rb +20 -0
  26. data/lib/primer/view_components/linters/argument_mappers/helpers/erb_block.rb +24 -0
  27. data/lib/primer/view_components/linters/argument_mappers/label.rb +50 -0
  28. data/lib/primer/view_components/linters/argument_mappers/system_arguments.rb +4 -1
  29. data/lib/primer/view_components/linters/autocorrectable.rb +30 -0
  30. data/lib/primer/view_components/linters/button_component_migration_counter.rb +9 -23
  31. data/lib/primer/view_components/linters/clipboard_copy_component_migration_counter.rb +21 -0
  32. data/lib/primer/view_components/linters/close_button_component_migration_counter.rb +16 -0
  33. data/lib/primer/view_components/linters/helpers.rb +42 -41
  34. data/lib/primer/view_components/linters/label_component_migration_counter.rb +25 -0
  35. data/lib/primer/view_components/version.rb +1 -1
  36. data/lib/rubocop/config/default.yml +12 -0
  37. data/lib/rubocop/cop/primer.rb +4 -0
  38. data/lib/rubocop/cop/primer/no_tag_memoize.rb +42 -0
  39. data/lib/rubocop/cop/primer/system_argument_instead_of_class.rb +85 -0
  40. data/lib/tasks/constants.rake +12 -0
  41. data/lib/tasks/docs.rake +24 -23
  42. data/lib/tasks/utilities.rake +4 -10
  43. data/lib/yard/docs_helper.rb +12 -3
  44. data/static/arguments.yml +973 -0
  45. data/static/assets/view-components.svg +18 -0
  46. data/static/classes.yml +174 -0
  47. data/static/constants.json +628 -0
  48. data/static/statuses.json +1 -1
  49. metadata +26 -8
  50. data/app/components/primer/avatar_stack_component.rb +0 -90
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b0ba62c9f5f11df09d24b40ca5e61e7d618046aba651ad733c8db06c482fe26
4
- data.tar.gz: 34c0b8066b85d5392dd241baeb3b654ba47d7de1f360b793ac893750989d99dd
3
+ metadata.gz: 2e619cd65db4a67059cc81f1d9f7d57a9c3b6c9cf9dadf9cfb2cd4c37fdcedc2
4
+ data.tar.gz: 18eaf89147d7a3e415520b09f117bf4b748fbb9a06e166df12ccc3a930506007
5
5
  SHA512:
6
- metadata.gz: 47e1bca34582e4406fcc268ac786a322cc3f99a62789aeece262b91e1ca26a15c145835ad60afbcd85a13b3a37da7a54541aedf8e2af5b60c43d7d6cf6767072
7
- data.tar.gz: 2edfb035e82896ea22bec576a98b34eb2e3c18cccf2d3ba18744f7b77135f6a1310b466179025c216d396708fdf5fbe802a2dc436c57d1510632eb9567776df1
6
+ metadata.gz: 821f1b4b53b823049bbc8e1a740b1f21d36ada796e69c59471c9f9a28183b5d94a61958544ef3c3e6386424b595e72627abcab2cd05d61de10104ea40941e201
7
+ data.tar.gz: 58d0e5f7961736bee805ed3d99d5432bb4dfa16df8170abe89b1f24f2857cf80a17a578fb762b0f6f1e5b307fe96a9803e960cce6a1a2901282908e678295f79
data/CHANGELOG.md CHANGED
@@ -30,6 +30,119 @@ The category for changes related to documentation, testing and tooling. Also, fo
30
30
 
31
31
  ## main
32
32
 
33
+ ## 0.0.51
34
+
35
+ ### Breaking changes
36
+
37
+ * Rename `width` and `height` System Arguments to `w` and `h`, resolving conflict with HTML attribute names.
38
+
39
+ *Manuel Puyol*
40
+
41
+ ### Updates
42
+
43
+ * `SystemArgumentInsteadOfClass` linter will check for arguments in ViewHelpers.
44
+
45
+ *Manuel Puyol*
46
+
47
+ ## 0.0.50
48
+
49
+ ### New
50
+
51
+ * Add linter suggestions for `CloseButton` component.
52
+
53
+ *Manuel Puyol*
54
+
55
+ ### Breaking changes
56
+
57
+ * Update to `octicons` `v15`, removing open-ended dependency. See [https://github.com/primer/octicons/releases/tag/v15.0.0] for icon name changes in release.
58
+
59
+ *Joel Hawksley*
60
+
61
+ ### Updates
62
+
63
+ * Don't require `title` for `Label`.
64
+
65
+ *Manuel Puyol*
66
+
67
+ * Improve autocorrectable linters to convert known SystemArgument classes.
68
+
69
+ *Manuel Puyol*
70
+
71
+ * Add support for `width: :full` and `height: :full` to System Arguments.
72
+
73
+ *Joel Hawksley*
74
+
75
+ ### Bug fixes
76
+
77
+ * Update linters to not autocorrect attributes with ERB blocks.
78
+
79
+ *Manuel Puyol*
80
+
81
+ * Fix `:height` and `:width` docs to pull from Utilities
82
+
83
+ *Jon Rohan*
84
+
85
+ ## 0.0.49
86
+
87
+ ### New
88
+
89
+ * Add linter suggestions for `Label` component.
90
+
91
+ *Manuel Puyol*
92
+
93
+ * Add linter suggestions for `ClipboardCopy` component.
94
+
95
+ *Manuel Puyol*
96
+
97
+ ### Updates
98
+
99
+ * Update the `Truncate` component to accept `:strong` as a tag.
100
+
101
+ *Amélia Chavot*
102
+
103
+ * Improve `Primer::Classify::Utilities.classes_to_hash` performance.
104
+
105
+ *Manuel Puyol*
106
+
107
+ ### Breaking changes
108
+
109
+ * Require tab with panels to have `panel_id` so `aria-controls` can be set.
110
+
111
+ *Kate Higa*
112
+
113
+ * Renames:
114
+ * `Primer::AvatarStackComponent` to `Primer::Beta::AvatarStack`.
115
+
116
+ *Manuel Puyol*
117
+
118
+ ### Misc
119
+
120
+ * Extract example tag parsing into helper.
121
+
122
+ *Kate Higa*
123
+
124
+ * Generate a static constant JSON and use it when defining linters.
125
+
126
+ *Manuel Puyol*
127
+
128
+ ## 0.0.48
129
+
130
+ ### Breaking changes
131
+
132
+ * Ensure panels in `Navigation::Tab` have a label.
133
+
134
+ *Kate Higa*
135
+
136
+ ### Misc
137
+
138
+ * Expose custom cops and default config for erblint.
139
+
140
+ *Manuel Puyol*
141
+
142
+ * Fix double constant assign.
143
+
144
+ *Manuel Puyol*
145
+
33
146
  ## 0.0.47
34
147
 
35
148
  ### Breaking changes
@@ -79,7 +79,6 @@ module Primer
79
79
  # | `flex_shrink` | Integer | To enable, set to `0`. |
80
80
  # | `flex_wrap` | Symbol | <%= one_of(Primer::Classify::Flex::WRAP_MAPPINGS.keys) %> |
81
81
  # | `justify_content` | Symbol | <%= one_of(Primer::Classify::Flex::JUSTIFY_CONTENT_VALUES) %> |
82
- # | `width` | Symbol | <%= one_of([:fit]) %> |
83
82
  #
84
83
  # ## Grid
85
84
  #
@@ -94,7 +93,8 @@ module Primer
94
93
  # | Name | Type | Description |
95
94
  # | :- | :- | :- |
96
95
  # | `display` | Symbol | <%= one_of(Primer::Classify::Utilities.mappings(:display)) %> |
97
- # | `height` | Symbol | <%= one_of([:fit]) %> |
96
+ # | `w` | Symbol | <%= one_of(Primer::Classify::Utilities.mappings(:w)) %> Also supports integer values. |
97
+ # | `h` | Symbol | <%= one_of(Primer::Classify::Utilities.mappings(:h)) %> Also supports integer values. |
98
98
  # | `hide` | Symbol | Hide the element at a specific breakpoint. <%= one_of(Primer::Classify::Utilities.mappings(:hide)) %> |
99
99
  # | `visibility` | Symbol | Visibility. <%= one_of(Primer::Classify::Utilities.mappings(:visibility)) %> |
100
100
  # | `vertical_align` | Symbol | <%= one_of(Primer::Classify::Utilities.mappings(:vertical_align)) %> |
@@ -29,7 +29,7 @@ module Primer
29
29
  # @param type [Symbol] <%= one_of(Primer::Beta::AutoComplete::Input::TYPE_OPTIONS) %>
30
30
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
31
31
  renders_one :input, lambda { |**system_arguments|
32
- aria_label = system_arguments[:"aria-label"] || system_arguments.dig(:aria, :label) || @aria_label
32
+ aria_label = aria("label", system_arguments) || @aria_label
33
33
  if aria_label.present?
34
34
  system_arguments[:"aria-label"] = aria_label
35
35
  system_arguments[:aria]&.delete(:label)
@@ -106,7 +106,7 @@ module Primer
106
106
  def initialize(src:, list_id:, input_id:, **system_arguments)
107
107
  @list_id = list_id
108
108
  @input_id = input_id
109
- @aria_label = system_arguments[:"aria-label"] || system_arguments.dig(:aria, :label)
109
+ @aria_label = aria("label", system_arguments)
110
110
 
111
111
  system_arguments.delete(:"aria-label") && system_arguments[:aria]&.delete(:label)
112
112
 
@@ -8,7 +8,7 @@ module Primer
8
8
  # for organizations or any other non-human avatars.
9
9
  # - By default, `Avatar` will render a static `<img>`. To have `Avatar` function as a link, set the `href` which will wrap the `<img>` in a `<a>`.
10
10
  # - Set `size` to update the height and width of the `Avatar` in pixels.
11
- # - To stack multiple avatars together, use <%= link_to_component(Primer::AvatarStackComponent) %>.
11
+ # - To stack multiple avatars together, use <%= link_to_component(Primer::Beta::AvatarStack) %>.
12
12
  #
13
13
  # @accessibility
14
14
  # Images should have text alternatives that describe the information or function represented.
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Beta
5
+ # Use `AvatarStack` to stack multiple avatars together.
6
+ class AvatarStack < Primer::Component
7
+ status :beta
8
+
9
+ ALIGN_DEFAULT = :left
10
+ ALIGN_OPTIONS = [ALIGN_DEFAULT, :right].freeze
11
+
12
+ DEFAULT_TAG = :div
13
+ TAG_OPTIONS = [DEFAULT_TAG, :span].freeze
14
+
15
+ DEFAULT_BODY_TAG = :div
16
+ BODY_TAG_OPTIONS = [DEFAULT_BODY_TAG, :span].freeze
17
+ # Required list of stacked avatars.
18
+ #
19
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::Beta::Avatar) %>.
20
+ renders_many :avatars, "Primer::Beta::Avatar"
21
+
22
+ # @example Default
23
+ # <%= render(Primer::Beta::AvatarStack.new) do |c| %>
24
+ # <%= c.avatar(src: "http://placekitten.com/200/200", alt: "@kittenuser") %>
25
+ # <%= c.avatar(src: "http://placekitten.com/200/200", alt: "@kittenuser") %>
26
+ # <%= c.avatar(src: "http://placekitten.com/200/200", alt: "@kittenuser") %>
27
+ # <% end %>
28
+ #
29
+ # @example Align right
30
+ # <%= render(Primer::Beta::AvatarStack.new(align: :right)) do |c| %>
31
+ # <%= c.avatar(src: "http://placekitten.com/200/200", alt: "@kittenuser") %>
32
+ # <%= c.avatar(src: "http://placekitten.com/200/200", alt: "@kittenuser") %>
33
+ # <%= c.avatar(src: "http://placekitten.com/200/200", alt: "@kittenuser") %>
34
+ # <% end %>
35
+ #
36
+ # @example With tooltip
37
+ # <%= render(Primer::Beta::AvatarStack.new(tooltipped: true, body_arguments: { label: 'This is a tooltip!' })) do |c| %>
38
+ # <%= c.avatar(src: "http://placekitten.com/200/200", alt: "@kittenuser") %>
39
+ # <%= c.avatar(src: "http://placekitten.com/200/200", alt: "@kittenuser") %>
40
+ # <%= c.avatar(src: "http://placekitten.com/200/200", alt: "@kittenuser") %>
41
+ # <% end %>
42
+ #
43
+ # @param tag [Symbol] <%= one_of(Primer::Beta::AvatarStack::TAG_OPTIONS) %>
44
+ # @param align [Symbol] <%= one_of(Primer::Beta::AvatarStack::ALIGN_OPTIONS) %>
45
+ # @param tooltipped [Boolean] Whether to add a tooltip to the stack or not.
46
+ # @param body_arguments [Hash] Parameters to add to the Body. If `tooltipped` is set, has the same arguments as <%= link_to_component(Primer::Tooltip) %>.
47
+ # The default tag is <%= pretty_value(Primer::Beta::AvatarStack::DEFAULT_BODY_TAG) %> but can be changed using `tag:`
48
+ # to <%= one_of(Primer::Beta::AvatarStack::BODY_TAG_OPTIONS, lower: true) %>
49
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
50
+ def initialize(tag: DEFAULT_TAG, align: ALIGN_DEFAULT, tooltipped: false, body_arguments: {}, **system_arguments)
51
+ @align = fetch_or_fallback(ALIGN_OPTIONS, align, ALIGN_DEFAULT)
52
+ @system_arguments = system_arguments
53
+ @tooltipped = tooltipped
54
+ @body_arguments = body_arguments
55
+
56
+ body_tag = @body_arguments[:tag] || DEFAULT_BODY_TAG
57
+ @body_arguments[:tag] = fetch_or_fallback(BODY_TAG_OPTIONS, body_tag, DEFAULT_BODY_TAG)
58
+ @body_arguments[:classes] = class_names(
59
+ "AvatarStack-body",
60
+ @body_arguments[:classes]
61
+ )
62
+
63
+ @system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
64
+ @system_arguments[:classes] = class_names(
65
+ "AvatarStack",
66
+ system_arguments[:classes],
67
+ "AvatarStack--right" => @align == :right
68
+ )
69
+ end
70
+
71
+ def body_component
72
+ if @tooltipped
73
+ Primer::Tooltip.new(**@body_arguments)
74
+ else
75
+ Primer::BaseComponent.new(**@body_arguments)
76
+ end
77
+ end
78
+
79
+ def before_render
80
+ @system_arguments[:classes] = class_names(
81
+ @system_arguments[:classes],
82
+ "AvatarStack--two" => avatars.size == 2,
83
+ "AvatarStack--three-plus" => avatars.size > 2
84
+ )
85
+ end
86
+
87
+ def render?
88
+ avatars.any?
89
+ end
90
+ end
91
+ end
92
+ end
@@ -2,7 +2,7 @@
2
2
  <% if content.present? %>
3
3
  <%= content %>
4
4
  <% else %>
5
- <%= render Primer::OcticonComponent.new("clippy") %>
6
- <%= render Primer::OcticonComponent.new("check", color: :icon_success, style: "display: none;") %>
5
+ <%= render Primer::OcticonComponent.new(:paste) %>
6
+ <%= render Primer::OcticonComponent.new(:check, color: :icon_success, style: "display: none;") %>
7
7
  <% end %>
8
8
  <% end %>
@@ -29,8 +29,12 @@ module Primer
29
29
  ActiveSupport::Deprecation.warn(message)
30
30
  end
31
31
 
32
+ def aria(val, system_arguments)
33
+ system_arguments[:"aria-#{val}"] || system_arguments.dig(:aria, val.to_sym)
34
+ end
35
+
32
36
  def validate_aria_label
33
- aria_label = @system_arguments[:"aria-label"] || @system_arguments.dig(:aria, :label)
37
+ aria_label = aria("label", @system_arguments)
34
38
  raise ArgumentError, "`aria-label` is required." if aria_label.nil? && !Rails.env.production?
35
39
  end
36
40
 
@@ -5,8 +5,8 @@
5
5
  <%= render(Primer::SpinnerComponent.new(size: :large, flex: 1, "data-loading-slot": true)) %>
6
6
  <% end %>
7
7
 
8
- <input type="hidden" data-image-crop-input="x" name="cropped_x">
9
- <input type="hidden" data-image-crop-input="y" name="cropped_y">
10
- <input type="hidden" data-image-crop-input="width" name="cropped_width">
11
- <input type="hidden" data-image-crop-input="height" name="cropped_height">
8
+ <input autocomplete="off" type="hidden" data-image-crop-input="x" name="cropped_x">
9
+ <input autocomplete="off" type="hidden" data-image-crop-input="y" name="cropped_y">
10
+ <input autocomplete="off" type="hidden" data-image-crop-input="width" name="cropped_width">
11
+ <input autocomplete="off" type="hidden" data-image-crop-input="height" name="cropped_height">
12
12
  <% end %>
@@ -2,6 +2,9 @@
2
2
 
3
3
  module Primer
4
4
  # Use `Label` to add contextual metadata to a design.
5
+ #
6
+ # @accessibility
7
+ # Use `aria-label` if the `Label` or the context around it don't explain the label.
5
8
  class LabelComponent < Primer::Component
6
9
  status :beta
7
10
 
@@ -29,27 +32,25 @@ module Primer
29
32
  VARIANT_OPTIONS = VARIANT_MAPPINGS.keys << nil
30
33
 
31
34
  # @example Schemes
32
- # <%= render(Primer::LabelComponent.new(title: "Label: Label")) { "Default" } %>
33
- # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :primary)) { "Primary" } %>
34
- # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :secondary)) { "Secondary" } %>
35
- # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :info)) { "Info" } %>
36
- # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :success)) { "Success" } %>
37
- # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :warning)) { "Warning" } %>
38
- # <%= render(Primer::LabelComponent.new(title: "Label: Label", scheme: :danger)) { "Danger" } %>
35
+ # <%= render(Primer::LabelComponent.new) { "Default" } %>
36
+ # <%= render(Primer::LabelComponent.new( scheme: :primary)) { "Primary" } %>
37
+ # <%= render(Primer::LabelComponent.new( scheme: :secondary)) { "Secondary" } %>
38
+ # <%= render(Primer::LabelComponent.new( scheme: :info)) { "Info" } %>
39
+ # <%= render(Primer::LabelComponent.new( scheme: :success)) { "Success" } %>
40
+ # <%= render(Primer::LabelComponent.new( scheme: :warning)) { "Warning" } %>
41
+ # <%= render(Primer::LabelComponent.new( scheme: :danger)) { "Danger" } %>
39
42
  #
40
43
  # @example Variants
41
- # <%= render(Primer::LabelComponent.new(title: "Label: Label")) { "Default" } %>
42
- # <%= render(Primer::LabelComponent.new(title: "Label: Label", variant: :large)) { "Large" } %>
44
+ # <%= render(Primer::LabelComponent.new) { "Default" } %>
45
+ # <%= render(Primer::LabelComponent.new( variant: :large)) { "Large" } %>
43
46
  #
44
47
  # @param tag [Symbol] <%= one_of(Primer::LabelComponent::TAG_OPTIONS) %>
45
- # @param title [String] `title` attribute for the component element.
46
48
  # @param scheme [Symbol] <%= one_of(Primer::LabelComponent::SCHEME_MAPPINGS.keys) %>
47
49
  # @param variant [Symbol] <%= one_of(Primer::LabelComponent::VARIANT_OPTIONS) %>
48
50
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
49
- def initialize(tag: DEFAULT_TAG, title:, scheme: nil, variant: nil, **system_arguments)
51
+ def initialize(tag: DEFAULT_TAG, scheme: nil, variant: nil, **system_arguments)
50
52
  @system_arguments = system_arguments
51
53
  @system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
52
- @system_arguments[:title] = title
53
54
  @system_arguments[:classes] = class_names(
54
55
  "Label",
55
56
  system_arguments[:classes],
@@ -18,10 +18,23 @@ module Primer
18
18
  #
19
19
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
20
20
  renders_one :panel, lambda { |**system_arguments|
21
+ return unless @with_panel
22
+
23
+ system_arguments[:id] = @panel_id
21
24
  system_arguments[:tag] = :div
22
25
  system_arguments[:role] ||= :tabpanel
26
+ system_arguments[:tabindex] = 0
23
27
  system_arguments[:hidden] = true unless @selected
24
28
 
29
+ label_present = aria("label", system_arguments) || aria("labelledby", system_arguments)
30
+ unless label_present
31
+ if @id.present?
32
+ system_arguments[:"aria-labelledby"] = @id
33
+ elsif !Rails.env.production?
34
+ raise ArgumentError, "Panels must be labelled. Either set a unique `id` on the tab, or set an `aria-label` directly on the panel"
35
+ end
36
+ end
37
+
25
38
  Primer::BaseComponent.new(**system_arguments)
26
39
  }
27
40
 
@@ -86,20 +99,24 @@ module Primer
86
99
  # @param list [Boolean] Whether the Tab is an item in a `<ul>` list.
87
100
  # @param selected [Boolean] Whether the Tab is selected or not.
88
101
  # @param with_panel [Boolean] Whether the Tab has an associated panel.
102
+ # @param panel_id [String] Only applies if `with_panel` is `true`. Unique id of panel.
89
103
  # @param icon_classes [Boolean] Classes that must always be applied to icons.
90
104
  # @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.
91
105
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
92
- def initialize(list: false, selected: false, with_panel: false, icon_classes: "", wrapper_arguments: {}, **system_arguments)
106
+ def initialize(list: false, selected: false, with_panel: false, panel_id: "", icon_classes: "", wrapper_arguments: {}, **system_arguments)
93
107
  @selected = selected
94
108
  @icon_classes = icon_classes
95
109
  @list = list
110
+ @with_panel = with_panel
96
111
 
97
112
  @system_arguments = system_arguments
113
+ @id = @system_arguments[:id]
98
114
 
99
115
  if with_panel || @system_arguments[:tag] == :button
100
116
  @system_arguments[:tag] = :button
101
117
  @system_arguments[:type] = :button
102
118
  @system_arguments[:role] = :tab
119
+ panel_id(panel_id)
103
120
  else
104
121
  @system_arguments[:tag] = :a
105
122
  end
@@ -111,7 +128,7 @@ module Primer
111
128
  return unless @selected
112
129
 
113
130
  if @system_arguments[:tag] == :a
114
- aria_current = @system_arguments[:"aria-current"] || @system_arguments.dig(:aria, :current) || DEFAULT_ARIA_CURRENT_FOR_ANCHOR
131
+ aria_current = aria("current", system_arguments) || DEFAULT_ARIA_CURRENT_FOR_ANCHOR
115
132
  @system_arguments[:"aria-current"] = fetch_or_fallback(ARIA_CURRENT_OPTIONS_FOR_ANCHOR, aria_current, DEFAULT_ARIA_CURRENT_FOR_ANCHOR)
116
133
  else
117
134
  @system_arguments[:"aria-selected"] = true
@@ -128,6 +145,17 @@ module Primer
128
145
  yield
129
146
  end
130
147
  end
148
+
149
+ private
150
+
151
+ def panel_id(panel_id)
152
+ if panel_id.blank?
153
+ raise ArgumentError, "`panel_id` is required" unless Rails.env.production?
154
+ else
155
+ @panel_id = panel_id
156
+ @system_arguments[:"aria-controls"] = @panel_id
157
+ end
158
+ end
131
159
  end
132
160
  end
133
161
  end
@@ -13,6 +13,7 @@ module Primer
13
13
  # Tabs to be rendered. When `with_panel` is set on the parent, a button is rendered for panel navigation. Otherwise,
14
14
  # an anchor tag is rendered for page navigation. For more information, refer to <%= link_to_component(Primer::Navigation::TabComponent) %>.
15
15
  #
16
+ # @param panel_id [String] Only applies if `with_panel` is `true`. Unique ID of panel.
16
17
  # @param selected [Boolean] Whether the tab is selected.
17
18
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
18
19
  renders_many :tabs, lambda { |selected: false, **system_arguments|
@@ -63,19 +64,19 @@ module Primer
63
64
  #
64
65
  # @example With panels
65
66
  # <%= render(Primer::TabNavComponent.new(label: "With panels", with_panel: true)) do |c| %>
66
- # <% c.tab(selected: true) do |t| %>
67
+ # <% c.tab(selected: true, panel_id: "panel-1", id: "tab-1") do |t| %>
67
68
  # <% t.text { "Tab 1" } %>
68
69
  # <% t.panel do %>
69
70
  # Panel 1
70
71
  # <% end %>
71
72
  # <% end %>
72
- # <% c.tab do |t| %>
73
+ # <% c.tab(id: "tab-2", panel_id: "panel-2") do |t| %>
73
74
  # <% t.text { "Tab 2" } %>
74
75
  # <% t.panel do %>
75
76
  # Panel 2
76
77
  # <% end %>
77
78
  # <% end %>
78
- # <% c.tab do |t| %>
79
+ # <% c.tab(id: "tab-3", panel_id: "panel-3") do |t| %>
79
80
  # <% t.text { "Tab 3" } %>
80
81
  # <% t.panel do %>
81
82
  # Panel 3