primer_view_components 0.0.44 → 0.0.48

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +187 -0
  3. data/app/components/primer/avatar_stack_component.rb +9 -3
  4. data/app/components/primer/base_component.rb +52 -23
  5. data/app/components/primer/beta/auto_complete.rb +159 -0
  6. data/app/components/primer/beta/auto_complete/auto_complete.d.ts +1 -0
  7. data/app/components/primer/{auto_complete → beta/auto_complete}/auto_complete.html.erb +0 -0
  8. data/app/components/primer/beta/auto_complete/auto_complete.js +1 -0
  9. data/app/components/primer/{auto_complete → beta/auto_complete}/auto_complete.ts +0 -0
  10. data/app/components/primer/beta/auto_complete/item.rb +44 -0
  11. data/app/components/primer/beta/avatar.rb +77 -0
  12. data/app/components/primer/border_box_component.rb +3 -0
  13. data/app/components/primer/clipboard_copy.rb +25 -7
  14. data/app/components/primer/component.rb +9 -1
  15. data/app/components/primer/details_component.rb +12 -8
  16. data/app/components/primer/image_crop.rb +1 -1
  17. data/app/components/primer/markdown.rb +9 -9
  18. data/app/components/primer/menu_component.rb +7 -3
  19. data/app/components/primer/navigation/tab_component.rb +19 -5
  20. data/app/components/primer/popover_component.rb +6 -3
  21. data/app/components/primer/primer.d.ts +1 -1
  22. data/app/components/primer/primer.js +1 -1
  23. data/app/components/primer/primer.ts +1 -1
  24. data/app/components/primer/tab_nav_component.rb +8 -6
  25. data/app/components/primer/timeline_item_component.rb +2 -2
  26. data/app/components/primer/tooltip.rb +1 -1
  27. data/app/components/primer/truncate.rb +5 -0
  28. data/app/components/primer/underline_nav_component.rb +12 -6
  29. data/{app/lib → lib}/primer/classify.rb +16 -33
  30. data/{app/lib → lib}/primer/classify/cache.rb +6 -40
  31. data/{app/lib → lib}/primer/classify/flex.rb +0 -0
  32. data/{app/lib → lib}/primer/classify/functional_background_colors.rb +2 -0
  33. data/{app/lib → lib}/primer/classify/functional_border_colors.rb +2 -0
  34. data/{app/lib → lib}/primer/classify/functional_colors.rb +0 -0
  35. data/{app/lib → lib}/primer/classify/functional_text_colors.rb +2 -0
  36. data/{app/lib → lib}/primer/classify/grid.rb +0 -0
  37. data/lib/primer/classify/utilities.rb +148 -0
  38. data/lib/primer/classify/utilities.yml +1271 -0
  39. data/lib/primer/view_components.rb +1 -0
  40. data/lib/primer/view_components/linters/argument_mappers/system_arguments.rb +5 -4
  41. data/lib/primer/view_components/linters/button_component_migration_counter.rb +9 -5
  42. data/lib/primer/view_components/linters/helpers.rb +132 -17
  43. data/lib/primer/view_components/statuses.rb +14 -0
  44. data/lib/primer/view_components/version.rb +1 -1
  45. data/lib/rubocop/config/default.yml +12 -0
  46. data/lib/rubocop/cop/primer.rb +4 -0
  47. data/lib/rubocop/cop/primer/no_tag_memoize.rb +42 -0
  48. data/lib/rubocop/cop/primer/system_argument_instead_of_class.rb +75 -0
  49. data/lib/tasks/docs.rake +72 -18
  50. data/lib/tasks/utilities.rake +105 -0
  51. data/lib/yard/docs_helper.rb +1 -1
  52. data/static/statuses.json +4 -4
  53. metadata +30 -21
  54. data/app/components/primer/auto_complete.rb +0 -156
  55. data/app/components/primer/auto_complete/item.rb +0 -42
  56. data/app/components/primer/avatar_component.rb +0 -75
  57. data/app/lib/primer/classify/spacing.rb +0 -63
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8134ca7a0a270e780b58ce74eabd681c4c2d20ace4b45dd0a5a85eff6a21635d
4
- data.tar.gz: 8a8bc46d3f161e4398d129881b2ef6a0f1fe8f4a513b6cf82bd8b1c466f36d2d
3
+ metadata.gz: 2c751bc60362c19e97ba5fbe22227e19186efa799862932a6f7b8541ad8e1eaa
4
+ data.tar.gz: 4efd584159e4883f8bcc72954fff356c753b0b8980056ee0c58b7a3bd650cb90
5
5
  SHA512:
6
- metadata.gz: '0557945a7c1e8ce40e117c65c283ecc7b0d2e1480d130586dbc5bc807d4ac084ec776928ac400eaa6c7a2391edb9b22e21c97071b9a499cb7221af78f1c4dbf4'
7
- data.tar.gz: 31f974e2c2dc37056d9f67cb8036092521469d1d8e36c694dd02e664df50d7bffc12e6c3308853a89ad6b8a9794cc911196656fd36f5b3e6b4b389ee5fcd6a1e
6
+ metadata.gz: c6169f90241d046f9cee4faf1fb971cac34360c8ebb116e2c2e39fdd0aeff5e8cca17e899bdd8a97f94ebefa4c3705d6929b0978a96f0264dcab3fa96342faaf
7
+ data.tar.gz: 8576857a521647f43ccb3fe413b0e24b5d65a8c06e9036dd3035b37d8fb5989aa43a7ac2d33863f5faa022c099c6a8386d30de8b764e19cf93b9dcb207fd4eef
data/CHANGELOG.md CHANGED
@@ -1,7 +1,190 @@
1
1
  # CHANGELOG
2
2
 
3
+ <!--
4
+ Authoring changelog entries
5
+
6
+ This file holds all the changes made in previous versions of Primer View Components and the ones coming to the next version.
7
+ To add yours, you need to find in which category to write it under the `main` section. `Main` is the first section on top of the document.
8
+ There are six categories currently in use, `New`, `Updates`, `Bug fixes`, `Breaking changes`, `Deprecations` and `Misc`.
9
+
10
+ - New
11
+ Category for new components, system behaviours, options and arguments changes
12
+
13
+ - Updates
14
+ Every non-breaking change to the source code go there.
15
+
16
+ - Bug Fixes
17
+ Non-breaking bug fixes to existing code.
18
+
19
+ - Breaking Changes
20
+ The category for changes creating incompatibilities to code written with previous versions.
21
+ It includes any changes to components name, signature and behaviour. Also, include removing tags options or changing file location.
22
+ If you are not sure you made breaking changes, ask us in your pull request.
23
+
24
+ - Deprecations
25
+ For changes that explicitly deprecate part of the code base.
26
+
27
+ - Misc
28
+ The category for changes related to documentation, testing and tooling. Also, for pull requests that can't fit in other sections.
29
+ -->
30
+
3
31
  ## main
4
32
 
33
+ ## 0.0.48
34
+
35
+ ### Breaking changes
36
+
37
+ * Ensure panels in `Navigation::Tab` have a label.
38
+
39
+ *Kate Higa*
40
+
41
+ ### Misc
42
+
43
+ * Expose custom cops and default config for erblint.
44
+
45
+ *Manuel Puyol*
46
+
47
+ * Fix double constant assign.
48
+
49
+ *Manuel Puyol*
50
+
51
+ ## 0.0.47
52
+
53
+ ### Breaking changes
54
+
55
+ * Restrict tag for `Popover` to `:div` and `Popover` heading slot to headings.
56
+
57
+ *Kate Higa*
58
+
59
+ * Renames:
60
+ * `Primer::AutoComplete` to `Primer::Beta::AutoComplete`
61
+ * `Primer::AutoComplete::Item` to `Primer::Beta::AutoComplete::Item`
62
+ * `Primer::AvatarComponent` to `Primer::Beta::Avatar`
63
+
64
+ *Manuel Puyol*
65
+
66
+ ### Misc
67
+
68
+ * Update `doc_examples_axe_test` to exclude non-standalone components and fix `Markdown` example.
69
+
70
+ *Kate Higa*
71
+
72
+ * Update `DetailsComponent` examples.
73
+
74
+ *Manuel Puyol*
75
+
76
+ * Add linter to suggest system arguments instead of classes.
77
+
78
+ *Manuel Puyol*
79
+
80
+ * Update component generator to create components in the right status module.
81
+
82
+ *Manuel Puyol*
83
+
84
+ * Add example for truncating HTML to `Truncate`.
85
+
86
+ *Joel Hawksley*
87
+
88
+ * Update docs generation to point to the correct file sources.
89
+
90
+ *Manuel Puyol*
91
+
92
+ * Add ENV flag to dump linter data into a file.
93
+
94
+ *Manuel Puyol*
95
+
96
+ ## 0.0.46
97
+
98
+ ### Updates
99
+
100
+ * Default to matching `name` and `id` of `input`.
101
+
102
+ *Kate Higa*
103
+
104
+ * Restrict usage of padding system arguments on BorderBox, recommending use of `padding` density instead.
105
+
106
+ *Joel Hawksley*
107
+
108
+ ### Breaking changes
109
+
110
+ * Restrict `TabNav`and `Tab` tags.
111
+
112
+ *Kate Higa*
113
+
114
+ * Restrict `AvatarStack` body slot tag and `ImageCrop` spinner tag.
115
+
116
+ *Kate Higa*
117
+
118
+ * Restrict `Details` body slot tags and `UnderlineNav` body slot tags.
119
+
120
+ *Kate Higa*
121
+
122
+ * Move Primer::Classify from `app/lib/` to `lib/`. This requires an extra `require "primer/classify"` statement for anywhere Classify is needed.
123
+
124
+ *Manuel Puyol, Jon Rohan*
125
+
126
+ * Restrict `Menu` heading slot tags to heading tags and require `tag` argument.
127
+
128
+ *Kate Higa*
129
+
130
+ * Adding animation, vertical_align, word_break, display, visibility, & position arguments to the utilities class. `animation: :grow` is now `animation: :hover_grow` this was a change because we changed the class name in primer.
131
+
132
+ *Jon Rohan*
133
+
134
+ ### Misc
135
+
136
+ * Update contributing guidelines with release instructions.
137
+
138
+ *Kate Higa*
139
+
140
+ * Prevent flexible tag syntax with rubocop rule.
141
+
142
+ *Kate Higa*
143
+
144
+ * Update linter autocorrection to use `""` instead of `true` for boolean attributes.
145
+
146
+ *Manuel Puyol*
147
+
148
+ * Update Storybook version.
149
+
150
+ *Manuel Puyol*
151
+
152
+ * Added a changelog authoring guide to `CHANGELOG.md`.
153
+
154
+ *Amélia Chavot*
155
+
156
+ ## 0.0.45
157
+
158
+ ### Updates
159
+
160
+ * Allow copying from elements using `for` in `ClipboardCopy`.
161
+
162
+ *Manuel Puyol*
163
+
164
+ ### Breaking changes
165
+
166
+ * Remove `label` argument in favor of `aria-label` in `ClipboardCopy`.
167
+
168
+ *Manuel Puyol*
169
+
170
+ ### Misc
171
+
172
+ * Add autocorrect for button linters.
173
+
174
+ *Manuel Puyol*
175
+
176
+ * Unify contributing guidelines.
177
+
178
+ *Kate Higa*
179
+
180
+ * Rerun flaky system tests.
181
+
182
+ *Manuel Puyol*
183
+
184
+ * Check if selector is a classify class in Utilities.
185
+
186
+ *Jon Rohan*
187
+
5
188
  ## 0.0.44
6
189
 
7
190
  ### Updates
@@ -30,6 +213,10 @@
30
213
 
31
214
  ### Misc
32
215
 
216
+ * Replace Classify::Spacing class with pre-generated mappings.
217
+
218
+ *Jon Rohan*
219
+
33
220
  * Add linter suggestions for `Button` component.
34
221
 
35
222
  *Manuel Puyol*
@@ -10,10 +10,13 @@ module Primer
10
10
 
11
11
  DEFAULT_TAG = :div
12
12
  TAG_OPTIONS = [DEFAULT_TAG, :span].freeze
13
+
14
+ DEFAULT_BODY_TAG = :div
15
+ BODY_TAG_OPTIONS = [DEFAULT_BODY_TAG, :span].freeze
13
16
  # Required list of stacked avatars.
14
17
  #
15
- # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::AvatarComponent) %>.
16
- renders_many :avatars, Primer::AvatarComponent
18
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::Beta::Avatar) %>.
19
+ renders_many :avatars, "Primer::Beta::Avatar"
17
20
 
18
21
  # @example Default
19
22
  # <%= render(Primer::AvatarStackComponent.new) do |c| %>
@@ -40,6 +43,8 @@ module Primer
40
43
  # @param align [Symbol] <%= one_of(Primer::AvatarStackComponent::ALIGN_OPTIONS) %>
41
44
  # @param tooltipped [Boolean] Whether to add a tooltip to the stack or not.
42
45
  # @param body_arguments [Hash] Parameters to add to the Body. If `tooltipped` is set, has the same arguments as <%= link_to_component(Primer::Tooltip) %>.
46
+ # The default tag is <%= pretty_value(Primer::AvatarStackComponent::DEFAULT_BODY_TAG) %> but can be changed using `tag:`
47
+ # to <%= one_of(Primer::AvatarStackComponent::BODY_TAG_OPTIONS, lower: true) %>
43
48
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
44
49
  def initialize(tag: DEFAULT_TAG, align: ALIGN_DEFAULT, tooltipped: false, body_arguments: {}, **system_arguments)
45
50
  @align = fetch_or_fallback(ALIGN_OPTIONS, align, ALIGN_DEFAULT)
@@ -47,7 +52,8 @@ module Primer
47
52
  @tooltipped = tooltipped
48
53
  @body_arguments = body_arguments
49
54
 
50
- @body_arguments[:tag] ||= :div
55
+ body_tag = @body_arguments[:tag] || DEFAULT_BODY_TAG
56
+ @body_arguments[:tag] = fetch_or_fallback(BODY_TAG_OPTIONS, body_tag, DEFAULT_BODY_TAG)
51
57
  @body_arguments[:classes] = class_names(
52
58
  "AvatarStack-body",
53
59
  @body_arguments[:classes]
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "primer/classify"
4
+
3
5
  module Primer
4
6
  # All Primer ViewComponents accept a standard set of options called system arguments, mimicking the [styled-system API](https://styled-system.com/table) used by [Primer React](https://primer.style/components/system-props).
5
7
  #
@@ -43,7 +45,7 @@ module Primer
43
45
  #
44
46
  # | Name | Type | Description |
45
47
  # | :- | :- | :- |
46
- # | `animation` | Symbol | <%= one_of([:fade_in, :fade_out, :fade_up, :fade_down, :scale_in, :pulse, :grow_x, :grow]) %> |
48
+ # | `animation` | Symbol | <%= one_of(Primer::Classify::Utilities.mappings(:animation)) %> |
47
49
  #
48
50
  # ## Border
49
51
  #
@@ -61,7 +63,7 @@ module Primer
61
63
  #
62
64
  # | Name | Type | Description |
63
65
  # | :- | :- | :- |
64
- # | `bg` | String, Symbol | Background color. Accepts either a hex value as a String or <%= one_of(Primer::Classify::FunctionalBorderColors::OPTIONS, lower: true) %> |
66
+ # | `bg` | String, Symbol | Background color. Accepts either a hex value as a String or <%= one_of(Primer::Classify::FunctionalBackgroundColors::OPTIONS, lower: true) %> |
65
67
  # | `border_color` | Symbol | Border color. <%= one_of(Primer::Classify::FunctionalBorderColors::OPTIONS) %> |
66
68
  # | `color` | Symbol | Text color. <%= one_of(Primer::Classify::FunctionalTextColors::OPTIONS) %> |
67
69
  #
@@ -91,20 +93,20 @@ module Primer
91
93
  #
92
94
  # | Name | Type | Description |
93
95
  # | :- | :- | :- |
94
- # | `display` | Symbol | <%= one_of([:none, :block, :flex, :inline, :inline_block, :inline_flex, :table, :table_cell]) %> |
96
+ # | `display` | Symbol | <%= one_of(Primer::Classify::Utilities.mappings(:display)) %> |
95
97
  # | `height` | Symbol | <%= one_of([:fit]) %> |
96
- # | `hide` | Symbol | Hide the element at a specific breakpoint. <%= one_of([:sm, :md, :lg, :xl]) %> |
97
- # | `v` | Symbol | Visibility. <%= one_of([:hidden, :visible]) %> |
98
- # | `vertical_align` | Symbol | <%= one_of([:baseline, :top, :middle, :bottom, :text_top, :text_bottom]) %> |
98
+ # | `hide` | Symbol | Hide the element at a specific breakpoint. <%= one_of(Primer::Classify::Utilities.mappings(:hide)) %> |
99
+ # | `visibility` | Symbol | Visibility. <%= one_of(Primer::Classify::Utilities.mappings(:visibility)) %> |
100
+ # | `vertical_align` | Symbol | <%= one_of(Primer::Classify::Utilities.mappings(:vertical_align)) %> |
99
101
  #
100
102
  # ## Position
101
103
  #
102
104
  # | Name | Type | Description |
103
105
  # | :- | :- | :- |
104
106
  # | `bottom` | Boolean | If `false`, sets `bottom: 0`. |
105
- # | `float` | Symbol | <%= one_of([:left, :right]) %> |
107
+ # | `float` | Symbol | <%= one_of(Primer::Classify::Utilities.mappings(:float)) %> |
106
108
  # | `left` | Boolean | If `false`, sets `left: 0`. |
107
- # | `position` | Symbol | <%= one_of([:relative, :absolute, :fixed]) %> |
109
+ # | `position` | Symbol | <%= one_of(Primer::Classify::Utilities.mappings(:position)) %> |
108
110
  # | `right` | Boolean | If `false`, sets `right: 0`. |
109
111
  # | `top` | Boolean | If `false`, sets `top: 0`. |
110
112
  #
@@ -112,20 +114,20 @@ module Primer
112
114
  #
113
115
  # | Name | Type | Description |
114
116
  # | :- | :- | :- |
115
- # | `m` | Integer | Margin. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:m]) %> |
116
- # | `mb` | Integer | Margin bottom. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:mb]) %> |
117
- # | `ml` | Integer | Margin left. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:ml]) %> |
118
- # | `mr` | Integer | Margin right. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:mr]) %> |
119
- # | `mt` | Integer | Margin top. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:mt]) %> |
120
- # | `mx` | Integer | Horizontal margins. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:mx]) %> |
121
- # | `my` | Integer | Vertical margins. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:my]) %> |
122
- # | `p` | Integer | Padding. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:p]) %> |
123
- # | `pb` | Integer | Padding bottom. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:pb]) %> |
124
- # | `pl` | Integer | Padding left. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:pl]) %> |
125
- # | `pr` | Integer | Padding right. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:pr]) %> |
126
- # | `pt` | Integer | Padding left. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:pt]) %> |
127
- # | `px` | Integer | Horizontal padding. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:px]) %> |
128
- # | `py` | Integer | Vertical padding. <%= one_of(Primer::Classify::Spacing::MAPPINGS[:py]) %> |
117
+ # | `m` | Integer | Margin. <%= one_of(Primer::Classify::Utilities.mappings(:m)) %> |
118
+ # | `mb` | Integer | Margin bottom. <%= one_of(Primer::Classify::Utilities.mappings(:mb)) %> |
119
+ # | `ml` | Integer | Margin left. <%= one_of(Primer::Classify::Utilities.mappings(:ml)) %> |
120
+ # | `mr` | Integer | Margin right. <%= one_of(Primer::Classify::Utilities.mappings(:mr)) %> |
121
+ # | `mt` | Integer | Margin top. <%= one_of(Primer::Classify::Utilities.mappings(:mt)) %> |
122
+ # | `mx` | Integer | Horizontal margins. <%= one_of(Primer::Classify::Utilities.mappings(:mx)) %> |
123
+ # | `my` | Integer | Vertical margins. <%= one_of(Primer::Classify::Utilities.mappings(:my)) %> |
124
+ # | `p` | Integer | Padding. <%= one_of(Primer::Classify::Utilities.mappings(:p)) %> |
125
+ # | `pb` | Integer | Padding bottom. <%= one_of(Primer::Classify::Utilities.mappings(:pb)) %> |
126
+ # | `pl` | Integer | Padding left. <%= one_of(Primer::Classify::Utilities.mappings(:pl)) %> |
127
+ # | `pr` | Integer | Padding right. <%= one_of(Primer::Classify::Utilities.mappings(:pr)) %> |
128
+ # | `pt` | Integer | Padding left. <%= one_of(Primer::Classify::Utilities.mappings(:pt)) %> |
129
+ # | `px` | Integer | Horizontal padding. <%= one_of(Primer::Classify::Utilities.mappings(:px)) %> |
130
+ # | `py` | Integer | Vertical padding. <%= one_of(Primer::Classify::Utilities.mappings(:py)) %> |
129
131
  #
130
132
  # ## Typography
131
133
  #
@@ -138,7 +140,7 @@ module Primer
138
140
  # | `text_align` | Symbol | Text alignment. <%= one_of([:left, :right, :center]) %> |
139
141
  # | `text_transform` | Symbol | Text alignment. <%= one_of([:uppercase]) %> |
140
142
  # | `underline` | Boolean | Whether text should be underlined. |
141
- # | `word_break` | Symbol | Whether to break words on line breaks. Can only be `:break_all`. |
143
+ # | `word_break` | Symbol | Whether to break words on line breaks. <%= one_of(Primer::Classify::Utilities.mappings(:word_break)) %> |
142
144
  #
143
145
  # ## Other
144
146
  #
@@ -152,6 +154,33 @@ module Primer
152
154
 
153
155
  raise ArgumentError, "`class` is an invalid argument. Use `classes` instead." if system_arguments.key?(:class) && !Rails.env.production?
154
156
 
157
+ if (denylist = system_arguments[:system_arguments_denylist])
158
+ if force_system_arguments? && !ENV["PRIMER_WARNINGS_DISABLED"]
159
+ # Convert denylist from:
160
+ # { [:p, :pt] => "message" } to:
161
+ # { p: "message", pt: "message" }
162
+ unpacked_denylist =
163
+ denylist.each_with_object({}) do |(keys, value), memo|
164
+ keys.each { |key| memo[key] = value }
165
+ end
166
+
167
+ violations = unpacked_denylist.keys & @system_arguments.keys
168
+
169
+ if violations.any?
170
+ message = "Found #{violations.count} #{'violation'.pluralize(violations)}:"
171
+ violations.each do |violation|
172
+ message += "\n The #{violation} system argument is not allowed here. #{unpacked_denylist[violation]}"
173
+ end
174
+
175
+ raise(ArgumentError, message)
176
+ end
177
+ end
178
+
179
+ # Remove :system_arguments_denylist key and any denied keys from system arguments
180
+ @system_arguments.except!(:system_arguments_denylist)
181
+ @system_arguments.except!(*denylist.keys.flatten)
182
+ end
183
+
155
184
  @result = Primer::Classify.call(**@system_arguments.merge(classes: classes))
156
185
 
157
186
  @system_arguments[:"data-view-component"] = true
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Beta
5
+ # Use `AutoComplete` to provide a user with a list of selectable suggestions that appear when they type into the
6
+ # input field. This list is populated by server search results.
7
+ # @accessibility
8
+ # Always set an accessible label to help the user interact with the component.
9
+ #
10
+ # * Set the `label` slot to render a visible label. Alternatively, associate an existing visible text element
11
+ # as a label by setting `aria-labelledby`.
12
+ # * If you must use a non-visible label, set `:"aria-label"` on `AutoComplete` and Primer
13
+ # will apply it to the correct elements. However, please note that a visible label should almost
14
+ # always be used unless there is compelling reason not to. A placeholder is not a label.
15
+ class AutoComplete < Primer::Component
16
+ status :beta
17
+
18
+ # Optionally render a visible label. See <%= link_to_accessibility %>
19
+ #
20
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
21
+ renders_one :label, lambda { |**system_arguments|
22
+ system_arguments[:for] = @input_id
23
+ system_arguments[:tag] = :label
24
+ Primer::BaseComponent.new(**system_arguments)
25
+ }
26
+
27
+ # Required input used to search for results
28
+ #
29
+ # @param type [Symbol] <%= one_of(Primer::Beta::AutoComplete::Input::TYPE_OPTIONS) %>
30
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
31
+ renders_one :input, lambda { |**system_arguments|
32
+ aria_label = aria("label", system_arguments) || @aria_label
33
+ if aria_label.present?
34
+ system_arguments[:"aria-label"] = aria_label
35
+ system_arguments[:aria]&.delete(:label)
36
+ end
37
+
38
+ name = system_arguments[:name] || @input_id
39
+ Input.new(id: @input_id, name: name, **system_arguments)
40
+ }
41
+
42
+ # Optional icon to be rendered before the input. Has the same arguments as <%= link_to_component(Primer::OcticonComponent) %>.
43
+ #
44
+ renders_one :icon, Primer::OcticonComponent
45
+
46
+ # Customizable results list.
47
+ #
48
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
49
+ renders_one :results, lambda { |**system_arguments|
50
+ system_arguments[:tag] = :ul
51
+ system_arguments[:id] = @list_id
52
+ system_arguments[:classes] = class_names(
53
+ "autocomplete-results",
54
+ system_arguments[:classes]
55
+ )
56
+
57
+ aria_label = system_arguments[:"aria-label"] || system_arguments.dig(:aria, :label) || @aria_label
58
+ system_arguments[:"aria-label"] = aria_label if aria_label.present?
59
+ system_arguments[:aria]&.delete(:label)
60
+
61
+ Primer::BaseComponent.new(**system_arguments)
62
+ }
63
+
64
+ # @example Default
65
+ # <%= render(Primer::Beta::AutoComplete.new(src: "/auto_complete", input_id: "fruits-input-1", list_id: "fruits-popup-1", position: :relative)) do |c| %>
66
+ # <% c.label(classes:"").with_content("Fruits") %>
67
+ # <% c.input(type: :text) %>
68
+ # <% end %>
69
+ #
70
+ # @example With `aria-label`
71
+ # <%= render(Primer::Beta::AutoComplete.new("aria-label": "Fruits", src: "/auto_complete", input_id: "fruits-input-2", list_id: "fruits-popup-2", position: :relative)) do |c| %>
72
+ # <% c.input(type: :text) %>
73
+ # <% end %>
74
+ #
75
+ # @example With `aria-labelledby`
76
+ # <%= render(Primer::HeadingComponent.new(tag: :h2, id: "search-1")) { "Search" } %>
77
+ # <%= render(Primer::Beta::AutoComplete.new(src: "/auto_complete", input_id: "fruits-input-3", list_id: "fruits-popup-2", position: :relative)) do |c| %>
78
+ # <% c.input("aria-labelledby": "search-1") %>
79
+ # <% end %>
80
+ #
81
+ # @example With custom classes for the results
82
+ # <%= render(Primer::Beta::AutoComplete.new(src: "/auto_complete", input_id: "fruits-input-4", list_id: "fruits-popup-3", position: :relative)) do |c| %>
83
+ # <% c.label(classes:"").with_content("Fruits") %>
84
+ # <% c.input(type: :text) %>
85
+ # <% c.results(classes: "custom-class") do %>
86
+ # <%= render(Primer::Beta::AutoComplete::Item.new(selected: true, value: "apple")) do |c| %>
87
+ # Apple
88
+ # <% end %>
89
+ # <%= render(Primer::Beta::AutoComplete::Item.new(value: "orange")) do |c| %>
90
+ # Orange
91
+ # <% end %>
92
+ # <% end %>
93
+ # <% end %>
94
+ #
95
+ # @example With Icon
96
+ # <%= render(Primer::Beta::AutoComplete.new(src: "/auto_complete", list_id: "fruits-popup-4", input_id: "fruits-input-4", position: :relative)) do |c| %>
97
+ # <% c.label(classes:"").with_content("Fruits") %>
98
+ # <% c.input(type: :text) %>
99
+ # <% c.icon(icon: :search) %>
100
+ # <% end %>
101
+ #
102
+ # @param src [String] The route to query.
103
+ # @param input_id [String] Id of the input element.
104
+ # @param list_id [String] Id of the list element.
105
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
106
+ def initialize(src:, list_id:, input_id:, **system_arguments)
107
+ @list_id = list_id
108
+ @input_id = input_id
109
+ @aria_label = aria("label", system_arguments)
110
+
111
+ system_arguments.delete(:"aria-label") && system_arguments[:aria]&.delete(:label)
112
+
113
+ @system_arguments = system_arguments
114
+ @system_arguments[:tag] = "auto-complete"
115
+ @system_arguments[:src] = src
116
+ @system_arguments[:for] = list_id
117
+ end
118
+
119
+ # add `results` without needing to explicitly call it in the view
120
+ def before_render
121
+ raise ArgumentError, "Missing `input` slot" if input.blank?
122
+ raise ArgumentError, "Accessible label is required." if label.blank? && input.missing_label?
123
+
124
+ results(classes: "") unless results
125
+ end
126
+
127
+ # This component is part of `Primer::Beta::AutoCompleteComponent` and should not be
128
+ # used as a standalone component.
129
+ class Input < Primer::Component
130
+ DEFAULT_TYPE = :text
131
+ TYPE_OPTIONS = [DEFAULT_TYPE, :search].freeze
132
+
133
+ # @param type [Symbol] <%= one_of(Primer::Beta::AutoComplete::Input::TYPE_OPTIONS) %>
134
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
135
+ def initialize(type: DEFAULT_TYPE, **system_arguments)
136
+ @system_arguments = system_arguments
137
+ @system_arguments[:tag] = :input
138
+
139
+ @aria_label = system_arguments[:"aria-label"]
140
+ @aria_labelledby = system_arguments[:"aria-labelledby"] || system_arguments.dig(:aria, :labelledby)
141
+
142
+ @system_arguments[:type] = fetch_or_fallback(TYPE_OPTIONS, type, DEFAULT_TYPE)
143
+ @system_arguments[:classes] = class_names(
144
+ "form-control",
145
+ system_arguments[:classes]
146
+ )
147
+ end
148
+
149
+ def missing_label?
150
+ @aria_label.blank? && @aria_labelledby.blank?
151
+ end
152
+
153
+ def call
154
+ render(Primer::BaseComponent.new(**@system_arguments))
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end