primer_view_components 0.0.46 → 0.0.47

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/app/components/primer/avatar_stack_component.rb +2 -2
  4. data/app/components/primer/beta/auto_complete.rb +159 -0
  5. data/app/components/primer/beta/auto_complete/auto_complete.d.ts +1 -0
  6. data/app/components/primer/{auto_complete → beta/auto_complete}/auto_complete.html.erb +0 -0
  7. data/app/components/primer/beta/auto_complete/auto_complete.js +1 -0
  8. data/app/components/primer/{auto_complete → beta/auto_complete}/auto_complete.ts +0 -0
  9. data/app/components/primer/beta/auto_complete/item.rb +44 -0
  10. data/app/components/primer/beta/avatar.rb +77 -0
  11. data/app/components/primer/details_component.rb +7 -7
  12. data/app/components/primer/markdown.rb +9 -9
  13. data/app/components/primer/popover_component.rb +6 -3
  14. data/app/components/primer/primer.d.ts +1 -1
  15. data/app/components/primer/primer.js +1 -1
  16. data/app/components/primer/primer.ts +1 -1
  17. data/app/components/primer/timeline_item_component.rb +2 -2
  18. data/app/components/primer/truncate.rb +5 -0
  19. data/app/components/primer/underline_nav_component.rb +1 -1
  20. data/lib/primer/classify.rb +1 -11
  21. data/lib/primer/classify/utilities.rb +22 -11
  22. data/lib/primer/view_components/linters/helpers.rb +17 -0
  23. data/lib/primer/view_components/statuses.rb +14 -0
  24. data/lib/primer/view_components/version.rb +1 -1
  25. data/lib/tasks/docs.rake +70 -16
  26. data/lib/yard/docs_helper.rb +1 -1
  27. data/static/statuses.json +4 -4
  28. metadata +10 -7
  29. data/app/components/primer/auto_complete.rb +0 -157
  30. data/app/components/primer/auto_complete/item.rb +0 -42
  31. data/app/components/primer/avatar_component.rb +0 -75
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19a15f3c293e45a99165f8fe3ceb6967626cabfa15e726cdbcb614223c6e39c5
4
- data.tar.gz: 6554eeda36440b40d83ee5b5cd395fd6af3cfc9e35c0ac971e14be31dae5d5bf
3
+ metadata.gz: 1b0ba62c9f5f11df09d24b40ca5e61e7d618046aba651ad733c8db06c482fe26
4
+ data.tar.gz: 34c0b8066b85d5392dd241baeb3b654ba47d7de1f360b793ac893750989d99dd
5
5
  SHA512:
6
- metadata.gz: d480348ff38e2a89907c58c352632bf3462f1bb8a2cd365e70a04318e9843bf7bf5ba9f4d945d4a51601b5d6786afb2db323d7dd237101ad200f62a297e0d299
7
- data.tar.gz: dc0876004af2d8e32cb06b8b703ea0290f764c4d53333b3d0c9053bd73e2f8d87facb12f76776c54d03ca4a7a73fd2e41e1b41db0600a32a4039f442db0840f1
6
+ metadata.gz: 47e1bca34582e4406fcc268ac786a322cc3f99a62789aeece262b91e1ca26a15c145835ad60afbcd85a13b3a37da7a54541aedf8e2af5b60c43d7d6cf6767072
7
+ data.tar.gz: 2edfb035e82896ea22bec576a98b34eb2e3c18cccf2d3ba18744f7b77135f6a1310b466179025c216d396708fdf5fbe802a2dc436c57d1510632eb9567776df1
data/CHANGELOG.md CHANGED
@@ -30,6 +30,51 @@ The category for changes related to documentation, testing and tooling. Also, fo
30
30
 
31
31
  ## main
32
32
 
33
+ ## 0.0.47
34
+
35
+ ### Breaking changes
36
+
37
+ * Restrict tag for `Popover` to `:div` and `Popover` heading slot to headings.
38
+
39
+ *Kate Higa*
40
+
41
+ * Renames:
42
+ * `Primer::AutoComplete` to `Primer::Beta::AutoComplete`
43
+ * `Primer::AutoComplete::Item` to `Primer::Beta::AutoComplete::Item`
44
+ * `Primer::AvatarComponent` to `Primer::Beta::Avatar`
45
+
46
+ *Manuel Puyol*
47
+
48
+ ### Misc
49
+
50
+ * Update `doc_examples_axe_test` to exclude non-standalone components and fix `Markdown` example.
51
+
52
+ *Kate Higa*
53
+
54
+ * Update `DetailsComponent` examples.
55
+
56
+ *Manuel Puyol*
57
+
58
+ * Add linter to suggest system arguments instead of classes.
59
+
60
+ *Manuel Puyol*
61
+
62
+ * Update component generator to create components in the right status module.
63
+
64
+ *Manuel Puyol*
65
+
66
+ * Add example for truncating HTML to `Truncate`.
67
+
68
+ *Joel Hawksley*
69
+
70
+ * Update docs generation to point to the correct file sources.
71
+
72
+ *Manuel Puyol*
73
+
74
+ * Add ENV flag to dump linter data into a file.
75
+
76
+ *Manuel Puyol*
77
+
33
78
  ## 0.0.46
34
79
 
35
80
  ### Updates
@@ -15,8 +15,8 @@ module Primer
15
15
  BODY_TAG_OPTIONS = TAG_OPTIONS = [DEFAULT_TAG, :span].freeze
16
16
  # Required list of stacked avatars.
17
17
  #
18
- # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::AvatarComponent) %>.
19
- 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"
20
20
 
21
21
  # @example Default
22
22
  # <%= render(Primer::AvatarStackComponent.new) do |c| %>
@@ -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 = system_arguments[:"aria-label"] || system_arguments.dig(:aria, :label) || @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 = system_arguments[:"aria-label"] || system_arguments.dig(:aria, :label)
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
@@ -0,0 +1 @@
1
+ import '@github/auto-complete-element';
@@ -0,0 +1 @@
1
+ import '@github/auto-complete-element';
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Beta
5
+ class AutoComplete
6
+ # Use `AutoCompleteItem` to list results of an auto-completed search.
7
+ class Item < Primer::Component
8
+ status :beta
9
+
10
+ # @example Default
11
+ # <%= render(Primer::Beta::AutoComplete::Item.new(selected: true, value: "value")) do |c| %>
12
+ # Selected
13
+ # <% end %>
14
+ # <%= render(Primer::Beta::AutoComplete::Item.new(value: "value")) do |c| %>
15
+ # Not selected
16
+ # <% end %>
17
+ #
18
+ # @param value [String] Value of the item.
19
+ # @param selected [Boolean] Whether the item is selected.
20
+ # @param disabled [Boolean] Whether the item is disabled.
21
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
22
+ def initialize(value:, selected: false, disabled: false, **system_arguments)
23
+ @system_arguments = system_arguments
24
+ @system_arguments[:tag] = :li
25
+ @system_arguments[:role] = :option
26
+ @system_arguments[:"data-autocomplete-value"] = value
27
+
28
+ @system_arguments[:"aria-selected"] = true if selected
29
+ @system_arguments[:"aria-disabled"] = true if disabled
30
+
31
+ @system_arguments[:classes] = class_names(
32
+ "autocomplete-item",
33
+ system_arguments[:classes],
34
+ "disabled" => disabled
35
+ )
36
+ end
37
+
38
+ def call
39
+ render(Primer::BaseComponent.new(**@system_arguments)) { content }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Beta
5
+ # `Avatar` can be used to represent users and organizations on GitHub.
6
+ #
7
+ # - Use the default round avatar for users, and the `square` argument
8
+ # for organizations or any other non-human avatars.
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
+ # - 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) %>.
12
+ #
13
+ # @accessibility
14
+ # Images should have text alternatives that describe the information or function represented.
15
+ # If the avatar functions as a link, provide alt text that helps convey the function. For instance,
16
+ # if `Avatar` is a link to a user profile, the alt attribute should be `@kittenuser profile`
17
+ # rather than `@kittenuser`.
18
+ # [Learn more about best image practices (WAI Images)](https://www.w3.org/WAI/tutorials/images/)
19
+ class Avatar < Primer::Component
20
+ status :beta
21
+
22
+ SMALL_THRESHOLD = 24
23
+
24
+ # @example Default
25
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser")) %>
26
+ #
27
+ # @example Square
28
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", square: true)) %>
29
+ #
30
+ # @example Link
31
+ # <%= render(Primer::Beta::Avatar.new(href: "#", src: "http://placekitten.com/200/200", alt: "@kittenuser profile")) %>
32
+ #
33
+ # @example With size
34
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 16)) %>
35
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 20)) %>
36
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 24)) %>
37
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 28)) %>
38
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 32)) %>
39
+ # <%= render(Primer::Beta::Avatar.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 36)) %>
40
+ #
41
+ # @param src [String] The source url of the avatar image.
42
+ # @param alt [String] Passed through to alt on img tag.
43
+ # @param size [Integer] Adds the avatar-small class if less than 24.
44
+ # @param square [Boolean] Used to create a square avatar.
45
+ # @param href [String] The URL to link to. If used, component will be wrapped by an `<a>` tag.
46
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
47
+ def initialize(src:, alt:, size: 20, square: false, href: nil, **system_arguments)
48
+ @href = href
49
+ @system_arguments = system_arguments
50
+ @system_arguments[:tag] = :img
51
+ @system_arguments[:src] = src
52
+ @system_arguments[:alt] = alt
53
+ @system_arguments[:size] = size
54
+ @system_arguments[:height] = size
55
+ @system_arguments[:width] = size
56
+
57
+ @system_arguments[:classes] = class_names(
58
+ system_arguments[:classes],
59
+ "avatar",
60
+ "avatar-small" => size < SMALL_THRESHOLD,
61
+ "circle" => !square,
62
+ "lh-0" => href # Addresses an overflow issue with linked avatars
63
+ )
64
+ end
65
+
66
+ def call
67
+ if @href
68
+ render(Primer::LinkComponent.new(href: @href, classes: @system_arguments[:classes])) do
69
+ render(Primer::BaseComponent.new(**@system_arguments.except(:classes))) { content }
70
+ end
71
+ else
72
+ render(Primer::BaseComponent.new(**@system_arguments)) { content }
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -29,7 +29,7 @@ module Primer
29
29
 
30
30
  # Use the Body slot as the main content to be shown when triggered by the Summary.
31
31
  #
32
- # @param tag [String] (Primer::DetailsComponent::BODY_TAG_DEFAULT) <%= one_of(Primer::DetailsComponent::BODY_TAG_OPTIONS) %>
32
+ # @param tag [Symbol] (Primer::DetailsComponent::BODY_TAG_DEFAULT) <%= one_of(Primer::DetailsComponent::BODY_TAG_OPTIONS) %>
33
33
  # @param kwargs [Hash] The same arguments as <%= link_to_system_arguments_docs %>.
34
34
  renders_one :body, lambda { |tag: BODY_TAG_DEFAULT, **system_arguments|
35
35
  system_arguments[:tag] = fetch_or_fallback(BODY_TAG_OPTIONS, tag, BODY_TAG_DEFAULT)
@@ -40,12 +40,12 @@ module Primer
40
40
  # @example Default
41
41
  #
42
42
  # <%= render Primer::DetailsComponent.new do |c| %>
43
- # component.summary do
44
- # "Summary"
45
- # end
46
- # component.body do
47
- # "Body"
48
- # end
43
+ # <% c.summary do %>
44
+ # Summary
45
+ # <% end %>
46
+ # <% c.body do %>
47
+ # Body
48
+ # <% end %>
49
49
  # <% end %>
50
50
  #
51
51
  # @param overlay [Symbol] Dictates the type of overlay to render with. <%= one_of(Primer::DetailsComponent::OVERLAY_MAPPINGS.keys) %>
@@ -99,17 +99,17 @@ module Primer
99
99
  # <p>And an unordered task list:</p>
100
100
  #
101
101
  # <ul>
102
- # <li><input type="checkbox" checked /> Create a sample markdown document</li>
103
- # <li><input type="checkbox"/> Add task lists to it</li>
104
- # <li><input type="checkbox"/> Take a vacation</li>
102
+ # <li><input type="checkbox" id="create-markdown" checked /><label for="create-markdown">Create a sample markdown document</label><br></li>
103
+ # <li><input type="checkbox" id="tasks-list" checked /><label for="tasks-list">Add tasks list to it</label><br></li>
104
+ # <li><input type="checkbox" id="take-vacation" checked /><label for="take-vacation">Take a vacation</label><br></li>
105
105
  # </ul>
106
106
  #
107
107
  # <p>And a "mixed" task list:</p>
108
108
  #
109
109
  # <ul>
110
- # <li><input type="checkbox"/> Steal underpants</li>
110
+ # <li><input type="checkbox"id="steal-underpants"/><label for="steal-underpants">Steal underpants</label></li>
111
111
  # <li>?</li>
112
- # <li><input type="checkbox"/> Profit!</li>
112
+ # <li><input type="checkbox"id="profit"/><label for="profit">Profit!</label></li>
113
113
  # </ul>
114
114
  #
115
115
  # And a nested list:
@@ -241,9 +241,9 @@ module Primer
241
241
  #
242
242
  # <pre><code>var foo = "bar";</code></pre>
243
243
  #
244
- # <pre><code>Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.</code></pre>
244
+ # <pre tabindex="0"><code>Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.</code></pre>
245
245
  #
246
- # <pre><code>var foo = "The same thing is true for code with syntax highlighting. A single line of code should horizontally scroll if it is really long.";</code></pre>
246
+ # <pre tabindex="0"><code>var foo = "The same thing is true for code with syntax highlighting. A single line of code should horizontally scroll if it is really long.";</code></pre>
247
247
  #
248
248
  # <p>Inline code inside table cells should still be distinguishable.</p>
249
249
  #
@@ -270,11 +270,11 @@ module Primer
270
270
  #
271
271
  # <p>Small images should be shown at their actual size.</p>
272
272
  #
273
- # <p><img src="http://placekitten.com/g/300/200/"/></p>
273
+ # <p><img alt="kitten" src="http://placekitten.com/g/300/200/"/></p>
274
274
  #
275
275
  # <p>Large images should always scale down and fit in the content container.</p>
276
276
  #
277
- # <p><img src="http://placekitten.com/g/1200/800/"/></p>
277
+ # <p><img alt="kitten" src="http://placekitten.com/g/1200/800/"/></p>
278
278
  #
279
279
  # <pre><code>This is the final element on the page and there should be no margin below this.</code></pre>
280
280
  # <% end %>
@@ -23,12 +23,15 @@ module Primer
23
23
  :top_right => "Popover-message--top-right"
24
24
  }.freeze
25
25
 
26
+ DEFAULT_HEADING_TAG = :h4
27
+
26
28
  # The heading
27
29
  #
30
+ # @param tag [Symbol] (Primer::PopoverComponent::DEFAULT_HEADING_TAG) <%= one_of(Primer::HeadingComponent::TAG_OPTIONS) %>
28
31
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
29
- renders_one :heading, lambda { |**system_arguments|
32
+ renders_one :heading, lambda { |tag: DEFAULT_HEADING_TAG, **system_arguments|
33
+ system_arguments[:tag] = tag
30
34
  system_arguments[:mb] ||= 2
31
- system_arguments[:tag] ||= :h4 # rubocop:disable Primer/NoTagMemoize
32
35
 
33
36
  Primer::HeadingComponent.new(**system_arguments)
34
37
  }
@@ -106,7 +109,7 @@ module Primer
106
109
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
107
110
  def initialize(**system_arguments)
108
111
  @system_arguments = system_arguments
109
- @system_arguments[:tag] ||= :div # rubocop:disable Primer/NoTagMemoize
112
+ @system_arguments[:tag] = :div
110
113
  @system_arguments[:classes] = class_names(
111
114
  system_arguments[:classes],
112
115
  "Popover"
@@ -1,4 +1,4 @@
1
- import './auto_complete/auto_complete';
1
+ import './beta/auto_complete/auto_complete';
2
2
  import './clipboard_copy_component';
3
3
  import './tab_container_component';
4
4
  import './time_ago_component';
@@ -1,4 +1,4 @@
1
- import './auto_complete/auto_complete';
1
+ import './beta/auto_complete/auto_complete';
2
2
  import './clipboard_copy_component';
3
3
  import './tab_container_component';
4
4
  import './time_ago_component';
@@ -1,4 +1,4 @@
1
- import './auto_complete/auto_complete'
1
+ import './beta/auto_complete/auto_complete'
2
2
  import './clipboard_copy_component'
3
3
  import './tab_container_component'
4
4
  import './time_ago_component'
@@ -7,14 +7,14 @@ module Primer
7
7
 
8
8
  # Avatar to be rendered to the left of the Badge.
9
9
  #
10
- # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::AvatarComponent) %>.
10
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::Beta::Avatar) %>.
11
11
  renders_one :avatar, lambda { |src:, size: 40, square: true, **system_arguments|
12
12
  system_arguments[:classes] = class_names(
13
13
  "TimelineItem-avatar",
14
14
  system_arguments[:classes]
15
15
  )
16
16
 
17
- Primer::AvatarComponent.new(src: src, size: size, square: square, **system_arguments)
17
+ Primer::Beta::Avatar.new(src: src, size: size, square: square, **system_arguments)
18
18
  }
19
19
 
20
20
  # Badge that will be connected to other TimelineItems.
@@ -22,6 +22,11 @@ module Primer
22
22
  # @example Custom size
23
23
  # <%= render(Primer::Truncate.new(tag: :span, inline: true, expandable: true, max_width: 100)) { "branch-name-that-is-really-long" } %>
24
24
  #
25
+ # @example With HTML content
26
+ # <%= render(Primer::Truncate.new(tag: :span, inline: true, expandable: true, max_width: 100)) do %>
27
+ # <span>branch-name-that-is-really-long</span>
28
+ # <% end %>
29
+ #
25
30
  # @param tag [Symbol] <%= one_of(Primer::Truncate::TAG_OPTIONS) %>
26
31
  # @param inline [Boolean] Whether the element is inline (or inline-block).
27
32
  # @param expandable [Boolean] Whether the entire string should be revealed on hover. Can only be used in conjunction with `inline`.
@@ -38,7 +38,7 @@ module Primer
38
38
 
39
39
  # Use actions for a call to action.
40
40
  #
41
- # @param tag [String] (Primer::UnderlineNavComponent::ACTIONS_TAG_DEFAULT) <%= one_of(Primer::UnderlineNavComponent::ACTIONS_TAG_OPTIONS) %>
41
+ # @param tag [Symbol] (Primer::UnderlineNavComponent::ACTIONS_TAG_DEFAULT) <%= one_of(Primer::UnderlineNavComponent::ACTIONS_TAG_OPTIONS) %>
42
42
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
43
43
  renders_one :actions, lambda { |tag: ACTIONS_TAG_DEFAULT, **system_arguments|
44
44
  system_arguments[:tag] = fetch_or_fallback(ACTIONS_TAG_OPTIONS, tag, ACTIONS_TAG_DEFAULT)
@@ -11,16 +11,6 @@ require_relative "classify/utilities"
11
11
  module Primer
12
12
  # :nodoc:
13
13
  class Classify
14
- # Load the utilities.yml file.
15
- # Disabling because we want to load symbols, strings, and integers from the .yml file
16
- # rubocop:disable Security/YAMLLoad
17
- UTILITIES = YAML.load(
18
- File.read(
19
- File.join(File.dirname(__FILE__), "./classify/utilities.yml")
20
- )
21
- ).freeze
22
- # rubocop:enable Security/YAMLLoad
23
-
24
14
  # Keys where we can simply translate { key: value } into ".key-value"
25
15
  CONCAT_KEYS = %i[text box_shadow].freeze
26
16
 
@@ -90,7 +80,7 @@ module Primer
90
80
  BORDER_RADIUS_KEY = :border_radius
91
81
  TYPOGRAPHY_KEYS = [:font_size].freeze
92
82
  VALID_KEYS = (
93
- UTILITIES.keys +
83
+ Primer::Classify::Utilities::UTILITIES.keys +
94
84
  CONCAT_KEYS +
95
85
  BOOLEAN_MAPPINGS.keys +
96
86
  BORDER_MARGIN_KEYS +
@@ -5,13 +5,24 @@ module Primer
5
5
  class Classify
6
6
  # Handler for PrimerCSS utility classes loaded from utilities.rake
7
7
  class Utilities
8
+ # Load the utilities.yml file.
9
+ # Disabling because we want to load symbols, strings, and integers from the .yml file
10
+ # rubocop:disable Security/YAMLLoad
11
+ UTILITIES = YAML.load(
12
+ File.read(
13
+ File.join(File.dirname(__FILE__), "./utilities.yml")
14
+ )
15
+ ).freeze
16
+ # rubocop:enable Security/YAMLLoad
17
+ BREAKPOINTS = ["", "-sm", "-md", "-lg", "-xl"].freeze
18
+
8
19
  class << self
9
20
  def classname(key, val, breakpoint = "")
10
21
  if (valid = validate(key, val, breakpoint))
11
22
  valid
12
23
  else
13
24
  # Get selector
14
- Primer::Classify::UTILITIES[key][val][Primer::Classify::BREAKPOINTS.index(breakpoint)]
25
+ UTILITIES[key][val][BREAKPOINTS.index(breakpoint)]
15
26
  end
16
27
  end
17
28
 
@@ -19,14 +30,14 @@ module Primer
19
30
  #
20
31
  # returns Boolean
21
32
  def supported_key?(key)
22
- Primer::Classify::UTILITIES[key].present?
33
+ UTILITIES[key].present?
23
34
  end
24
35
 
25
36
  # Does the Utilitiy class support the given key and value
26
37
  #
27
38
  # returns Boolean
28
39
  def supported_value?(key, val)
29
- supported_key?(key) && Primer::Classify::UTILITIES[key][val].present?
40
+ supported_key?(key) && UTILITIES[key][val].present?
30
41
  end
31
42
 
32
43
  # Does the given selector exist in the utilities file
@@ -34,7 +45,7 @@ module Primer
34
45
  # returns Boolean
35
46
  def supported_selector?(selector)
36
47
  # This method is too slow to run in production
37
- return false if Rails.env.production?
48
+ return false if ENV["RAILS_ENV"] == "production"
38
49
 
39
50
  find_selector(selector).present?
40
51
  end
@@ -43,7 +54,7 @@ module Primer
43
54
  #
44
55
  # returns Boolean
45
56
  def responsive?(key, val)
46
- supported_value?(key, val) && Primer::Classify::UTILITIES[key][val].count > 1
57
+ supported_value?(key, val) && UTILITIES[key][val].count > 1
47
58
  end
48
59
 
49
60
  # Get the options for the given key
@@ -52,13 +63,13 @@ module Primer
52
63
  def mappings(key)
53
64
  return unless supported_key?(key)
54
65
 
55
- Primer::Classify::UTILITIES[key].keys
66
+ UTILITIES[key].keys
56
67
  end
57
68
 
58
69
  # Extract hash from classes ie. "mr-1 mb-2 foo" => { mr: 1, mb: 2, classes: "foo" }
59
70
  def classes_to_hash(classes)
60
71
  # This method is too slow to run in production
61
- return { classes: classes } if Rails.env.production?
72
+ return { classes: classes } if ENV["RAILS_ENV"] == "production"
62
73
 
63
74
  obj = {}
64
75
  classes = classes.split(" ")
@@ -94,7 +105,7 @@ module Primer
94
105
 
95
106
  def find_selector(selector)
96
107
  # Search each key/value_hash pair, eg. key `:mr` and value_hash `{ 0 => [ "mr-0", "mr-sm-0", "mr-md-0", "mr-lg-0", "mr-xl-0" ] }`
97
- Primer::Classify::UTILITIES.each do |key, value_hash|
108
+ UTILITIES.each do |key, value_hash|
98
109
  # Each value hash will also contain an array of classnames for breakpoints
99
110
  # Key argument `0`, classes `[ "mr-0", "mr-sm-0", "mr-md-0", "mr-lg-0", "mr-xl-0" ]`
100
111
  value_hash.each do |key_argument, classnames|
@@ -112,19 +123,19 @@ module Primer
112
123
 
113
124
  def validate(key, val, breakpoint)
114
125
  unless supported_key?(key)
115
- raise ArgumentError, "#{key} is not a valid Primer utility key" unless Rails.env.production?
126
+ raise ArgumentError, "#{key} is not a valid Primer utility key" unless ENV["RAILS_ENV"] == "production"
116
127
 
117
128
  return ""
118
129
  end
119
130
 
120
131
  unless breakpoint.empty? || responsive?(key, val)
121
- raise ArgumentError, "#{key} does not support responsive values" unless Rails.env.production?
132
+ raise ArgumentError, "#{key} does not support responsive values" unless ENV["RAILS_ENV"] == "production"
122
133
 
123
134
  return ""
124
135
  end
125
136
 
126
137
  unless supported_value?(key, val)
127
- raise ArgumentError, "#{val} is not a valid value for :#{key}. Use one of #{mappings(key)}" unless Rails.env.production?
138
+ raise ArgumentError, "#{val} is not a valid value for :#{key}. Use one of #{mappings(key)}" unless ENV["RAILS_ENV"] == "production"
128
139
 
129
140
  return ""
130
141
  end
@@ -13,10 +13,13 @@ module ERBLint
13
13
  link menuitem meta param source track wbr img
14
14
  ].freeze
15
15
 
16
+ DUMP_FILE = ".erblint-counter-ignore.json"
17
+
16
18
  def self.included(base)
17
19
  base.include(ERBLint::LinterRegistry)
18
20
 
19
21
  define_method "run" do |processed_source|
22
+ @total_offenses = 0
20
23
  @offenses_not_corrected = 0
21
24
  tags = tags(processed_source)
22
25
  tag_tree = build_tag_tree(tags)
@@ -43,6 +46,7 @@ module ERBLint
43
46
  tag_tree.each do |tag, h|
44
47
  next unless h[:offense]
45
48
 
49
+ @total_offenses += 1
46
50
  # We always fix the offenses using blocks. The closing tag corresponds to `<% end %>`.
47
51
  if h[:correctable]
48
52
  add_offense(tag.loc, h[:message], h[:correction])
@@ -54,6 +58,8 @@ module ERBLint
54
58
  end
55
59
 
56
60
  counter_correct?(processed_source)
61
+
62
+ dump_data(processed_source) if ENV["DUMP_LINT_DATA"] == "1"
57
63
  end
58
64
 
59
65
  define_method "autocorrect" do |processed_source, offense|
@@ -186,6 +192,17 @@ module ERBLint
186
192
  offense = ["#{klass_name}:#{message}", tag.node.loc.source].join("\n")
187
193
  add_offense(processed_source.to_source_range(tag.loc), offense, replacement)
188
194
  end
195
+
196
+ def dump_data(processed_source)
197
+ return if @total_offenses.zero?
198
+
199
+ data = File.exist?(DUMP_FILE) ? JSON.parse(File.read(DUMP_FILE)) : {}
200
+
201
+ data[processed_source.filename] ||= {}
202
+ data[processed_source.filename][self.class.name.demodulize] = @total_offenses
203
+
204
+ File.write(DUMP_FILE, JSON.pretty_generate(data))
205
+ end
189
206
  end
190
207
  end
191
208
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Primer
6
+ # :nodoc:
7
+ module ViewComponents
8
+ STATUSES = JSON.parse(
9
+ File.read(
10
+ File.join(File.dirname(__FILE__), "../../../static/statuses.json")
11
+ )
12
+ ).freeze
13
+ end
14
+ end
@@ -5,7 +5,7 @@ module Primer
5
5
  module VERSION
6
6
  MAJOR = 0
7
7
  MINOR = 0
8
- PATCH = 46
8
+ PATCH = 47
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH].join(".")
11
11
  end
data/lib/tasks/docs.rake CHANGED
@@ -32,9 +32,9 @@ namespace :docs do
32
32
  Primer::OcticonSymbolsComponent,
33
33
  Primer::ImageCrop,
34
34
  Primer::IconButton,
35
- Primer::AutoComplete,
36
- Primer::AutoComplete::Item,
37
- Primer::AvatarComponent,
35
+ Primer::Beta::AutoComplete,
36
+ Primer::Beta::AutoComplete::Item,
37
+ Primer::Beta::Avatar,
38
38
  Primer::AvatarStackComponent,
39
39
  Primer::BaseButton,
40
40
  Primer::BlankslateComponent,
@@ -81,7 +81,7 @@ namespace :docs do
81
81
  Primer::Dropdown,
82
82
  Primer::LocalTime,
83
83
  Primer::ImageCrop,
84
- Primer::AutoComplete,
84
+ Primer::Beta::AutoComplete,
85
85
  Primer::ClipboardCopy,
86
86
  Primer::TabContainerComponent,
87
87
  Primer::TabNavComponent,
@@ -97,28 +97,30 @@ namespace :docs do
97
97
 
98
98
  errors = []
99
99
 
100
- components.each do |component|
100
+ # Deletes docs before regenerating them, guaranteeing that we don't keep stale docs.
101
+ FileUtils.rm_rf(Dir.glob("docs/content/components/**/*.md"))
102
+
103
+ components.sort_by(&:name).each do |component|
101
104
  documentation = registry.get(component.name)
102
105
 
103
- # Primer::AvatarComponent => Avatar
104
- short_name = component.name.gsub(/Primer|::|Component/, "")
106
+ data = docs_metadata(component)
105
107
 
106
- path = Pathname.new("docs/content/components/#{short_name.downcase}.md")
108
+ path = Pathname.new(data[:path])
107
109
  path.dirname.mkdir unless path.dirname.exist?
108
110
  File.open(path, "w") do |f|
109
111
  f.puts("---")
110
- f.puts("title: #{short_name}")
111
- f.puts("status: #{component.status.to_s.capitalize}")
112
- f.puts("source: https://github.com/primer/view_components/tree/main/app/components/primer/#{component.to_s.demodulize.underscore}.rb")
113
- f.puts("storybook: https://primer.style/view-components/stories/?path=/story/primer-#{short_name.underscore.dasherize}-component")
112
+ f.puts("title: #{data[:title]}")
113
+ f.puts("status: #{data[:status]}")
114
+ f.puts("source: #{data[:source]}")
115
+ f.puts("storybook: #{data[:storybook]}")
114
116
  f.puts("---")
115
117
  f.puts
116
- f.puts("import Example from '../../src/@primer/gatsby-theme-doctocat/components/example'")
118
+ f.puts("import Example from '#{data[:example_path]}'")
117
119
 
118
120
  initialize_method = documentation.meths.find(&:constructor?)
119
121
 
120
122
  if js_components.include?(component)
121
- f.puts("import RequiresJSFlash from '../../src/@primer/gatsby-theme-doctocat/components/requires-js-flash'")
123
+ f.puts("import RequiresJSFlash from '#{data[:require_js_path]}'")
122
124
  f.puts
123
125
  f.puts("<RequiresJSFlash />")
124
126
  end
@@ -183,8 +185,8 @@ namespace :docs do
183
185
  end
184
186
 
185
187
  component_args = {
186
- "component" => short_name,
187
- "source" => "https://github.com/primer/view_components/tree/main/app/components/primer/#{component.to_s.demodulize.underscore}.rb",
188
+ "component" => data[:title],
189
+ "source" => data[:source],
188
190
  "parameters" => args
189
191
  }
190
192
 
@@ -346,6 +348,7 @@ namespace :docs do
346
348
  end
347
349
 
348
350
  def generate_yard_registry
351
+ ENV["SKIP_STORYBOOK_PRELOAD"] = "1"
349
352
  require File.expand_path("./../../demo/config/environment.rb", __dir__)
350
353
  require "primer/view_components"
351
354
  require "yard/docs_helper"
@@ -384,4 +387,55 @@ namespace :docs do
384
387
 
385
388
  pretty_value(constant_value)
386
389
  end
390
+
391
+ def status_module_and_short_name(component)
392
+ name_with_status = component.name.gsub(/Primer::|Component/, "")
393
+
394
+ m = name_with_status.match(/(?<status>Beta|Alpha|Deprecated)?(::)?(?<name>.*)/)
395
+ [m[:status]&.downcase, m[:name].gsub("::", "")]
396
+ end
397
+
398
+ def docs_metadata(component)
399
+ (status_module, short_name) = status_module_and_short_name(component)
400
+ status_path = status_module.nil? ? "" : "#{status_module}/"
401
+ status = component.status.to_s
402
+
403
+ {
404
+ title: short_name,
405
+ status: status.capitalize,
406
+ source: source_url(component),
407
+ storybook: storybook_url(component),
408
+ path: "docs/content/components/#{status_path}#{short_name.downcase}.md",
409
+ example_path: example_path(component),
410
+ require_js_path: require_js_path(component)
411
+ }
412
+ end
413
+
414
+ def source_url(component)
415
+ path = component.name.split("::").map(&:underscore).join("/")
416
+
417
+ "https://github.com/primer/view_components/tree/main/app/components/#{path}.rb"
418
+ end
419
+
420
+ def storybook_url(component)
421
+ path = component.name.split("::").map { |n| n.underscore.dasherize }.join("-")
422
+
423
+ "https://primer.style/view-components/stories/?path=/story/#{path}"
424
+ end
425
+
426
+ def example_path(component)
427
+ example_path = "../../src/@primer/gatsby-theme-doctocat/components/example"
428
+ example_path = "../#{example_path}" if status_module?(component)
429
+ example_path
430
+ end
431
+
432
+ def require_js_path(component)
433
+ require_js_path = "../../src/@primer/gatsby-theme-doctocat/components/requires-js-flash"
434
+ require_js_path = "../#{require_js_path}" if status_module?(component)
435
+ require_js_path
436
+ end
437
+
438
+ def status_module?(component)
439
+ (%w[Alpha Beta] & component.name.split("::")).any?
440
+ end
387
441
  end
@@ -41,7 +41,7 @@ module YARD
41
41
  end
42
42
 
43
43
  def link_to_component(component)
44
- short_name = component.name.gsub(/Primer|::|Component/, "")
44
+ short_name = component.name.gsub(/Primer|::|Alpha|Beta|Component/, "")
45
45
  "[#{short_name}](/components/#{short_name.downcase})"
46
46
  end
47
47
 
data/static/statuses.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "Primer::Alpha::ButtonMarketing": "alpha",
3
- "Primer::AutoComplete": "beta",
4
- "Primer::AutoComplete::Input": "alpha",
5
- "Primer::AutoComplete::Item": "beta",
6
- "Primer::AvatarComponent": "beta",
7
3
  "Primer::AvatarStackComponent": "beta",
8
4
  "Primer::BaseButton": "beta",
9
5
  "Primer::BaseComponent": "beta",
6
+ "Primer::Beta::AutoComplete": "beta",
7
+ "Primer::Beta::AutoComplete::Input": "alpha",
8
+ "Primer::Beta::AutoComplete::Item": "beta",
9
+ "Primer::Beta::Avatar": "beta",
10
10
  "Primer::Beta::Text": "beta",
11
11
  "Primer::BlankslateComponent": "beta",
12
12
  "Primer::BorderBoxComponent": "beta",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: primer_view_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.46
4
+ version: 0.0.47
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-29 00:00:00.000000000 Z
11
+ date: 2021-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionview
@@ -351,19 +351,21 @@ files:
351
351
  - app/assets/javascripts/primer_view_components.js
352
352
  - app/assets/javascripts/primer_view_components.js.map
353
353
  - app/components/primer/alpha/button_marketing.rb
354
- - app/components/primer/auto_complete.rb
355
354
  - app/components/primer/auto_complete/auto_complete.d.ts
356
- - app/components/primer/auto_complete/auto_complete.html.erb
357
355
  - app/components/primer/auto_complete/auto_complete.js
358
- - app/components/primer/auto_complete/auto_complete.ts
359
356
  - app/components/primer/auto_complete/auto_component.d.ts
360
357
  - app/components/primer/auto_complete/auto_component.js
361
- - app/components/primer/auto_complete/item.rb
362
- - app/components/primer/avatar_component.rb
363
358
  - app/components/primer/avatar_stack_component.html.erb
364
359
  - app/components/primer/avatar_stack_component.rb
365
360
  - app/components/primer/base_button.rb
366
361
  - app/components/primer/base_component.rb
362
+ - app/components/primer/beta/auto_complete.rb
363
+ - app/components/primer/beta/auto_complete/auto_complete.d.ts
364
+ - app/components/primer/beta/auto_complete/auto_complete.html.erb
365
+ - app/components/primer/beta/auto_complete/auto_complete.js
366
+ - app/components/primer/beta/auto_complete/auto_complete.ts
367
+ - app/components/primer/beta/auto_complete/item.rb
368
+ - app/components/primer/beta/avatar.rb
367
369
  - app/components/primer/beta/text.rb
368
370
  - app/components/primer/blankslate_component.html.erb
369
371
  - app/components/primer/blankslate_component.rb
@@ -483,6 +485,7 @@ files:
483
485
  - lib/primer/view_components/linters/button_component_migration_counter.rb
484
486
  - lib/primer/view_components/linters/flash_component_migration_counter.rb
485
487
  - lib/primer/view_components/linters/helpers.rb
488
+ - lib/primer/view_components/statuses.rb
486
489
  - lib/primer/view_components/version.rb
487
490
  - lib/tasks/coverage.rake
488
491
  - lib/tasks/docs.rake
@@ -1,157 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Primer
4
- # Use `AutoComplete` to provide a user with a list of selectable suggestions that appear when they type into the
5
- # input field. This list is populated by server search results.
6
- # @accessibility
7
- # Always set an accessible label to help the user interact with the component.
8
- #
9
- # * Set the `label` slot to render a visible label. Alternatively, associate an existing visible text element
10
- # as a label by setting `aria-labelledby`.
11
- # * If you must use a non-visible label, set `:"aria-label"` on `AutoComplete` and Primer
12
- # will apply it to the correct elements. However, please note that a visible label should almost
13
- # always be used unless there is compelling reason not to. A placeholder is not a label.
14
- class AutoComplete < Primer::Component
15
- status :beta
16
-
17
- # Optionally render a visible label. See <%= link_to_accessibility %>
18
- #
19
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
20
- renders_one :label, lambda { |**system_arguments|
21
- system_arguments[:for] = @input_id
22
- system_arguments[:tag] = :label
23
- Primer::BaseComponent.new(**system_arguments)
24
- }
25
-
26
- # Required input used to search for results
27
- #
28
- # @param type [Symbol] <%= one_of(Primer::AutoComplete::Input::TYPE_OPTIONS) %>
29
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
30
- renders_one :input, lambda { |**system_arguments|
31
- aria_label = system_arguments[:"aria-label"] || system_arguments.dig(:aria, :label) || @aria_label
32
- if aria_label.present?
33
- system_arguments[:"aria-label"] = aria_label
34
- system_arguments[:aria]&.delete(:label)
35
- end
36
-
37
- name = system_arguments[:name] || @input_id
38
- Input.new(id: @input_id, name: name, **system_arguments)
39
- }
40
-
41
- # Optional icon to be rendered before the input. Has the same arguments as <%= link_to_component(Primer::OcticonComponent) %>.
42
- #
43
- renders_one :icon, Primer::OcticonComponent
44
-
45
- # Customizable results list.
46
- #
47
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
48
- renders_one :results, lambda { |**system_arguments|
49
- system_arguments[:tag] = :ul
50
- system_arguments[:id] = @list_id
51
- system_arguments[:classes] = class_names(
52
- "autocomplete-results",
53
- system_arguments[:classes]
54
- )
55
-
56
- aria_label = system_arguments[:"aria-label"] || system_arguments.dig(:aria, :label) || @aria_label
57
- system_arguments[:"aria-label"] = aria_label if aria_label.present?
58
- system_arguments[:aria]&.delete(:label)
59
-
60
- Primer::BaseComponent.new(**system_arguments)
61
- }
62
-
63
- # @example Default
64
- # <%= render(Primer::AutoComplete.new(src: "/auto_complete", input_id: "fruits-input-1", list_id: "fruits-popup-1", position: :relative)) do |c| %>
65
- # <% c.label(classes:"").with_content("Fruits") %>
66
- # <% c.input(type: :text) %>
67
- # <% end %>
68
- #
69
- # @example With `aria-label`
70
- # <%= render(Primer::AutoComplete.new("aria-label": "Fruits", src: "/auto_complete", input_id: "fruits-input-2", list_id: "fruits-popup-2", position: :relative)) do |c| %>
71
- # <% c.input(type: :text) %>
72
- # <% end %>
73
- #
74
- # @example With `aria-labelledby`
75
- # <%= render(Primer::HeadingComponent.new(tag: :h2, id: "search-1")) { "Search" } %>
76
- # <%= render(Primer::AutoComplete.new(src: "/auto_complete", input_id: "fruits-input-3", list_id: "fruits-popup-2", position: :relative)) do |c| %>
77
- # <% c.input("aria-labelledby": "search-1") %>
78
- # <% end %>
79
- #
80
- # @example With custom classes for the results
81
- # <%= render(Primer::AutoComplete.new(src: "/auto_complete", input_id: "fruits-input-4", list_id: "fruits-popup-3", position: :relative)) do |c| %>
82
- # <% c.label(classes:"").with_content("Fruits") %>
83
- # <% c.input(type: :text) %>
84
- # <% c.results(classes: "custom-class") do %>
85
- # <%= render(Primer::AutoComplete::Item.new(selected: true, value: "apple")) do |c| %>
86
- # Apple
87
- # <% end %>
88
- # <%= render(Primer::AutoComplete::Item.new(value: "orange")) do |c| %>
89
- # Orange
90
- # <% end %>
91
- # <% end %>
92
- # <% end %>
93
- #
94
- # @example With Icon
95
- # <%= render(Primer::AutoComplete.new(src: "/auto_complete", list_id: "fruits-popup-4", input_id: "fruits-input-4", position: :relative)) do |c| %>
96
- # <% c.label(classes:"").with_content("Fruits") %>
97
- # <% c.input(type: :text) %>
98
- # <% c.icon(icon: :search) %>
99
- # <% end %>
100
- #
101
- # @param src [String] The route to query.
102
- # @param input_id [String] Id of the input element.
103
- # @param list_id [String] Id of the list element.
104
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
105
- def initialize(src:, list_id:, input_id:, **system_arguments)
106
- @list_id = list_id
107
- @input_id = input_id
108
- @aria_label = system_arguments[:"aria-label"] || system_arguments.dig(:aria, :label)
109
-
110
- system_arguments.delete(:"aria-label") && system_arguments[:aria]&.delete(:label)
111
-
112
- @system_arguments = system_arguments
113
- @system_arguments[:tag] = "auto-complete"
114
- @system_arguments[:src] = src
115
- @system_arguments[:for] = list_id
116
- end
117
-
118
- # add `results` without needing to explicitly call it in the view
119
- def before_render
120
- raise ArgumentError, "Missing `input` slot" if input.blank?
121
- raise ArgumentError, "Accessible label is required." if label.blank? && input.missing_label?
122
-
123
- results(classes: "") unless results
124
- end
125
-
126
- # This component is part of `Primer::AutoCompleteComponent` and should not be
127
- # used as a standalone component.
128
- class Input < Primer::Component
129
- DEFAULT_TYPE = :text
130
- TYPE_OPTIONS = [DEFAULT_TYPE, :search].freeze
131
-
132
- # @param type [Symbol] <%= one_of(Primer::AutoComplete::Input::TYPE_OPTIONS) %>
133
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
134
- def initialize(type: DEFAULT_TYPE, **system_arguments)
135
- @system_arguments = system_arguments
136
- @system_arguments[:tag] = :input
137
-
138
- @aria_label = system_arguments[:"aria-label"]
139
- @aria_labelledby = system_arguments[:"aria-labelledby"] || system_arguments.dig(:aria, :labelledby)
140
-
141
- @system_arguments[:type] = fetch_or_fallback(TYPE_OPTIONS, type, DEFAULT_TYPE)
142
- @system_arguments[:classes] = class_names(
143
- "form-control",
144
- system_arguments[:classes]
145
- )
146
- end
147
-
148
- def missing_label?
149
- @aria_label.blank? && @aria_labelledby.blank?
150
- end
151
-
152
- def call
153
- render(Primer::BaseComponent.new(**@system_arguments))
154
- end
155
- end
156
- end
157
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Primer
4
- class AutoComplete
5
- # Use `AutoCompleteItem` to list results of an auto-completed search.
6
- class Item < Primer::Component
7
- status :beta
8
-
9
- # @example Default
10
- # <%= render(Primer::AutoComplete::Item.new(selected: true, value: "value")) do |c| %>
11
- # Selected
12
- # <% end %>
13
- # <%= render(Primer::AutoComplete::Item.new(value: "value")) do |c| %>
14
- # Not selected
15
- # <% end %>
16
- #
17
- # @param value [String] Value of the item.
18
- # @param selected [Boolean] Whether the item is selected.
19
- # @param disabled [Boolean] Whether the item is disabled.
20
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
21
- def initialize(value:, selected: false, disabled: false, **system_arguments)
22
- @system_arguments = system_arguments
23
- @system_arguments[:tag] = :li
24
- @system_arguments[:role] = :option
25
- @system_arguments[:"data-autocomplete-value"] = value
26
-
27
- @system_arguments[:"aria-selected"] = true if selected
28
- @system_arguments[:"aria-disabled"] = true if disabled
29
-
30
- @system_arguments[:classes] = class_names(
31
- "autocomplete-item",
32
- system_arguments[:classes],
33
- "disabled" => disabled
34
- )
35
- end
36
-
37
- def call
38
- render(Primer::BaseComponent.new(**@system_arguments)) { content }
39
- end
40
- end
41
- end
42
- end
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Primer
4
- # `Avatar` can be used to represent users and organizations on GitHub.
5
- #
6
- # - Use the default round avatar for users, and the `square` argument
7
- # for organizations or any other non-human avatars.
8
- # - 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>`.
9
- # - Set `size` to update the height and width of the `Avatar` in pixels.
10
- # - To stack multiple avatars together, use <%= link_to_component(Primer::AvatarStackComponent) %>.
11
- #
12
- # @accessibility
13
- # Images should have text alternatives that describe the information or function represented.
14
- # If the avatar functions as a link, provide alt text that helps convey the function. For instance,
15
- # if `Avatar` is a link to a user profile, the alt attribute should be `@kittenuser profile`
16
- # rather than `@kittenuser`.
17
- # [Learn more about best image practices (WAI Images)](https://www.w3.org/WAI/tutorials/images/)
18
- class AvatarComponent < Primer::Component
19
- status :beta
20
-
21
- SMALL_THRESHOLD = 24
22
-
23
- # @example Default
24
- # <%= render(Primer::AvatarComponent.new(src: "http://placekitten.com/200/200", alt: "@kittenuser")) %>
25
- #
26
- # @example Square
27
- # <%= render(Primer::AvatarComponent.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", square: true)) %>
28
- #
29
- # @example Link
30
- # <%= render(Primer::AvatarComponent.new(href: "#", src: "http://placekitten.com/200/200", alt: "@kittenuser profile")) %>
31
- #
32
- # @example With size
33
- # <%= render(Primer::AvatarComponent.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 16)) %>
34
- # <%= render(Primer::AvatarComponent.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 20)) %>
35
- # <%= render(Primer::AvatarComponent.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 24)) %>
36
- # <%= render(Primer::AvatarComponent.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 28)) %>
37
- # <%= render(Primer::AvatarComponent.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 32)) %>
38
- # <%= render(Primer::AvatarComponent.new(src: "http://placekitten.com/200/200", alt: "@kittenuser", size: 36)) %>
39
- #
40
- # @param src [String] The source url of the avatar image.
41
- # @param alt [String] Passed through to alt on img tag.
42
- # @param size [Integer] Adds the avatar-small class if less than 24.
43
- # @param square [Boolean] Used to create a square avatar.
44
- # @param href [String] The URL to link to. If used, component will be wrapped by an `<a>` tag.
45
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
46
- def initialize(src:, alt:, size: 20, square: false, href: nil, **system_arguments)
47
- @href = href
48
- @system_arguments = system_arguments
49
- @system_arguments[:tag] = :img
50
- @system_arguments[:src] = src
51
- @system_arguments[:alt] = alt
52
- @system_arguments[:size] = size
53
- @system_arguments[:height] = size
54
- @system_arguments[:width] = size
55
-
56
- @system_arguments[:classes] = class_names(
57
- system_arguments[:classes],
58
- "avatar",
59
- "avatar-small" => size < SMALL_THRESHOLD,
60
- "circle" => !square,
61
- "lh-0" => href # Addresses an overflow issue with linked avatars
62
- )
63
- end
64
-
65
- def call
66
- if @href
67
- render(Primer::LinkComponent.new(href: @href, classes: @system_arguments[:classes])) do
68
- render(Primer::BaseComponent.new(**@system_arguments.except(:classes))) { content }
69
- end
70
- else
71
- render(Primer::BaseComponent.new(**@system_arguments)) { content }
72
- end
73
- end
74
- end
75
- end