Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autocomplete design updates #1163

Merged
merged 31 commits into from
May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7cc38e2
start fresh branch
langermank May 20, 2022
5ef106a
use temp css version
langermank May 20, 2022
d517f31
update docs
langermank May 22, 2022
f488158
tests
langermank May 22, 2022
c9d975d
fix docs build
langermank May 24, 2022
bf838f7
docs: build docs
actions-user May 24, 2022
adb4373
deprecate params
langermank May 24, 2022
3b70c75
Merge branch 'autocomplete-design-updates' of https://github.com/prim…
langermank May 24, 2022
e5f7ef2
Merge branch 'main' of https://github.com/primer/view_components into…
langermank May 24, 2022
0e2ee50
docs: build docs
actions-user May 24, 2022
6c6fb75
re yarn
langermank May 24, 2022
7ea91c7
Merge branch 'autocomplete-design-updates' of https://github.com/prim…
langermank May 24, 2022
3defa48
tests
langermank May 24, 2022
9ade801
docs: build docs
actions-user May 24, 2022
ad166d6
fix css
langermank May 24, 2022
90f9623
Merge branch 'autocomplete-design-updates' of https://github.com/prim…
langermank May 24, 2022
3d74cc0
fix docs
langermank May 25, 2022
ce6da0b
Create dull-roses-reflect.md
langermank May 25, 2022
99093de
Update app/components/primer/beta/auto_complete.rb
langermank May 26, 2022
ecf5422
Update app/components/primer/beta/auto_complete.rb
langermank May 26, 2022
eb118f9
Update app/components/primer/beta/auto_complete/item.rb
langermank May 26, 2022
16ffbff
Update lib/rubocop/cop/primer/deprecated_arguments.rb
langermank May 26, 2022
0cfdb19
indent w spaces, cleanup
langermank May 26, 2022
161ddb9
bump pcss
langermank May 26, 2022
efe42ef
lint
langermank May 26, 2022
959c513
remove include
langermank May 26, 2022
315a228
so confident these tests will pass
langermank May 26, 2022
bc0a25d
docs: build docs
actions-user May 26, 2022
118c08b
Update test/components/beta/auto_complete_test.rb
langermank May 26, 2022
8898e63
jon rohan!
langermank May 26, 2022
e65af60
docs: build docs
actions-user May 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion app/assets/javascripts/primer_view_components.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion app/assets/javascripts/primer_view_components.js.map

Large diffs are not rendered by default.

156 changes: 109 additions & 47 deletions app/components/primer/beta/auto_complete.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,20 @@ module Beta
# 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`.
# * If you must hide the label, set `visually_hide_label` to `true`.
# 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 :beta

DEFAULT_SIZE = :medium
SIZE_MAPPINGS = {
:small => "FormControl--small",
DEFAULT_SIZE => "FormControl--medium",
:large => "FormControl--large"
}.freeze
SIZE_OPTIONS = SIZE_MAPPINGS.keys

#
# Customizable results list.
#
Expand All @@ -22,13 +31,26 @@ class AutoComplete < Primer::Component
system_arguments[:tag] = :ul
system_arguments[:id] = @list_id
system_arguments[:classes] = class_names(
"autocomplete-results",
"ActionList",
system_arguments[:classes]
)

Primer::BaseComponent.new(**system_arguments)
}

#
# Leading visual.
#
# - `leading_visual_icon` for a <%= link_to_component(Primer::OcticonComponent) %>.
#
# @param system_arguments [Hash] Same arguments as <%= link_to_component(Primer::OcticonComponent) %>.
renders_one :leading_visual, types: {
camertron marked this conversation as resolved.
Show resolved Hide resolved
icon: lambda { |**system_arguments|
system_arguments[:classes] = class_names("FormControl--input-leadingVisual")
Primer::OcticonComponent.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.
#
Expand All @@ -38,7 +60,7 @@ class AutoComplete < Primer::Component
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.",
"instead of `aria-label`, include `label_text` and set `visually_hide_label` to `true` on the component initializer.",
**sanitized_args
)
deny_single_argument(
Expand All @@ -55,45 +77,82 @@ class AutoComplete < Primer::Component
sanitized_args[:name] = @input_name
sanitized_args[:tag] = :input
sanitized_args[:autocomplete] = "off"

sanitized_args[:disabled] = true if @disabled
sanitized_args[:invalid] = true if @invalid
sanitized_args[:type] = :text
sanitized_args[:classes] = class_names(
"form-control",
"FormControl",
"FormControl--input",
SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, @size, DEFAULT_SIZE)],
sanitized_args[:classes]
)
sanitized_args[:placeholder] = @placeholder

Primer::BaseComponent.new(**sanitized_args)
}

# @example Default
# @example Leading visual
# @description
# Labels are stacked by default.
# Display any Octicon as a leading visual within the field
# @code
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--default", list_id: "fruits-popup--default")) %>
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Select a fruit", src: "/auto_complete", input_id:"input-id-1", list_id: "list-id-1")) do |c| %>
# <% c.leading_visual_icon(icon: :search) %>
# <% c.results do %>
# <%= render(Primer::Beta::AutoComplete::Item.new(selected: true, value: "apple")) do |_c| %>
# Apple
# <% end %>
# <%= render(Primer::Beta::AutoComplete::Item.new(value: "orange")) do |_c| %>
# Orange
# <% end %>
# <% end %>
# <% end %>
#
# @example With inline label
# @example Trailing action
# @description
# Labels can be inline by setting `is_label_inline: true`. However, labels will always become stacked on smaller screen sizes.
# Show a clear button
# @code
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", is_label_inline: true, input_id: "fruits-input--inline-label", list_id: "fruits-popup--inline-label")) %>
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Select a fruit", input_id: "input-id-2", list_id: "list-id-2", src: "/auto_complete", show_clear_button: true )) do |c| %>
# <% c.results do %>
# <%= render(Primer::Beta::AutoComplete::Item.new(selected: true, value: "apple")) do |_c| %>
# Apple
# <% end %>
# <%= render(Primer::Beta::AutoComplete::Item.new(value: "orange")) do |_c| %>
# Orange
# <% end %>
# <% end %>
# <% end %>
#
# @example With non-visible label
# @example Visually hidden label
# @description
# A non-visible label may be rendered with `is_label_visible: false`, but it is highly discouraged. See <%= link_to_accessibility %>.
# A non-visible label may be rendered with `visually_hide_label: true`, but it is highly discouraged. See <%= link_to_accessibility %>.
# @code
# <%= render(Primer::Beta::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)) %>
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Select a fruit", input_id: "fruits-input--custom-results-1", list_id: "fruits-popup--custom-result-1", src: "/auto_complete", visually_hide_label: true)) do |c| %>
# <% c.leading_visual_icon(icon: :search) %>
# <% c.results do %>
# <%= render(Primer::Beta::AutoComplete::Item.new(selected: true, value: "apple")) do |_c| %>
# Apple
# <% end %>
# <%= render(Primer::Beta::AutoComplete::Item.new(value: "orange")) do |_c| %>
# Orange
# <% end %>
# <% end %>
# <% end %>
#
# @example With icon
# @example Full width field
# @description
# To display a search icon, set `with_icon` to `true`.
# To allow field to span width of its container, set `full_width` to `true`.
# @code
# <%= render(Primer::Beta::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::Beta::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::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--clear", list_id: "fruits-popup--clear", is_clearable: true)) %>
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Select a fruit", input_id: "fruits-input--custom-results-2", list_id: "fruits-popup--custom-results-2", src: "/auto_complete", full_width: true)) do |c| %>
# <% c.leading_visual_icon(icon: :search) %>
# <% c.results do %>
# <%= render(Primer::Beta::AutoComplete::Item.new(selected: true, value: "apple")) do |_c| %>
# Apple
# <% end %>
# <%= render(Primer::Beta::AutoComplete::Item.new(value: "orange")) do |_c| %>
# Orange
# <% end %>
# <% end %>
# <% end %>
#
# @example With custom classes for the input
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--custom-input", list_id: "fruits-popup--custom-input")) do |c| %>
Expand All @@ -117,46 +176,49 @@ class AutoComplete < Primer::Component
# @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 visually_hide_label [Boolean] Controls if the label is visible. If `true`, screen reader only text will be added.
# @param show_clear_button [Boolean] Adds optional clear button.
# @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)
# @param size [Hash] Input size can be small, medium (default), or large
# @param full_width [Boolean] Input can be full-width or fit to content
# @param disabled [Boolean] Disabled input
# @param invalid [Boolean] Invalid input
# @param placeholder [String] The placeholder text displayed within the input
def initialize(label_text:, src:, list_id:, input_id:, input_name: nil, placeholder: nil, show_clear_button: false, visually_hide_label: false, size: DEFAULT_SIZE, full_width: false, disabled: false, invalid: 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)
@placeholder = placeholder
@visually_hide_label = visually_hide_label
@show_clear_button = show_clear_button
@system_arguments = deny_tag_argument(**system_arguments)
@system_arguments[:tag] = "auto-complete"
@system_arguments[:src] = src
@system_arguments[:for] = list_id
@disabled = disabled
@invalid = invalid
@size = size
@full_width = full_width
@field_wrap_classes = class_names(
"FormControl-fieldWrap",
"FormControl-fieldWrap--input",
SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, @size, DEFAULT_SIZE)],
"FormControl-fieldWrap--disabled": disabled,
"FormControl-fieldWrap--invalid": invalid,
"FormControl-fieldWrap--input-trailingAction": show_clear_button
)
@form_group_classes = class_names(
"FormGroup",
"FormGroup--fullWidth": full_width
)
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
36 changes: 19 additions & 17 deletions app/components/primer/beta/auto_complete/auto_complete.html.erb
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
<%= render Primer::BaseComponent.new(**@system_arguments) do %>
<label for="<%= @input_id %>" class="<%= @label_classes %>">
<% if @is_label_visible %>
<div class="<%= @form_group_classes %>">
<label for="<%= @input_id %>" class="FormControl-label <% if @visually_hide_label %>sr-only<% end %>">
<%= @label_text %>
</label>
<% if leading_visual || @show_clear_button %>
<div class="<%= @field_wrap_classes %> <% if leading_visual %>FormControl-fieldWrap--input-leadingVisual<% end %>">
<%= leading_visual %>
<%= input %>
<% if @show_clear_button %>
<button id="<%= @input_id %>-clear" class="FormControl--input-trailingAction" aria-label="Clear"><%= primer_octicon "x-circle-fill" %></button>
<% end %>
</div>
<% else %>
<span class="sr-only"><%= @label_text %></span>
<%= input %>
<% 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>
<div class="Overlay-backdrop--anchor">
<div class="Overlay Overlay--height-auto Overlay--width-auto">
<div class="Overlay-body Overlay-body--paddingNone">
<%= results %>
</div>
<% end %>
<%= results %>
</span>
</div>
</div>
<div id="<%= @list_id %>-feedback" class="sr-only"></div>
<% end %>
12 changes: 8 additions & 4 deletions app/components/primer/beta/auto_complete/item.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

# TODO: use generic ActionList item for Autocomplete
module Primer
module Beta
class AutoComplete
Expand Down Expand Up @@ -29,14 +30,17 @@ def initialize(value:, selected: false, disabled: false, **system_arguments)
@system_arguments[:"aria-disabled"] = true if disabled

@system_arguments[:classes] = class_names(
"autocomplete-item",
system_arguments[:classes],
"disabled" => disabled
"ActionList-item",
system_arguments[:classes]
)
end

def call
render(Primer::BaseComponent.new(**@system_arguments)) { content }
render(Primer::BaseComponent.new(**@system_arguments)) do
render(Primer::BaseComponent.new(tag: :span, classes: "ActionList-content")) do
render(Primer::BaseComponent.new(tag: :span, classes: "ActionList-item-label")) { content }
end
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"version": "0.1.0",
"dependencies": {
"@primer/css": "^20.1.1",
"@primer/css": "^20.2.2",
"@rails/actioncable": "^6.0.0",
"@rails/ujs": "^6.0.0",
"prettier": "^2.4.1",
Expand Down
10 changes: 6 additions & 4 deletions demo/test/components/previews/primer/auto_complete_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ def default
end

def with_non_visible_label
render(Primer::Beta::AutoComplete.new(label_text: "Select a fruit", input_id: "input-id", list_id: "test-id", src: "/auto_complete", is_label_visible: false))
render(Primer::Beta::AutoComplete.new(label_text: "Select a fruit", input_id: "input-id", list_id: "test-id", src: "/auto_complete", visually_hide_label: true))
end

def with_icon
render(Primer::Beta::AutoComplete.new(label_text: "Select a fruit", input_id: "input-id", list_id: "test-id", src: "/auto_complete", with_icon: true))
render(Primer::Beta::AutoComplete.new(label_text: "Select a fruit", input_id: "input-id", list_id: "test-id", src: "/auto_complete")) do |c|
c.leading_visual_icon(icon: :search)
end
end

def with_clear_button
render(Primer::Beta::AutoComplete.new(label_text: "Select a fruit", input_id: "input-id", list_id: "test-id", src: "/auto_complete", is_clearable: true))
def show_clear_button
render(Primer::Beta::AutoComplete.new(label_text: "Select a fruit", input_id: "input-id", list_id: "test-id", src: "/auto_complete", show_clear_button: true))
end
end
end
8 changes: 4 additions & 4 deletions demo/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1377,10 +1377,10 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353"
integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==

"@primer/css@^20.1.1":
version "20.1.1"
resolved "https://registry.yarnpkg.com/@primer/css/-/css-20.1.1.tgz#522b44b181d4443a0d12b1435fd5b0a13e15ca3b"
integrity sha512-Aah6qa9SCkEqVeauF+q+AUg29Bq5Ut8l6aYP9v5mt8IK9l0uHowpMcOeA+ERy4wSOGhHkn2zOe9M0qQTlVvhzg==
"@primer/css@^20.2.2":
version "20.2.2"
resolved "https://registry.yarnpkg.com/@primer/css/-/css-20.2.2.tgz#c36b73e9c336651675e51074c5b9e56d13fae3f5"
integrity sha512-vUy0vMKpdX9f3rhnm5fEIdcg9yxOdRYvjbZk5QoBXtn32oJPonSvIb/7JwVJA+EdiOHTvhRDx9zDpUwOYX+XrQ==
dependencies:
"@primer/primitives" "^7.8.3"

Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"build": "gatsby build --prefix-paths"
},
"dependencies": {
"@primer/css": "^20.1.1",
"@primer/css": "^20.2.2",
"@primer/gatsby-theme-doctocat": "^4.0.0",
"@primer/primitives": "^6.0.0",
"@primer/react": "^35.2.2",
Expand Down
9 changes: 6 additions & 3 deletions docs/src/@primer/gatsby-theme-doctocat/components/head.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react'
import Helmet from 'react-helmet'
import useSiteMetadata from '@primer/gatsby-theme-doctocat/src/use-site-metadata'
import '@primer/css/dist/primer.css'
import '@primer/css/dist/primitives.css'

// Reset PrimerCSS changing body font-size to 14px
const bodyStyle = `
Expand All @@ -19,10 +20,12 @@ function Head(props) {

let primerViewComponentsSrc

if(process.env.NODE_ENV === 'development') {
primerViewComponentsSrc = "http://localhost:4000/assets/primer_view_components.js"
if (process.env.NODE_ENV === 'development') {
langermank marked this conversation as resolved.
Show resolved Hide resolved
primerViewComponentsSrc =
'http://localhost:4000/assets/primer_view_components.js'
} else {
primerViewComponentsSrc = "https://unpkg.com/@primer/view-components@latest/app/assets/javascripts/primer_view_components.js"
primerViewComponentsSrc =
'https://unpkg.com/@primer/view-components@latest/app/assets/javascripts/primer_view_components.js'
}

return (
Expand Down
Loading