From fbc730aeec36f5a4e7b8817ada7a17dffc55817f Mon Sep 17 00:00:00 2001 From: michael-n-cooper Date: Wed, 1 Jul 2020 00:44:21 +0000 Subject: [PATCH] Generated by TRAVIS-CI 50750259ec94396d03b7dcbbe733caccda0304bb Add example of select-only combobox (pull #1396) Resolves issue #1026 by adding a new select-only combobox example. Co-authored-by: Valerie Young Co-authored-by: Matt King Co-authored-by: Carolyn MacLeod --- .../combobox/combobox-autocomplete-both.html | 1 + .../combobox/combobox-autocomplete-list.html | 1 + .../combobox/combobox-autocomplete-none.html | 1 + examples/combobox/combobox-select-only.html | 417 ++++++++++++++++++ examples/combobox/css/select-only.css | 103 +++++ examples/combobox/grid-combo.html | 1 + examples/combobox/js/select-only.js | 372 ++++++++++++++++ examples/index.html | 20 +- examples/js/app.js | 56 +++ examples/js/notice.html | 28 ++ index.html | 11 +- 11 files changed, 1005 insertions(+), 6 deletions(-) create mode 100644 examples/combobox/combobox-select-only.html create mode 100644 examples/combobox/css/select-only.css create mode 100644 examples/combobox/js/select-only.js create mode 100644 examples/js/notice.html diff --git a/examples/combobox/combobox-autocomplete-both.html b/examples/combobox/combobox-autocomplete-both.html index c3153415ef..09f1d6cbd3 100644 --- a/examples/combobox/combobox-autocomplete-both.html +++ b/examples/combobox/combobox-autocomplete-both.html @@ -39,6 +39,7 @@

Editable Combobox With Both List and Inline Autocomplete Example

Similar examples include:

    +
  • Select-Only Combobox: A single-select combobox with no text input that is functionally similar to an HTML select element.
  • Editable Combobox with List Autocomplete: An editable combobox that demonstrates the autocomplete behavior known as list with manual selection.
  • Editable Combobox Without Autocomplete: An editable combobox that demonstrates the behavior associated with aria-autocomplete=none.
  • Editable Combobox with Grid Popup: An editable combobox that presents suggestions in a grid, enabling users to navigate descriptive information about each suggestion.
  • diff --git a/examples/combobox/combobox-autocomplete-list.html b/examples/combobox/combobox-autocomplete-list.html index 3be610cd02..557518f36a 100644 --- a/examples/combobox/combobox-autocomplete-list.html +++ b/examples/combobox/combobox-autocomplete-list.html @@ -39,6 +39,7 @@

    Editable Combobox With List Autocomplete Example

    Similar examples include:

      +
    • Select-Only Combobox: A single-select combobox with no text input that is functionally similar to an HTML select element.
    • Editable Combobox with Both List and Inline Autocomplete: An editable combobox that demonstrates the autocomplete behavior known as list with inline autocomplete.
    • Editable Combobox Without Autocomplete: An editable combobox that demonstrates the behavior associated with aria-autocomplete=none.
    • Editable Combobox with Grid Popup: An editable combobox that presents suggestions in a grid, enabling users to navigate descriptive information about each suggestion.
    • diff --git a/examples/combobox/combobox-autocomplete-none.html b/examples/combobox/combobox-autocomplete-none.html index 35d05c1420..4ff2577dd2 100644 --- a/examples/combobox/combobox-autocomplete-none.html +++ b/examples/combobox/combobox-autocomplete-none.html @@ -36,6 +36,7 @@

      Editable Combobox without Autocomplete Example

      Similar examples include:

        +
      • Select-Only Combobox: A single-select combobox with no text input that is functionally similar to an HTML select element.
      • Editable Combobox with Both List and Inline Autocomplete: An editable combobox that demonstrates the autocomplete behavior known as list with inline autocomplete.
      • Editable Combobox with List Autocomplete: An editable combobox that demonstrates the autocomplete behavior known as list with manual selection.
      • Editable Combobox with Grid Popup: An editable combobox that presents suggestions in a grid, enabling users to navigate descriptive information about each suggestion.
      • diff --git a/examples/combobox/combobox-select-only.html b/examples/combobox/combobox-select-only.html new file mode 100644 index 0000000000..64fe2b9337 --- /dev/null +++ b/examples/combobox/combobox-select-only.html @@ -0,0 +1,417 @@ + + + + +Select-Only Combobox Example | WAI-ARIA Authoring Practices 1.2 + + + + + + + + + + + + + + +
        +

        Select-Only Combobox Example

        +

        + The following example implementation of the ARIA design pattern for combobox + demonstrates a single-select combobox widget that is functionally similar to an HTML select element. + Unlike the editable combobox examples, this select-only combobox is not made with an <input> element, and it does not accept freeform user input. + However, like an HTML <select>, users can type characters to select matching options. +

        +

        Similar examples include:

        + + +
        +

        Example

        + +
        + +
        + +
        + + +
        +
        +
        + +
        + +
        +

        Keyboard Support

        +

        + The example combobox on this page implements the following keyboard interface. + Other variations and options for the keyboard interface are described in the + Keyboard Interaction section of the combobox design pattern. +

        +

        Closed Combobox

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        KeyFunction
        Down Arrow +
          +
        • Opens the listbox if it is not already displayed without moving focus or changing selection.
        • +
        • DOM focus remains on the combobox.
        • +
        +
        Alt + Down Arrow + Opens the listbox without moving focus or changing selection. +
        Up Arrow +
          +
        • First opens the listbox if it is not already displayed and then moves visual focus to the first option.
        • +
        • DOM focus remains on the combobox.
        • +
        +
        Enter + Opens the listbox without moving focus or changing selection. +
        Space + Opens the listbox without moving focus or changing selection. +
        Home + Opens the listbox and moves visual focus to the first option. +
        End + Opens the listbox and moves visual focus to the last option. +
        Printable Characters +
          +
        • First opens the listbox if it is not already displayed and then moves visual focus to the first option that matches the typed character.
        • +
        • If multiple keys are typed in quick succession, visual focus moves to the first option that matches the full string.
        • +
        • If the same character is typed in succession, visual focus cycles among the options starting with that character
        • +
        +
        +

        Listbox Popup

        +

        + NOTE: When visual focus is in the listbox, DOM focus remains on the combobox and the value of aria-activedescendant on the combobox is set to a value that refers to the listbox option that is visually indicated as focused. + Where the following descriptions of keyboard commands mention focus, they are referring to the visual focus indicator. + For more information about this focus management technique, see + Using aria-activedescendant to Manage Focus. +

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        KeyFunction
        Enter +
          +
        • Sets the value to the content of the focused option in the listbox.
        • +
        • Closes the listbox.
        • +
        • Sets visual focus on the combobox.
        • +
        +
        Space +
          +
        • Sets the value to the content of the focused option in the listbox.
        • +
        • Closes the listbox.
        • +
        • Sets visual focus on the combobox.
        • +
        +
        Tab +
          +
        • Sets the value to the content of the focused option in the listbox.
        • +
        • Closes the listbox.
        • +
        • Performs the default action, moving focus to the next focusable element. + Note: the native <select> element closes the listbox but does not move focus on tab. + This pattern matches the behavior of the other comboboxes rather than the native element in this case.
        • +
        +
        Escape +
          +
        • Closes the listbox.
        • +
        • Sets visual focus on the combobox.
        • +
        +
        Down Arrow +
          +
        • Moves visual focus to the next option.
        • +
        • If visual focus is on the last option, visual focus does not move.
        • +
        +
        Up Arrow +
          +
        • Moves visual focus to the previous option.
        • +
        • If visual focus is on the first option, visual focus does not move.
        • +
        +
        Alt + Up Arrow +
          +
        • Sets the value to the content of the focused option in the listbox.
        • +
        • Closes the listbox.
        • +
        • Sets visual focus on the combobox.
        • +
        +
        HomeMoves visual focus to the first option.
        EndMoves visual focus to the last option.
        PageUpJumps visual focus up 10 options (or to first option).
        PageDownJumps visual focus down 10 options (or to last option).
        Printable Characters +
          +
        • First opens the listbox if it is not already displayed and then moves visual focus to the first option that matches the typed character.
        • +
        • If multiple keys are typed in quick succession, visual focus moves to the first option that matches the full string.
        • +
        • If the same character is typed in succession, visual focus cycles among the options starting with that character
        • +
        +
        +
        + +
        +

        Role, Property, State, and Tabindex Attributes

        +

        + The example combobox on this page implements the following ARIA roles, states, and properties. + Information about other ways of applying ARIA roles, states, and properties is available in the + Roles, States, and Properties section of the combobox design pattern. +

        +

        Combobox

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        RoleAttributeElementUsage
        + combobox + divIdentifies the input as a combobox.
        + aria-labelledby="#IDREF" + divIdentifies the element that labels the combobox.
        + aria-controls="#IDREF" + divIdentifies the element that serves as the popup.
        + aria-expanded="false" + divIndicates that the popup element is not displayed.
        + aria-expanded="true" + divIndicates that the popup element is displayed.
        + aria-activedescendant="IDREF" + div +
          +
        • When an option in the listbox is visually indicated as having keyboard focus, refers to that option.
        • +
        • When navigation keys, such as Down Arrow, are pressed, the JavaScript changes the value.
        • +
        • Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the input element.
        • +
        • + For more information about this focus management technique, see + Using aria-activedescendant to Manage Focus. +
        • +
        +
        +

        Listbox Popup

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        RoleAttributeElementUsage
        + listbox + + div + Identifies the element as a listbox.
        + option + div +
          +
        • Identifies the element as a listbox option.
        • +
        • The text content of the element provides the accessible name of the option.
        • +
        +
        + aria-selected="true" + li +
          +
        • Specified on an option in the listbox when it is visually highlighted as selected.
        • +
        • Occurs only when an option in the list is referenced by aria-activedescendant.
        • +
        +
        +
        + +
        +

        Javascript and CSS Source Code

        + +
        + +
        +

        HTML Source Code

        + +
        + + +
        +
        + + + diff --git a/examples/combobox/css/select-only.css b/examples/combobox/css/select-only.css new file mode 100644 index 0000000000..6eeaf22e7a --- /dev/null +++ b/examples/combobox/css/select-only.css @@ -0,0 +1,103 @@ +.combo *, +.combo *::before, +.combo *::after { + box-sizing: border-box; +} + +.combo { + display: block; + margin-bottom: 1.5em; + max-width: 400px; + position: relative; +} + +.combo::after { + border-bottom: 2px solid rgba(0, 0, 0, 0.75); + border-right: 2px solid rgba(0, 0, 0, 0.75); + content: ''; + display: block; + height: 12px; + pointer-events: none; + position: absolute; + right: 16px; + top: 50%; + transform: translate(0, -65%) rotate(45deg); + width: 12px; +} + +.combo-input { + background-color: #f5f5f5; + border: 2px solid rgba(0, 0, 0, 0.75); + border-radius: 4px; + display: block; + font-size: 1em; + min-height: calc(1.4em + 26px); + padding: 12px 16px 14px; + text-align: left; + width: 100%; +} + +.open .combo-input { + border-radius: 4px 4px 0 0; +} + +.combo-input:focus { + border-color: #0067b8; + box-shadow: 0 0 4px 2px #0067b8; + outline: 4px solid transparent; +} + +.combo-label { + display: block; + font-size: 20px; + font-weight: 100; + margin-bottom: 0.25em; +} + +.combo-menu { + background-color: #f5f5f5; + border: 1px solid rgba(0, 0, 0, 0.75); + border-radius: 0 0 4px 4px; + display: none; + max-height: 300px; + overflow-y:scroll; + left: 0; + position: absolute; + top: 100%; + width: 100%; + z-index: 100; +} + +.open .combo-menu { + display: block; +} + +.combo-option { + padding: 10px 12px 12px; +} + +.combo-option:hover { + background-color: rgba(0, 0, 0, 0.1); +} + +.combo-option.option-current { + outline: 3px solid #0067b8; + outline-offset: -3px; +} + +.combo-option[aria-selected="true"] { + padding-right: 30px; + position: relative; +} + +.combo-option[aria-selected="true"]::after { + border-bottom: 2px solid #000; + border-right: 2px solid #000; + content: ''; + height: 16px; + position: absolute; + right: 15px; + top: 50%; + transform: translate(0, -50%) rotate(45deg); + width: 8px; +} \ No newline at end of file diff --git a/examples/combobox/grid-combo.html b/examples/combobox/grid-combo.html index 140def9c9e..215a0d7fd6 100644 --- a/examples/combobox/grid-combo.html +++ b/examples/combobox/grid-combo.html @@ -44,6 +44,7 @@

        Editable Combobox with Grid Popup Example

        Similar examples include:

          +
        • Select-Only Combobox: A single-select combobox with no text input that is functionally similar to an HTML select element.
        • Editable Combobox with Both List and Inline Autocomplete: An editable combobox that demonstrates the autocomplete behavior known as list with inline autocomplete.
        • Editable Combobox with List Autocomplete: An editable combobox that demonstrates the autocomplete behavior known as list with manual selection.
        • Editable Combobox Without Autocomplete: An editable combobox that demonstrates the behavior associated with aria-autocomplete=none.
        • diff --git a/examples/combobox/js/select-only.js b/examples/combobox/js/select-only.js new file mode 100644 index 0000000000..2c18abed80 --- /dev/null +++ b/examples/combobox/js/select-only.js @@ -0,0 +1,372 @@ +/* +* This content is licensed according to the W3C Software License at +* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document +*/ + +'use strict'; + +// Save a list of named combobox actions, for future readability +const SelectActions = { + Close: 0, + CloseSelect: 1, + First: 2, + Last: 3, + Next: 4, + Open: 5, + PageDown: 6, + PageUp: 7, + Previous: 8, + Select: 9, + Type: 10 +} + +/* + * Helper functions + */ + +// filter an array of options against an input string +// returns an array of options that begin with the filter string, case-independent +function filterOptions(options = [], filter, exclude = []) { + return options.filter((option) => { + const matches = option.toLowerCase().indexOf(filter.toLowerCase()) === 0; + return matches && exclude.indexOf(option) < 0; + }); +} + +// map a key press to an action +function getActionFromKey(event, menuOpen) { + const { key, altKey, ctrlKey, metaKey } = event; + const openKeys = ['ArrowDown', 'ArrowUp', 'Enter', ' ']; // all keys that will do the default open action + // handle opening when closed + if (!menuOpen && openKeys.includes(key)) { + return SelectActions.Open; + } + + // home and end move the selected option when open or closed + if (key === 'Home') { + return SelectActions.First; + } + if (key === 'End') { + return SelectActions.Last; + } + + // handle typing characters when open or closed + if (key === 'Backspace' || key === 'Clear' || (key.length === 1 && key !== ' ' && !altKey && !ctrlKey && !metaKey)) { + return SelectActions.Type; + } + + // handle keys when open + if (menuOpen) { + if (key === 'ArrowUp' && altKey) { + return SelectActions.CloseSelect; + } + else if (key === 'ArrowDown' && !altKey) { + return SelectActions.Next; + } + else if (key === 'ArrowUp') { + return SelectActions.Previous; + } + else if (key === 'PageUp') { + return SelectActions.PageUp; + } + else if (key === 'PageDown') { + return SelectActions.PageDown; + } + else if (key === 'Escape') { + return SelectActions.Close; + } + else if (key === 'Enter' || key === ' ') { + return SelectActions.CloseSelect; + } + } +} + +// return the index of an option from an array of options, based on a search string +// if the filter is multiple iterations of the same letter (e.g "aaa"), then cycle through first-letter matches +function getIndexByLetter(options, filter, startIndex = 0) { + const orderedOptions = [...options.slice(startIndex), ...options.slice(0, startIndex)]; + const firstMatch = filterOptions(orderedOptions, filter)[0]; + const allSameLetter = (array) => array.every((letter) => letter === array[0]); + + // first check if there is an exact match for the typed string + if (firstMatch) { + return options.indexOf(firstMatch); + } + + // if the same letter is being repeated, cycle through first-letter matches + else if (allSameLetter(filter.split(''))) { + const matches = filterOptions(orderedOptions, filter[0]); + return options.indexOf(matches[0]); + } + + // if no matches, return -1 + else { + return -1; + } +} + +// get an updated option index after performing an action +function getUpdatedIndex(currentIndex, maxIndex, action) { + const pageSize = 10; // used for pageup/pagedown + + switch(action) { + case SelectActions.First: + return 0; + case SelectActions.Last: + return maxIndex; + case SelectActions.Previous: + return Math.max(0, currentIndex - 1); + case SelectActions.Next: + return Math.min(maxIndex, currentIndex + 1); + case SelectActions.PageUp: + return Math.max(0, currentIndex - pageSize); + case SelectActions.PageDown: + return Math.min(maxIndex, currentIndex + pageSize); + default: + return currentIndex; + } +} + +// check if an element is currently scrollable +function isScrollable(element) { + return element && element.clientHeight < element.scrollHeight; +} + +// ensure a given child element is within the parent's visible scroll area +// if the child is not visible, scroll the parent +function maintainScrollVisibility(activeElement, scrollParent) { + const { offsetHeight, offsetTop } = activeElement; + const { offsetHeight: parentOffsetHeight, scrollTop } = scrollParent; + + const isAbove = offsetTop < scrollTop; + const isBelow = (offsetTop + offsetHeight) > (scrollTop + parentOffsetHeight); + + if (isAbove) { + scrollParent.scrollTo(0, offsetTop); + } + else if (isBelow) { + scrollParent.scrollTo(0, offsetTop - parentOffsetHeight + offsetHeight); + } +} + +/* + * Select Component + * Accepts a combobox element and an array of string options + */ +const Select = function(el, options = []) { + // element refs + this.el = el; + this.comboEl = el.querySelector('[role=combobox]'); + this.listboxEl = el.querySelector('[role=listbox]'); + + // data + this.idBase = this.comboEl.id || 'combo'; + this.options = options; + + // state + this.activeIndex = 0; + this.open = false; + this.searchString = ''; + this.searchTimeout = null; + + // init + if (el && this.comboEl && this.listboxEl) { + this.init(); + } +} + +Select.prototype.init = function() { + // select first option by default + this.comboEl.innerHTML = this.options[0]; + + // add event listeners + this.comboEl.addEventListener('blur', this.onComboBlur.bind(this)); + this.comboEl.addEventListener('click', this.onComboClick.bind(this)); + this.comboEl.addEventListener('keydown', this.onComboKeyDown.bind(this)); + + // create options + this.options.map((option, index) => { + const optionEl = this.createOption(option, index); + this.listboxEl.appendChild(optionEl); + }); +} + +Select.prototype.createOption = function(optionText, index) { + const optionEl = document.createElement('div'); + optionEl.setAttribute('role', 'option'); + optionEl.id = `${this.idBase}-${index}`; + optionEl.className = index === 0 ? 'combo-option option-current' : 'combo-option'; + optionEl.setAttribute('aria-selected', `${index === 0}`); + optionEl.innerText = optionText; + + optionEl.addEventListener('click', (event) => { + event.stopPropagation(); + this.onOptionClick(index); + }); + optionEl.addEventListener('mousedown', this.onOptionMouseDown.bind(this)); + + return optionEl; +} + +Select.prototype.getSearchString = function(char) { + // reset typing timeout and start new timeout + // this allows us to make multiple-letter matches, like a native select + if (typeof this.searchTimeout === 'number') { + window.clearTimeout(this.searchTimeout); + } + + this.searchTimeout = window.setTimeout(() => { + this.searchString = ''; + }, 500); + + // add most recent letter to saved search string + this.searchString += char; + return this.searchString; +} + +Select.prototype.onComboBlur = function() { + // do not do blur action if ignoreBlur flag has been set + if (this.ignoreBlur) { + this.ignoreBlur = false; + return; + } + + // select current option and close + if (this.open) { + this.selectOption(this.activeIndex); + this.updateMenuState(false, false); + } +} + +Select.prototype.onComboClick = function() { + this.updateMenuState(!this.open, false); +} + +Select.prototype.onComboKeyDown = function(event) { + const { key } = event; + const max = this.options.length - 1; + + const action = getActionFromKey(event, this.open); + + switch(action) { + case SelectActions.Last: + case SelectActions.First: + this.updateMenuState(true); + // intentional fallthrough + case SelectActions.Next: + case SelectActions.Previous: + case SelectActions.PageUp: + case SelectActions.PageDown: + event.preventDefault(); + return this.onOptionChange(getUpdatedIndex(this.activeIndex, max, action)); + case SelectActions.CloseSelect: + event.preventDefault(); + this.selectOption(this.activeIndex); + // intentional fallthrough + case SelectActions.Close: + event.preventDefault(); + return this.updateMenuState(false); + case SelectActions.Type: + return this.onComboType(key); + case SelectActions.Open: + event.preventDefault(); + return this.updateMenuState(true); + } +} + +Select.prototype.onComboType = function(letter) { + // open the listbox if it is closed + this.updateMenuState(true); + + // find the index of the first matching option + const searchString = this.getSearchString(letter); + const searchIndex = getIndexByLetter(this.options, searchString, this.activeIndex + 1); + + // if a match was found, go to it + if (searchIndex >= 0) { + this.onOptionChange(searchIndex); + } + // if no matches, clear the timeout and search string + else { + window.clearTimeout(this.searchTimeout); + this.searchString = ''; + } +} + +Select.prototype.onOptionChange = function(index) { + // update state + this.activeIndex = index; + + // update aria-activedescendant + this.comboEl.setAttribute('aria-activedescendant', `${this.idBase}-${index}`); + + // update active option styles + const options = this.el.querySelectorAll('[role=option]'); + [...options].forEach((optionEl) => { + optionEl.classList.remove('option-current'); + }); + options[index].classList.add('option-current'); + + // ensure the new option is in view + if (isScrollable(this.listboxEl)) { + maintainScrollVisibility(options[index], this.listboxEl); + } +} + +Select.prototype.onOptionClick = function(index) { + this.onOptionChange(index); + this.selectOption(index); + this.updateMenuState(false); +} + +Select.prototype.onOptionMouseDown = function() { + // Clicking an option will cause a blur event, + // but we don't want to perform the default keyboard blur action + this.ignoreBlur = true; +} + +Select.prototype.selectOption = function(index) { + // update state + this.activeIndex = index; + + // update displayed value + const selected = this.options[index]; + this.comboEl.innerHTML = selected; + + // update aria-selected + const options = this.el.querySelectorAll('[role=option]'); + [...options].forEach((optionEl) => { + optionEl.setAttribute('aria-selected', 'false'); + }); + options[index].setAttribute('aria-selected', 'true'); +} + +Select.prototype.updateMenuState = function(open, callFocus = true) { + if (this.open === open) { + return; + } + + // update state + this.open = open; + + // update aria-expanded and styles + this.comboEl.setAttribute('aria-expanded', `${open}`); + open ? this.el.classList.add('open') : this.el.classList.remove('open'); + + // update activedescendant + const activeID = open ? `${this.idBase}-${this.activeIndex}` : ''; + this.comboEl.setAttribute('aria-activedescendant', activeID); + + // move focus back to the combobox, if needed + callFocus && this.comboEl.focus(); +} + +// init select +window.addEventListener('load', function () { + const options = ['Choose a Fruit', 'Apple', 'Banana', 'Blueberry', 'Boysenberry', 'Cherry', 'Cranberry', 'Durian', 'Eggplant', 'Fig', 'Grape', 'Guava', 'Huckleberry']; + const selectEls = document.querySelectorAll('.js-select'); + + selectEls.forEach((el) => { + new Select(el, options); + }); +}); \ No newline at end of file diff --git a/examples/index.html b/examples/index.html index 41c52a9711..7d1ae9e3c0 100644 --- a/examples/index.html +++ b/examples/index.html @@ -80,6 +80,7 @@

          Examples by Role

        • Editable Combobox With Both List and Inline Autocomplete
        • Editable Combobox With List Autocomplete
        • Editable Combobox without Autocomplete
        • +
        • Select-Only Combobox
        • Editable Combobox with Grid Popup
        @@ -148,6 +149,7 @@

        Examples by Role

      • Editable Combobox With Both List and Inline Autocomplete
      • Editable Combobox With List Autocomplete
      • Editable Combobox without Autocomplete
      • +
      • Select-Only Combobox
      • Collapsible Dropdown Listbox
      • Listbox with Grouped Options
      • Listboxes with Rearrangeable Options
      • @@ -225,6 +227,7 @@

        Examples by Role

      • Editable Combobox With Both List and Inline Autocomplete
      • Editable Combobox With List Autocomplete
      • Editable Combobox without Autocomplete
      • +
      • Select-Only Combobox
      @@ -298,6 +301,7 @@

      Examples by Role

      tab @@ -311,6 +315,7 @@

      Examples by Role

      tablist @@ -320,6 +325,7 @@

      Examples by Role

      tabpanel @@ -374,6 +380,7 @@

      Examples By Properties and States

    • Editable Combobox With Both List and Inline Autocomplete
    • Editable Combobox With List Autocomplete
    • Editable Combobox without Autocomplete
    • +
    • Select-Only Combobox
    • Editable Combobox with Grid Popup
    • Collapsible Dropdown Listbox
    • Listbox with Grouped Options
    • @@ -421,10 +428,12 @@

      Examples By Properties and States