Skip to content

Commit

Permalink
Autocomplete design updates (#1189)
Browse files Browse the repository at this point in the history
* start fresh branch

* use temp css version

* update docs

* tests

* fix docs build

Co-authored-by: Jon Rohan <[email protected]>

* docs: build docs

* deprecate params

* docs: build docs

* re yarn

* tests

* docs: build docs

* fix css

* fix docs

* Create dull-roses-reflect.md

* Update app/components/primer/beta/auto_complete.rb

Co-authored-by: Cameron Dutro <[email protected]>

* Update app/components/primer/beta/auto_complete.rb

Co-authored-by: Cameron Dutro <[email protected]>

* Update app/components/primer/beta/auto_complete/item.rb

Co-authored-by: Cameron Dutro <[email protected]>

* Update lib/rubocop/cop/primer/deprecated_arguments.rb

Co-authored-by: Cameron Dutro <[email protected]>

* indent w spaces, cleanup

* bump pcss

* lint

* remove include

* so confident these tests will pass

* docs: build docs

* Update test/components/beta/auto_complete_test.rb

Co-authored-by: Jon Rohan <[email protected]>

* jon rohan!

* docs: build docs

* test adding html to preview

* add leading + trailing visuals to item

* rename classes

* previews for items with leading/trailing vis

Co-authored-by: Jon Rohan <[email protected]>

* lint

* docs: build docs

* test

* update classes + tests

* docs: build docs

* remove trailing action divider

* try bumping primer to pass css test

* docs: build docs

* fix tests

* simplify icon slots

Co-authored-by: Jon Rohan <[email protected]>

* fix icon def

* update item slots to be more flexible

* alrighty

* docs: build docs

* bundle

* make description a slot

* docs: build docs

* add remaining field props + fix docs css

* fix docs

* docs: build docs

* restore existing beta and rename to alpha

* add integration test

Co-authored-by: krhkt <[email protected]>

* remove text

* docs: build docs

* Move autocomplete lookbook previews out of lookbook folder

* partly fixing tests, need to update selectors

* fix integration tests

Co-authored-by: Jon Rohan <[email protected]>

* its done

* remove extra js file

* update js from main

* fix lookbook

* delete extra previews

* remove stories test

* fix docs a11y

* add more tests

* moar tests

* try adding test

* tests.. tests tests tests

* add avatar test

* remove svg as an option

* Fix formatting

* reset map file

* Delete

* Lint fix

* Fixing beta preview testg

* Checkout app/assets

* Missing version param

* Fixing old autocomplete

* Add support for inline description to AutocompleteItem

* Fix lint and visual padding

* docs: build docs

* Add description_variant check and fallback

* docs: build docs

* remove utility

* docs: build docs

Co-authored-by: Jon Rohan <[email protected]>
Co-authored-by: Actions Auto Build <[email protected]>
Co-authored-by: Cameron Dutro <[email protected]>
Co-authored-by: krhkt <[email protected]>
Co-authored-by: krhkt <[email protected]>
  • Loading branch information
6 people authored Jul 28, 2022
1 parent 6aa6892 commit ec3af74
Show file tree
Hide file tree
Showing 33 changed files with 1,210 additions and 251 deletions.
5 changes: 5 additions & 0 deletions .changeset/dull-roses-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/view-components": patch
---

Autocomplete design updates
158 changes: 158 additions & 0 deletions app/components/primer/alpha/auto_complete.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# frozen_string_literal: true

module Primer
module Alpha
# Use `AutoComplete` to provide a user with a list of selectable suggestions that appear when they type into the
# input field. This list is populated by server search results.
# @accessibility
# Always set an accessible label to help the user interact with the component.
#
# * `label_text` is required and visible by default.
# * If you must use a non-visible label, set `is_label_visible` to `false`.
# However, please note that a visible label should almost always
# be used unless there is compelling reason not to. A placeholder is not a label.
class AutoComplete < Primer::Component
status :alpha

# Customizable results list.
#
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
renders_one :results, lambda { |**system_arguments|
deny_tag_argument(**system_arguments)
system_arguments[:tag] = :ul
system_arguments[:id] = @list_id
system_arguments[:classes] = class_names(
"autocomplete-results",
system_arguments[:classes]
)
Primer::BaseComponent.new(**system_arguments)
}

# Customizable input used to search for results.
# It is preferred to use this slot sparingly - it will be created by default if not explicity added.
#
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
renders_one :input, lambda { |**system_arguments|
sanitized_args = deny_tag_argument(**system_arguments)
sanitized_args = deny_single_argument(:autofocus, "autofocus is not allowed for accessibility reasons. See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus#accessibility_considerations for more information.", **sanitized_args)
deny_aria_key(
:label,
"instead of `aria-label`, include `label_text` and set `is_label_visible` to `false` on the component initializer.",
**sanitized_args
)
deny_single_argument(
:id,
"`id` will always be set to @input_id.",
**sanitized_args
)
deny_single_argument(
:name,
"Set @input_name on the component initializer instead with `input_name`.",
**sanitized_args
)
sanitized_args[:id] = @input_id
sanitized_args[:name] = @input_name
sanitized_args[:tag] = :input
sanitized_args[:autocomplete] = "off"
sanitized_args[:type] = :text
sanitized_args[:classes] = class_names(
"form-control",
sanitized_args[:classes]
)
Primer::BaseComponent.new(**sanitized_args)
}

# @example Default
# @description
# Labels are stacked by default.
# @code
# <%= render(Primer::Alpha::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--default", list_id: "fruits-popup--default")) %>
#
# @example With inline label
# @description
# Labels can be inline by setting `is_label_inline: true`. However, labels will always become stacked on smaller screen sizes.
# @code
# <%= render(Primer::Alpha::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", is_label_inline: true, input_id: "fruits-input--inline-label", list_id: "fruits-popup--inline-label")) %>
#
# @example With non-visible label
# @description
# A non-visible label may be rendered with `is_label_visible: false`, but it is highly discouraged. See <%= link_to_accessibility %>.
# @code
# <%= render(Primer::Alpha::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--non-visible-label", list_id: "fruits-popup--non-visible-label", is_label_visible: false)) %>
#
# @example With icon
# @description
# To display a search icon, set `with_icon` to `true`.
# @code
# <%= render(Primer::Alpha::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", list_id: "fruits-popup--icon", input_id: "fruits-input--icon", with_icon: true)) %>
#
# @example With icon and non-visible label
# <%= render(Primer::Alpha::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", list_id: "fruits-popup--icon-no-label", input_id: "fruits-input--icon-no-label", with_icon: true, is_label_visible: false)) %>
#
# @example With clear button
# <%= render(Primer::Alpha::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--clear", list_id: "fruits-popup--clear", is_clearable: true)) %>
#
# @example With custom classes for the input
# <%= render(Primer::Alpha::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--custom-input", list_id: "fruits-popup--custom-input")) do |c| %>
# <% c.input(classes: "custom-class") %>
# <% end %>
#
# @example With custom classes for the results
# <%= render(Primer::Alpha::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--custom-results", list_id: "fruits-popup--custom-results")) do |c| %>
# <% c.results(classes: "custom-class") do %>
# <%= render(Primer::Alpha::AutoComplete::Item.new(selected: true, value: "apple")) do |c| %>
# Apple
# <% end %>
# <%= render(Primer::Alpha::AutoComplete::Item.new(value: "orange")) do |c| %>
# Orange
# <% end %>
# <% end %>
# <% end %>
#
# @param label_text [String] The label of the input.
# @param src [String] The route to query.
# @param input_id [String] Id of the input element.
# @param input_name [String] Optional name of the input element, defaults to `input_id` when not set.
# @param list_id [String] Id of the list element.
# @param with_icon [Boolean] Controls if a search icon is visible, defaults to `false`.
# @param is_label_visible [Boolean] Controls if the label is visible. If `false`, screen reader only text will be added.
# @param is_clearable [Boolean] Adds optional clear button.
# @param is_label_inline [Boolean] Controls if the label is inline. On smaller screens, label will always become stacked.
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
def initialize(label_text:, src:, list_id:, input_id:, input_name: nil, is_label_visible: true, is_label_inline: false, with_icon: false, is_clearable: false, **system_arguments)
@label_text = label_text
@list_id = list_id
@input_id = input_id
@input_name = input_name || input_id
@is_label_visible = is_label_visible
@with_icon = with_icon
@is_clearable = is_clearable
@label_classes = label_classes(is_label_visible: is_label_visible, is_label_inline: is_label_inline)
@system_arguments = deny_tag_argument(**system_arguments)
@system_arguments[:tag] = "auto-complete"
@system_arguments[:src] = src
@system_arguments[:for] = list_id
end

# add `input` and `results` without needing to explicitly call them in the view
def before_render
results(classes: "") unless results
input(classes: "") unless input
end

private

# Private: determines the label classes based on component configration.
#
# If the label is not visible, return an empty string.
#
# @param args [Hash] The component configuration.
# @return [String] The label classes.
def label_classes(**args)
return "" if args[:is_label_visible] == false

args[:is_label_inline] ? "autocomplete-label-inline" : "autocomplete-label-stacked"
end
end
end
end
24 changes: 24 additions & 0 deletions app/components/primer/alpha/auto_complete/auto_complete.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<%= render Primer::BaseComponent.new(**@system_arguments) do %>
<label for="<%= @input_id %>" class="<%= @label_classes %>">
<% if @is_label_visible %>
<%= @label_text %>
<% else %>
<span class="sr-only"><%= @label_text %></span>
<% end %>
</label>
<span class="autocomplete-body">
<% if @with_icon %>
<div class="form-control autocomplete-embedded-icon-wrap">
<%= render Primer::OcticonComponent.new(:search) %>
<% end %>
<%= input %>
<% if @is_clearable %>
<button id="<%= @input_id %>-clear" class="btn-octicon" aria-label="Clear"><%= primer_octicon "x" %></button>
<% end %>
<% if @with_icon %>
</div>
<% end %>
<%= results %>
</span>
<div id="<%= @list_id %>-feedback" class="sr-only"></div>
<% end %>
46 changes: 46 additions & 0 deletions app/components/primer/alpha/auto_complete/item.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

module Primer
module Alpha
class AutoComplete
# Use `AutoCompleteItem` to list results of an auto-completed search.
class Item < Primer::Component
status :alpha

# @example Default
# <%= render(Primer::Alpha::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--custom-results", list_id: "fruits-popup--custom-results")) do |c| %>
# <% c.results(classes: "custom-class") do %>
# <%= render(Primer::Alpha::AutoComplete::Item.new(selected: true, value: "apple")) do |c| %>
# Apple
# <% end %>
# <%= render(Primer::Alpha::AutoComplete::Item.new(value: "orange")) do |c| %>
# Orange
# <% end %>
# <% end %>
# <% end %>
#
# @param value [String] Value of the item.
# @param selected [Boolean] Whether the item is selected.
# @param disabled [Boolean] Whether the item is disabled.
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
def initialize(value:, selected: false, disabled: false, **system_arguments)
@system_arguments = deny_tag_argument(**system_arguments)
@system_arguments[:tag] = :li
@system_arguments[:role] = :option
@system_arguments[:"data-autocomplete-value"] = value
@system_arguments[:"aria-selected"] = true if selected
@system_arguments[:"aria-disabled"] = true if disabled
@system_arguments[:classes] = class_names(
"autocomplete-item",
system_arguments[:classes],
"disabled" => disabled
)
end

def call
render(Primer::BaseComponent.new(**@system_arguments)) { content }
end
end
end
end
end
Loading

0 comments on commit ec3af74

Please sign in to comment.