primer_view_components 0.0.46 → 0.0.47

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 (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