Dropdown

Displays a list (typically of actions or links) revealed by a toggle button. Identifiable by the chevron icon in the button.

The dropdown component has several flexible features that allow developers to build the dropdown menu they wish. A dropdown displays a list of links or actions to choose from. It should not to be confused with a select element which is used in a form (element) as a way for the user to select one or more options from a list.

The Dropdown component is composed of different child components, each with their own APIs:

  • the dropdown component (parent to the child components)

  • Toggle components to open/close the dropdown

    • ToggleButton
    • ToggleIcon
  • And finally, list item components, to build the dropdown's list items

    • Description
    • Generic
    • Interactive
    • Separator
    • Title

Notice: to make the invocation more intuitive for developers, all the sub-components are named yields, so the yielded name is a simplified version of the full component name (eg. Hds::ListItem::Interactive becomes just Interactive). See below how they are invoked as yielded components.

Here is the API for the main ("container") component:

Name
listPosition
Type
string
Values
  • left
  • right (default)
Name
width
Type
string
Values
any valid CSS width (px, rem, etc)
Description
Notice: by default the dropdown list has a min-width of 200px and a max-width of 400px applied to it, so it adapts to the content size. If a @width parameter is provided then the list will have a fixed width.
Name
close
Type
function
Description
Function that can be called to programmatically close the dropdown. Notice: if this function is invoked using an {{on "click"}} modifier applied to the ListItem::Interactive element, there is a quirk behaviour of the Ember <LinkTo> component that will require some workaround to have the events executed in the right order (this happens only if it has a @route argument). Jamie White has detailed the issue and a possible solution in this GitHub comment.
Name
onClose
Type
function
Description
Callback function invoked when the dropdown is closed (if provided).
Name
…attributes
Description
...attributes spreading is supported on this component.

Toggle::Button

Here is the API for the "button-like" toggle component (yielded in a hash under the key ToggleButton):

Name
text
Type
string
Required
Required
Description
The text of the toggle button. If no text value is defined an error will be thrown.
Name
color
Type
enum
Values
  • primary (default)
  • secondary
Name
size
Type
enum
Values
  • medium (default)
  • small
Name
…attributes
Description
...attributes spreading is supported on this component.

Toggle::Icon

Here is the API for the icon-only toggle component (yielded as ToggleIcon):

Name
text
Type
string
Required
Required
Description
The value of aria-label for the toggle icon. If no text value is defined an error will be thrown.
Name
icon
Type
string
Description
Acceptable value: any Flight icon name.
Name
hasChevron
Type
boolean
Description
Per design, false is only acceptable when the "more-horizontal" icon is used; as such, it is set to true by default.
Name
imageSrc
Type
string
Name
…attributes
Description
...attributes spreading is supported on this component.

ListItem::CopyItem

Name
copyItemTitle
Type
string
Name
text
Type
string
Required
Required
Description
The text to be copied. If no text value is defined an error will be thrown.

ListItem::Description

Here is the API for the "description" list item component (yielded in a hash under the key Description):

Name
text
Type
string
Required
Required
Description
The text to be used for the description. If no text value is defined an error will be thrown.
Name
…attributes
Description
...attributes spreading is supported on this component.

ListItem::Generic

Here is the API for the "generic" list item component (yielded in a hash under the key Generic):

Name
yield
Description
Elements passed as children of this sub-component are yielded inside the list item. Notice: when using the "generic" list item the developer is completely responsible for any element yielded, including the accessibility of that element, as well as the layout of the content (we provide only the horizontal padding for consistency with the other items).
Name
…attributes
Description
...attributes spreading is supported on this component.

ListItem::Interactive

Here is the API for the "interactive" list item component (yielded in a hash under the key Interactive):

Name
text
Type
string
Required
Required
Description
The text to be used in the item. If no text value is defined an error will be thrown.
Name
color
Type
enum
Values
  • action (default)
  • critical
Description
Acceptabe values:
Name
icon
Type
string
Description
Acceptable value: any Flight icon name.
Name
isLoading
Type
boolean
Description
This controls if the item is in "loading" state. Notice: when in this state, the item is not actually interactive, but you can pass the other expected arguments for the item (they're simply ignored).
Name
href
Description
This is the URL parameter that is passed down to the <a> element.
Name
isHrefExternal
Type
boolean
Description
This controls if the <a> link is external and so for security reasons we need to add the target="_blank" and rel="noopener noreferrer" attributes to it.
Name
route models model query current-when replace
Description
These are the parameters that are passed down as arguments to the <LinkTo/LinkToExternal> component.
Name
isRouteExternal
Type
boolean
Description
This controls if the "LinkTo" is external to the Ember engine (more details here) in which case it will use a <LinkToExternal> instead of a simple <LinkTo> for the @route.
Name
…attributes
Description
...attributes spreading is supported on this component. Important: in this specific component, the ...attributes are not spread on the root element of the component (an <li> element) but on the underlying element/component (<button>, <a>, <LinkTo> or <LinkToExternal> depending on the @route/@href arguments).

ListItem::Separator

Here is the API for the "separator" list item component (yielded in a hash under the key Separator):

Name
…attributes
Description
...attributes spreading is supported on this component.

ListItem::Title

Here is the API for the "description" list item component (yielded in a hash under the key Title):

Name
text
Type
string
Required
Required
Description
The text to be used for the title. If no text value is defined an error will be thrown.
Name
…attributes
Description
...attributes spreading is supported on this component.

Invocation

To make the invocation more intuitive for developers, we've provided contextual components for the toggles and list-item items. For example, <Hds::Dropdown::ListItem::Separator /> is yielded in a hash under the key <XX.Separator /> when invoked:

<Hds::Dropdown as |dd|>
  <dd.ToggleButton @text="..." />
  <dd.Title @text="Lorem ipsum" />
  <dd.Description @text="Lorem ipsum dolor sine qua non est." />
  <dd.Interactive @href="..." @text="Add" />
  <dd.Separator />
  <dd.Interactive @route="components" @icon="trash" @text="Delete" @color="critical" />
</Hds::Dropdown>

URLs and routes handling

The Interactive list item renders the correct element based on the passing of an @route, @href, or the addition of a click event (i.e., {{on "click" this.myAction}}).

Notice: the Interactive list item component internally uses the generic Hds::Interactive component. For more details about how this low-level component works please refer to its documentation page.

Basic use

If you don't pass a @href or @route argument a simple <button> will be generated:

...
<Hds::Dropdown as |dd|>
  ...
  <dd.Interactive {{on "click" this.myAction}} @text="Run command" />
</Hds::Dropdown>

Notice: in this case you will have to add your own event handling function to it.

With @href

If you pass a @href argument a <a> link will be generated:

...
<Hds::Dropdown as |dd|>
  ...
  <dd.Interactive @href="https://www.hashicorp.com/request-demo/terraform" @text="Request a demo" />
</Hds::Dropdown>

Important: when using the @href argument the component adds by default the attributes target="_blank" and rel="noopener noreferrer" to the <a> element (because this is the most common use case: internal links are generally handled using a @route argument). If the href points to an internal link, or uses a different protocol (eg. "mailto" of "ftp") you can pass @isHrefExternal=true to the component and it will not add the target and rel attributes (but you can pass yours if needed, using the ...attributes spreading. For more details see the Hds::Interactive component.

With @route

If you pass a @route argument a <a> link will be generated using a <LinkTo> Ember component:

...
<Hds::Dropdown as |dd|>
  ...
  <dd.Interactive @route="my.page.route" @model="my.page.model" @text="Activate cluster" />
</Hds::Dropdown>

Important: if the route is external to your current engine you have to pass also @isRouteExternal=true to the component so that it will use <LinkToExternal> instead of a simple <LinkTo> for the @route. For more details see the Hds::Interactive component

Notice: all the standard arguments for the <LinkTo/LinkToExternal> components are supported (eg. models/model/query/current-when/replace).

Examples

ToggleButton + ListItem, Separator

This example demonstrates the use of a dropdown with a toggle-button, links, a separator and a link (color, critical):

<Hds::Dropdown as |dd|>
  <dd.ToggleButton @text="Text Toggle" />
  <dd.Interactive @route="components" @text="Item One" />
  <dd.Interactive @route="components" @text="Item Two" />
  <dd.Interactive @route="components" @text="Item Three" />
  <dd.Interactive @text="Item Four (closes on click)" {{on "click" dd.close}} />
  <dd.Separator />
  <dd.Interactive @route="components" @text="Delete" @color="critical" @icon="trash" />
</Hds::Dropdown>

Rendered (positioned to the right):

ToggleButton + Title, Description, CopyItem, Separator

This example demonstrates the use of a dropdown with a toggle-button (color, secondary), title, description, a generic (which is yielding a Link::Standalone component), copy-item, a separator and a link (color, critical):

To indicate that a secondary button style should be used for the "button" toggle, add @color="secondary". If no @color is declared, primary will be used by default.

<Hds::Dropdown as |dd| >
  <dd.ToggleButton @text="Integrate with Terraform Cloud" @color="secondary" />
  <dd.Title @text="Integrate with Terraform Cloud" />
  <dd.Description @text="Create a new run task in Terraform using the URL and key below." />
  <dd.Generic>
    <Hds::Link::Standalone @text="Watch tutorial video" @icon="film" href="/" />
  </dd.Generic>
  <dd.CopyItem @text="https://api.cloud.hashicorp.com" @copyItemTitle="Endpoint URL" />
  <dd.CopyItem @text="91ee1e8ef65b337f0e70d793f456c71d" @copyItemTitle="HMAC Key" />
</Hds::Dropdown>

Rendered as secondary variation (positioned to the right):

Notice: when using the "generic" list item the developer is completely responsible for any element yielded, including the accessibility of that element, as well as the layout of the content (we provide only the horizontal padding for consistency with the other items).

ToggleIcon for "overflow" dropdown menus

Example: an "overflow" toggle for use only in a table element (per design). The dropdown has default and destructive (critical) links. This is the only use case where it is acceptable to use @hasChevron=false.

Note that toggleText is still required, because it supplies the aria-label for the toggle button.

<Hds::Dropdown as |dd|>
  <dd.ToggleIcon @icon="more-horizontal" @text="Overflow Options" @hasChevron={{false}} />
  <dd.Interactive @route="components" @text="Create" />
  <dd.Interactive @route="components" @text="Read" />
  <dd.Interactive @route="components" @text="Update" />
  <dd.Separator />
  <dd.Interactive @route="components" @text="Delete" @color="critical" @icon="trash" />
</Hds::Dropdown>

Rendered in a table cell:

Column A

Column B

Column C

Row 1, cell 1

Row 1, cell 2

Row 2, cell 1

Row 2, cell 2

Row 3, cell 1

Row 3, cell 2

Row 4, cell 1

Row 4, cell 2

With a loading "interactive" item

Example: there may be use cases when it's necessary to put an item in a "loading" state while the app performs some operations (eg. checking asynchronously the user's permission to execute a certain operation, once the toggle has been clicked).

In that case the argument @isLoading=true can be passed to the item: this will show a "loading" icon (even if an argument @icon is provided) and set the item as non-interactive until the value of @isLoading is set to false again.

<Hds::Dropdown as |dd|>
  <dd.ToggleIcon @icon="more-horizontal" @text="Overflow Options" @hasChevron={{false}} />
  <dd.Interactive @route="components" @isLoading={{true}} @text="Edit cluster" @color="action" @icon="edit" />
  <dd.Interactive @route="components" @text="Delete" @color="critical" @icon="trash" />
</Hds::Dropdown>

Rendered in a table cell:

ID

Status

Cluster ABC

Running

Cluster XYZ

Idle

ToggleIcon as user menu

In this example, we have a user icon with a title, description, separator, and links.

Note that toggleText is still required, because it supplies the aria-label for the toggle button.

<Hds::Dropdown as |dd|>
  <dd.ToggleIcon @icon="user" @text="user menu" />
  <dd.Title @text="Signed In" />
  <dd.Description @text="design-systems@hashicorp.com" />
  <dd.Separator />
  <dd.Interactive @route="components" @text="Settings and Preferences" />
  <dd.Interactive @route="components" @text="Delete" @color="critical" @icon="trash" />
</Hds::Dropdown>

Rendered as a toggle/icon for a user menu (positioned to the right):

Here is a customized example to demonstrate how that would look like in dark mode (not supported by the design system yet):

ToggleIcon with other icons

In this example, we have a settings icon with a title, description, separator, and links.

Note that toggleText is still required, because it supplies the aria-label for the toggle button.

<Hds::Dropdown as |dd|>
  <dd.ToggleIcon @icon="settings" @text="settings menu" />
  <dd.Title @text="Signed In" />
  <dd.Description @text="design-systems@hashicorp.com" />
  <dd.Separator />
  <dd.Interactive @route="components" @text="Settings and Preferences" />
  <dd.Interactive @route="components" @text="Delete" @color="critical" @icon="trash" />
</Hds::Dropdown>

Rendered (positioned to the right):

Toggles

Toggle::Button
primary
secondary

primary small
secondary small
Toggle::Icon
with icon + chevron
icon only
with image (avatar)
States
primary/open:
secondary/open:
icon/open:
icon+chevron/open:
avatar+chevron/open:

List Items

Title / Description / Separator
default (min width)
  • A simple title
  • A description.
  • Item
default (max width)
fixed width
Interactive
Generated element
default ⇒ <button>
with @href<a>
with @route<LinkTo><a>
Colors
States (in each color)
Generic
  • some generic content here
CopyItem
Content
base
with copyItemTitle
  • Lorem ipsum dolor
States

When to use

  • To display a list of actions under a single button toggle.

When not to use

  • In forms, when needing to select one or more options, use a Select
  • When selecting an option results in immediate navigation or update to the page contentx, use a Context Switcher (coming soon)

Dropdown

Anatomy

Dropdown anatomy

Toggle

Required

List

Required, but only visible when open


Toggle

Anatomy

Button

Toggle button anatomy

Icon

Toggle icon anatomy

Toggle avatar anatomy

Toggle icon only anatomy


States

Button

Example of dropdown button states

Icon

Example of dropdown icon states

Banner (highlight): A note on disabled states: Because disabled states completely remove the interactive function of an element, it can be challenging for a user to understand why it has been disabled and/or why they cannot interact with that element. In an effort to avoid this confusion, we opt for using methods like enabling or hiding the element and, thus, are not offering a disabled state for the Dropdown Toggle. Read more about when to enable vs hide


Size

ToggleButtons come in a Medium and Small size to allow for placement in ButtonSets with buttons of the same size.

Medium

Small
--- ## Chevron Open toggles use a chevron pointing up, while closed toggles use a chevron pointing down. _Banner (highlight):_ Set `isOpen=true` when displaying the toggle with a menu and `isOpen=false` when displaying the toggle. ### Open
### Closed
### ToggleIcon Chevrons provide a stronger signifier that ToggleIcon's open the list, so they're required for all variations except thos used for the more icon.
--- _List_ ## Placement ### Right (default) The list will align to the right side of the toggle and will be placed 4px below the toggle. ![Right placement example](/assets/components/dropdown/dropdown-placement-right_example-a671d8625cb526f1cba28ac3fde10f1c.png) ### Left In the event that the toggle is positioned on the left side of the screen, the list can be aligned to the left side to fit more appropriately within the UI. ![Left placement example](/assets/components/dropdown/dropdown-placement-left_example-bc74fd730a59197ecff2a5a390bdf48e.png) --- ## Size ### Default ("fluid") width The default List has a min-width of 200px and a max-width of 400px. This means if there's a list item with a lot of text (ie. Description), the list will automatically expand up to 400px to accommodate the contentx of the widest list item.
### Fixed width If you do not want the width of the List to expand automatically to accommodate the widest list item, we offer a Fixed width list. As a best practice we do not recommend lists wider than 400px.
  • Consul version v1.10.6
  • Import to Terraform
  • Copy and run this command in Terraform to import and manage this resource via our Terraform Provider
### Height The height of the list container is based on the contents within the list. The list will no scroll.
  • Integrate with Terraform Cloud
  • Create a new run task in Terraform using the URL and key below.
  • Endpoint URL
  • HMAC Key
  • Manage
--- _ListItem_ ## Anatomy ![ListItem Anatomy](/assets/components/dropdown/dropdown-list_item-anatomy-135710c691f3e041ab49ce6c91b7e926.png) #### Text Required #### Icon Required for Critical ListItems. Optional otherwise. #### Indicator Visible in hover and active state #### Focus ring Visible in focus state --- ## Type ![Dropdown ListItem types](/assets/components/dropdown/dropdown-list_item-types-cc933079df2dcaa0b36ef379886a0454.png) _Banner (highlight):_ **A note on loading** Users may not understand why something is taking additional time to load. If possible, determine what should be displayed prior to the user opening the dropdown (ie. on page load). If that is not possible, you could consider providing a more informative loading message, such as “Checking permissions”. --- ## States
_Banner (highlight):_ **A note on disabled states** Because disabled states completely remove the interactive functionality of an element, it can be challenging for a user to understand why it has been disabled and/or why they cannot interact with that element. In an effort to avoid this confusion, we opt for using methods like enabling or hiding the element and, thus, are not offering a disabled state for the ListItems. [Read more about when to enable vs hide.](https://docs.google.com/document/d/1fqsXjjPnz5HK2NcY1buh5RcI5S6XCgQwfr8GP3kClv0/edit#heading=h.52ub6bvbvcb7) --- ## Icons Icons in ListItems are **optional.** We recommend letting the text speak for itself unless an icon provides additional value. Ask yourself... "Which icon should I use here?" If the answer isn't obvious within 5 seconds, consider whether the icons is really providing additional value.
  • About
  • Automate with Terraform
### Critical While icons are optional, we do recommend using a relevant icon for Critical ListItems. Using the right icon provides a stronger affordance that the action is destructive. See the section on [color blind users and critical actions](https://www.figma.com/file/8I4u10OyhYZIea4MpXwJwm/Design-guidelines-migration?node-id=7192%3A13227) for more details about making these actions more accessible.
--- ## Content ### General We recommend only using dropdowns to display a list of links or actions. A dropdown should not be a catch-all used to squeeze a lot of content into a small, contained area. The more content (especially non-ListItem content) added to a dropdown, the more challenging it can be for a user to quickly parse, and the more likely it is for the dropdown to become less accessible. When finding that you need to add more custom content to the dropdown, please [reach out the HDS team](https://hashicorp.slack.com/archives/C7KTUHNUS) to discuss alternative options. Text can wrap or the list can expand to accommodate the text, up to 400px. Review size guidelines to learn more about resizing the list. ### ListItems There is no character limit for interactive ListItems but we recommend keeping them short and concise (~36 characters before needing to expand the width of the list). --- _CopyItem_ _Banner (warning):_ **CopyItem** is a temporary built-in component. It was built so we could support existing use cases found during the audit. We will eventually be tackling this as its own component, at which point, the design and functionality are likely to change, so we don’t recommend using it outside of the dropdown component. ## Anatomy ![Dropdown CopyItem anatomy](/assets/components/dropdown/dropdown-copy_item-anatomy-d725d6551687452675c49c0a09310114.png) #### Title Optional #### Text Required #### Copy icon Required #### Focus ring Visible in focus state --- ## States
--- ## Accessibility Color blind users (specifically those with [Achromatopsia](https://en.wikipedia.org/wiki/Achromatopsia)) may have a hard time perceiving Critical ListItems within our dropdown. To provide a more accessible experience, we recommend: - Using strong, clear language for the text (ie. “Delete...”, “Revoke...”, etc) - Adding a relevant icon - Moving the Critical ListItem to the bottom of the list or the section - If at the bottom of a list, consider adding a separator above the Critical ListItem to help separate it from other ListItems - Adding a second confirmation layer after the user clicks “Delete” (ie. showing a confirmation modal that requires the user to type “Delete” into a field before proceeding)
### Keyboard navigation [Many types of users](https://webaim.org/techniques/keyboard/) rely on their keyboard to navigate the web, so it's important that we annotate the [focus order](https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html#:~:text=3%3A%20Focus%20Order-,Success%20Criterion%202.4.,that%20preserves%20meaning%20and%20operability) (how keyboard users navigate the web) to ensure we're providing them with a natural path and great experience. _Banner (highlight):_ Focus order annotated with Figma plugin, [A11y Focus Order](https://www.figma.com/community/plugin/731310036968334777/A11y---Focus-Orderer). ![Dropdown focus order](/assets/components/dropdown/dropdown-focus_order-01-1f97e5521cc7e5009baa52d7354977a8.png) ![Dropdown focus order](/assets/components/dropdown/dropdown-focus_order-02-dc806f2378ceae4656292666b3d09a50.png) ![Dropdown focus order](/assets/components/dropdown/dropdown-focus_order-03-f1ca6670a71d2e91c571ec4e205cf1ff.png) ### Known issuesd **ToggleIcon/Icon/No Chevron:** doesn't provide enough affordance; it's not quickly seen as actionable, but we intend to tackle this when working on the table component.

This component has been designed and implemented with accessibility in mind. When used as recommended, there should not be any accessibility issues with this component.

Known Potential Accessibility Issues

The following are known potential issues, and developers should keep these in mind when implementing this component:

  1. In any instance where data truncation occurs, there is no current method to access that data for the keyboard-only user. Therefore, we do not recommend allowing data to be truncated within this component.
  2. If the "overflow" toggle is used in the incorrect context, it will fail on perceivability. It must be used in in the current recommended context of a data table. For the best user experience, interactive elements should visually present as interactive, unless their context otherwise makes it clear that they are/should be interactive.

Applicable WCAG Success Criteria (Reference)

This section is for reference only. This component intends to conform to the following WCAG success criteria:

  • 1.3.1 Info and Relationships (Level A):
    Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.
  • 1.3.2 Meaningful Sequence (Level A):
    When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined.
  • 1.4.1 Use of Color (Level A):
    Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.
  • 1.4.10 Reflow (Level AA):
    Content can be presented without loss of information or functionality, and without requiring scrolling in two dimensions.
  • 1.4.11 Non-text Contrast (Level AA):
    The visual presentation of the following have a contrast ratio of at least 3:1 against adjacent color(s): user interface components; graphical objects.
  • 1.4.12 Text Spacing (Level AA):
    No loss of content or functionality occurs by setting all of the following and by changing no other style property: line height set to 1.5; spacing following paragraphs set to at least 2x the font size; letter-spacing set at least 0.12x of the font size, word spacing set to at least 0.16 times the font size.
  • 1.4.3 Minimum Contrast (Level AA):
    The visual presentation of text and images of text has a contrast ratio of at least 4.5:1
  • 1.4.4 Resize Text (Level AA):
    Except for captions and images of text, text can be resized without assistive technology up to 200 percent without loss of content or functionality.
  • 2.1.1 Keyboard (Level A):
    All functionality of the content is operable through a keyboard interface.
  • 2.1.2 No Keyboard Trap (Level A):
    If keyboard focus can be moved to a component of the page using a keyboard interface, then focus can be moved away from that component using only a keyboard interface.
  • 2.4.3 Focus Order (Level A):
    If a Web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.
  • 2.4.7 Focus Visible (Level AA):
    Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible.