From a842b9479fc5d473f576cc9ffa5e5e79bd8a2f17 Mon Sep 17 00:00:00 2001 From: Matt King Date: Mon, 9 Sep 2019 16:07:01 -0700 Subject: [PATCH 01/54] Initial draft of 1.2 proposal --- .../combobox/combobox-autocomplete-list.html | 460 ++++++++++++++++++ examples/combobox/css/combobox.css | 36 ++ examples/combobox/css/listbox.css | 60 +++ examples/combobox/js/combobox-list.js | 377 ++++++++++++++ examples/combobox/js/listbox.js | 226 +++++++++ examples/combobox/js/listboxOption.js | 42 ++ 6 files changed, 1201 insertions(+) create mode 100644 examples/combobox/combobox-autocomplete-list.html create mode 100644 examples/combobox/css/combobox.css create mode 100644 examples/combobox/css/listbox.css create mode 100644 examples/combobox/js/combobox-list.js create mode 100644 examples/combobox/js/listbox.js create mode 100644 examples/combobox/js/listboxOption.js diff --git a/examples/combobox/combobox-autocomplete-list.html b/examples/combobox/combobox-autocomplete-list.html new file mode 100644 index 0000000000..851fbb0a9a --- /dev/null +++ b/examples/combobox/combobox-autocomplete-list.html @@ -0,0 +1,460 @@ + + + + +Combobox With List Autocomplete Example | WAI-ARIA Authoring Practices 1.1 + + + + + + + + + + + + + + + + + +
+

Combobox With List Autocomplete Example

+

+ The below combobox for choosing the name of a US state or territory demonstrates the + ARIA 1.0 design pattern for combobox. + The design pattern describes four types of autocomplete behavior. + This example illustrates the autocomplete behavior known as list autocomplete with manual selection. + If the user types one or more characters in the edit box and the typed characters match the beginning of the name of one or more states or territories, a listbox popup appears containing the matching names. + When the listbox appears, a suggested name is not automatically selected. + Thus, after typing, if the user tabs or clicks out of the combobox without choosing a value from the listbox, the typed string becomes the value of the combobox. + Note that this implementation enables users to input the name of a state or territory, but it does not prevent input of any other arbitrary value. +

+ +
+

Example

+ +
+
+ +
+ + +
+
    +
  • Alabama
  • +
  • Alaska
  • +
  • American Samoa
  • +
  • Arizona
  • +
  • Arkansas
  • +
  • California
  • +
  • Colorado
  • +
  • Connecticut
  • +
  • Delaware
  • +
  • District of Columbia
  • +
  • Florida
  • +
  • Georgia
  • +
  • Guam
  • +
  • Hawaii
  • +
  • Idaho
  • +
  • Illinois
  • +
  • Indiana
  • +
  • Iowa
  • +
  • Kansas
  • +
  • Kentucky
  • +
  • Louisiana
  • +
  • Maine
  • +
  • Maryland
  • +
  • Massachusetts
  • +
  • Michigan
  • +
  • Minnesota
  • +
  • Mississippi
  • +
  • Missouri
  • +
  • Montana
  • +
  • Nebraska
  • +
  • Nevada
  • +
  • New Hampshire
  • +
  • New Jersey
  • +
  • New Mexico
  • +
  • New York
  • +
  • North Carolina
  • +
  • North Dakota
  • +
  • Northern Marianas Islands
  • +
  • Ohio
  • +
  • Oklahoma
  • +
  • Oregon
  • +
  • Pennsylvania
  • +
  • Puerto Rico
  • +
  • Rhode Island
  • +
  • South Carolina
  • +
  • South Dakota
  • +
  • Tennessee
  • +
  • Texas
  • +
  • Utah
  • +
  • Vermont
  • +
  • Virginia
  • +
  • Virgin Islands
  • +
  • Washington
  • +
  • West Virginia
  • +
  • Wisconsin
  • +
  • Wyoming
  • +
+
+
+ +
+ +
+

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. +

+

Textbox

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyFunction
Down Arrow +
    +
  • If the textbox is not empty and the listbox is displayed, moves visual focus to the first suggested value.
  • +
  • If the textbox is empty and the listbox is not displayed, opens the listbox and moves visual focus to the first option.
  • +
  • In both cases DOM focus remains on the textbox.
  • +
+
Up Arrow +
    +
  • If the textbox is not empty and the listbox is displayed, moves visual focus to the last suggested value.
  • +
  • If the textbox is empty, first opens the listbox if it is not already displayed and then moves visual focus to the last option.
  • +
  • In both cases DOM focus remains on the textbox.
  • +
+
EnterCloses the listbox.
Escape +
    +
  • Clears the textbox.
  • +
  • If the listbox is displayed, closes it.
  • +
+
Standard single line text editing keys +
    +
  • Keys used for cursor movement and text manipulation, such as Delete and Shift + Right Arrow.
  • +
  • An HTML input with type="text" is used for the textbox so the browser will provide platform-specific editing keys.
  • +
+
+

Listbox Popup

+

+ NOTE: When visual focus is in the listbox, DOM focus remains on the textbox and the value of aria-activedescendant on the textbox 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 textbox value to the content of the focused option in the listbox.
  • +
  • Closes the listbox.
  • +
  • Sets visual focus on the textbox.
  • +
+
Escape +
    +
  • Clears the textbox.
  • +
  • Closes the listbox.
  • +
  • Sets visual focus on the textbox.
  • +
+
Down Arrow +
    +
  • Moves visual focus to the next option.
  • +
  • If visual focus is on the last option, moves visual focus to the first option.
  • +
  • Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
  • +
+
Up Arrow +
    +
  • Moves visual focus to the previous option.
  • +
  • If visual focus is on the first option, moves visual focus to the last option.
  • +
  • Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
  • +
+
Right ArrowMoves visual focus to the textbox and moves the editing cursor one character to the right.
Left ArrowMoves visual focus to the textbox and moves the editing cursor one character to the left.
HomeMoves visual focus to the textbox and places the editing cursor at the beginning of the field.
EndMoves visual focus to the textbox and places the editing cursor at the end of the field.
Printable Characters +
    +
  • Moves visual focus to the textbox.
  • +
  • Types the character in the textbox.
  • +
+
+
+ +
+

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. +

+

Textbox

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RoleAttributeElementUsage
+ combobox + input[type="text"] +
    +
  • Identifies the input as a combobox.
  • +
  • Note: The primary difference between the ARIA 1.0 pattern and the ARIA 1.1 pattern is the placement of the combobox role.
  • +
+
+ aria-autocomplete="list" + input[type="text"]Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions are related to the string that is present in the textbox.
+ aria-haspopup="true" + input[type="text"]Indicates that the combobox can popup another element to suggest values.
+ aria-owns="#IDREF" + input[type="text"] +
    +
  • Identifies the element that serves as the popup.
  • +
  • Note: In the ARIA 1.1 combobox pattern, the combobox uses aria-controls instead of aria-owns.
  • +
+
+ aria-expanded="false" + input[type="text"]Indicates that the popup element is not displayed.
+ aria-expanded="true" + input[type="text"]Indicates that the popup element is displayed.
+ aria-activedescendant="IDREF" + input[type="text"] +
    +
  • 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 + + ul + Identifies the ul element as a listbox.
+ aria-label="States" + ulProvides a label for the listbox.
+ option + li +
    +
  • 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/combobox.css b/examples/combobox/css/combobox.css new file mode 100644 index 0000000000..4240f8a17a --- /dev/null +++ b/examples/combobox/css/combobox.css @@ -0,0 +1,36 @@ +.annotate { + font-style: italic; + color: #366ed4; +} + +.combobox-list { + position: relative; +} + +.combobox-inline label, +.combobox-list label { + margin: 0; + padding: 0; + display: block; +} + +.combobox-list .group input, +.combobox-list .group button { + display: inline; + background-color: #eee; +} + +.combobox-list .group input.focus { + background-color: #d6e8f5; + outline-color: #348ccb; +} + +.combobox-list .group button { + margin: 0; + padding: 0 0.125em 0 0.125em; + position: relative; + top: 1px; + left: -2px; + font-size: 85%; + background-color: #eee; +} diff --git a/examples/combobox/css/listbox.css b/examples/combobox/css/listbox.css new file mode 100644 index 0000000000..fb238fc330 --- /dev/null +++ b/examples/combobox/css/listbox.css @@ -0,0 +1,60 @@ +ul[role="listbox"] { + margin: 0; + padding: 0; + position: absolute; + top: 3em; + list-style: none; + background-color: #eee; + display: none; + border: 2px #333 solid; + height: 12em; + width: 10em; + overflow: scroll; +} + +ul[role="listbox"] li[role="option"] { + margin: 0; + padding: 0; + padding-left: 0.125em; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} + +[role="listbox"].focus { + border-color: #348ccb; +} + +[role="listbox"] [role="option"] { + display: block; + margin: 0.25em; + padding: 0; + background-color: #eee; + font-size: 100%; +} + +/* focus and hover styling */ + +button:focus, +button:hover, +input:focus, +input:hover { + outline: 2px solid black; +} + +input:focus, +input:hover { + background-color: #eee; +} + +[role="listbox"] [role="option"][aria-selected="true"] { + background-color: #ccc; +} + +[role="listbox"].focus [role="option"][aria-selected="true"] { + background-color: #aed2ea; + border-color: #348ccb; +} + +[role="listbox"] li[role="option"]:hover { + background-color: #c2ddef; +} diff --git a/examples/combobox/js/combobox-list.js b/examples/combobox/js/combobox-list.js new file mode 100644 index 0000000000..5f4c4073e4 --- /dev/null +++ b/examples/combobox/js/combobox-list.js @@ -0,0 +1,377 @@ +/* +* This content is licensed according to the W3C Software License at +* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document +*/ +var ComboboxList = function (domNode) { + + this.domNode = domNode; + this.listbox = false; + this.option = false; + + this.hasFocus = false; + this.hasHover = false; + this.filter = ''; + this.isNone = false; + this.isList = false; + this.isBoth = false; + + this.keyCode = Object.freeze({ + 'BACKSPACE': 8, + 'TAB': 9, + 'RETURN': 13, + 'ESC': 27, + 'SPACE': 32, + 'PAGEUP': 33, + 'PAGEDOWN': 34, + 'END': 35, + 'HOME': 36, + 'LEFT': 37, + 'UP': 38, + 'RIGHT': 39, + 'DOWN': 40 + }); +}; + +ComboboxList.prototype.init = function () { + + this.domNode.setAttribute('aria-haspopup', 'true'); + + var autocomplete = this.domNode.getAttribute('aria-autocomplete'); + + if (typeof autocomplete === 'string') { + autocomplete = autocomplete.toLowerCase(); + this.isNone = autocomplete === 'none'; + this.isList = autocomplete === 'list'; + this.isBoth = autocomplete === 'both'; + } + else { + // default value of autocomplete + this.isNone = true; + } + + this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); + this.domNode.addEventListener('keyup', this.handleKeyup.bind(this)); + this.domNode.addEventListener('click', this.handleClick.bind(this)); + this.domNode.addEventListener('focus', this.handleFocus.bind(this)); + this.domNode.addEventListener('blur', this.handleBlur.bind(this)); + + // initialize pop up menus + + var listbox = document.getElementById(this.domNode.getAttribute('aria-controls')); + + if (listbox) { + this.listbox = new Listbox(listbox, this); + this.listbox.init(); + } + + // Open Button + + var button = this.domNode.nextElementSibling; + + if (button && button.tagName === 'BUTTON') { + button.addEventListener('click', this.handleButtonClick.bind(this)); + } + +}; + +ComboboxList.prototype.setActiveDescendant = function (option) { + if (option && this.listbox.hasFocus) { + this.domNode.setAttribute('aria-activedescendant', option.domNode.id); + } + else { + this.domNode.setAttribute('aria-activedescendant', ''); + } +}; + +ComboboxList.prototype.setValue = function (value) { + this.filter = value; + this.domNode.value = this.filter; + this.domNode.setSelectionRange(this.filter.length,this.filter.length); + if (this.isList || this.isBoth) { + this.listbox.filterOptions(this.filter, this.option); + } +}; + +ComboboxList.prototype.setOption = function (option, flag) { + if (typeof flag !== 'boolean') { + flag = false; + } + + if (option) { + this.option = option; + this.listbox.setCurrentOptionStyle(this.option); + this.setActiveDescendant(this.option); + + if (this.isBoth) { + this.domNode.value = this.option.textContent; + if (flag) { + this.domNode.setSelectionRange(this.option.textContent.length,this.option.textContent.length); + } + else { + this.domNode.setSelectionRange(this.filter.length,this.option.textContent.length); + } + } + } +}; + +ComboboxList.prototype.setVisualFocusTextbox = function () { + this.listbox.domNode.classList.remove('focus'); + this.listbox.hasFocus = false; + this.domNode.classList.add('focus'); + this.hasFocus = true; + this.setActiveDescendant(false); +}; + +ComboboxList.prototype.setVisualFocusListbox = function () { + this.domNode.classList.remove('focus'); + this.hasFocus = false; + this.listbox.domNode.classList.add('focus'); + this.listbox.hasFocus = true; + this.setActiveDescendant(this.option); +}; + +ComboboxList.prototype.removeVisualFocusAll = function () { + this.domNode.classList.remove('focus'); + this.hasFocus = false; + this.listbox.domNode.classList.remove('focus'); + this.listbox.hasFocus = true; + this.option = false; + this.setActiveDescendant(false); +}; + +/* Event Handlers */ + +ComboboxList.prototype.handleKeydown = function (event) { + var tgt = event.currentTarget, + flag = false, + char = event.key, + shiftKey = event.shiftKey, + ctrlKey = event.ctrlKey, + altKey = event.altKey; + + switch (event.keyCode) { + + case this.keyCode.RETURN: + if ((this.listbox.hasFocus || this.isBoth) && this.option) { + this.setValue(this.option.textContent); + } + this.listbox.close(true); + flag = true; + break; + + case this.keyCode.DOWN: + + if (this.listbox.hasOptions()) { + if (this.listbox.hasFocus || (this.isBoth && this.option)) { + this.setOption(this.listbox.getNextItem(this.option), true); + } + else { + this.listbox.open(); + if (!altKey) { + this.setOption(this.listbox.getFirstItem(), true); + } + } + this.setVisualFocusListbox(); + } + flag = true; + break; + + case this.keyCode.UP: + + if (this.listbox.hasOptions()) { + if (this.listbox.hasFocus || (this.isBoth && this.option)) { + this.setOption(this.listbox.getPreviousItem(this.option), true); + } + else { + this.listbox.open(); + if (!altKey) { + this.setOption(this.listbox.getLastItem(), true); + } + } + this.setVisualFocusListbox(); + } + flag = true; + break; + + case this.keyCode.ESC: + this.listbox.close(true); + this.setVisualFocusTextbox(); + this.setValue(''); + this.option = false; + flag = true; + break; + + case this.keyCode.TAB: + this.listbox.close(true); + if (this.listbox.hasFocus) { + if (this.option) { + this.setValue(this.option.textContent); + } + } + break; + + default: + break; + } + + if (flag) { + event.stopPropagation(); + event.preventDefault(); + } + +}; + +ComboboxList.prototype.handleKeyup = function (event) { + var tgt = event.currentTarget, + flag = false, + option = false, + char = event.key; + + function isPrintableCharacter (str) { + return str.length === 1 && str.match(/\S/); + } + + if (isPrintableCharacter(char)) { + this.filter += char; + } + + // this is for the case when a selection in the textbox has been deleted + if (this.domNode.value.length < this.filter.length) { + this.filter = this.domNode.value; + this.option = false; + } + + if (event.keyCode === this.keyCode.ESC) { + return; + } + + switch (event.keyCode) { + + case this.keyCode.BACKSPACE: + this.setValue(this.domNode.value); + this.setVisualFocusTextbox(); + this.listbox.setCurrentOptionStyle(false); + this.option = false; + flag = true; + break; + + case this.keyCode.LEFT: + case this.keyCode.RIGHT: + case this.keyCode.HOME: + case this.keyCode.END: + if (this.isBoth) { + this.filter = this.domNode.value; + } + else { + this.option = false; + this.listbox.setCurrentOptionStyle(false); + } + + this.setVisualFocusTextbox(); + flag = true; + break; + + default: + if (isPrintableCharacter(char)) { + this.setVisualFocusTextbox(); + this.listbox.setCurrentOptionStyle(false); + flag = true; + } + + break; + } + + if (event.keyCode !== this.keyCode.RETURN) { + + if (this.isList || this.isBoth) { + option = this.listbox.filterOptions(this.filter, this.option); + if (option) { + if (this.listbox.isClosed()) { + if (this.domNode.value.length) { + this.listbox.open(); + } + } + + if (option.textComparison.indexOf(this.domNode.value.toLowerCase()) === 0) { + this.option = option; + if (this.isBoth || this.listbox.hasFocus) { + this.listbox.setCurrentOptionStyle(option); + if (this.isBoth && isPrintableCharacter(char)) { + this.setOption(option); + } + } + } + else { + this.option = false; + this.listbox.setCurrentOptionStyle(false); + } + } + else { + this.listbox.close(); + this.option = false; + this.setActiveDescendant(false); + } + } + else { + if (this.domNode.value.length) { + this.listbox.open(); + } + } + + } + + + if (flag) { + event.stopPropagation(); + event.preventDefault(); + } + +}; + +ComboboxList.prototype.handleClick = function (event) { + if (this.listbox.isOpen()) { + this.listbox.close(true); + } + else { + this.listbox.open(); + } +}; + +ComboboxList.prototype.handleFocus = function (event) { + this.setVisualFocusTextbox(); + this.option = false; + this.listbox.setCurrentOptionStyle(null); +}; + +ComboboxList.prototype.handleBlur = function (event) { + this.listbox.hasFocus = false; + this.listbox.setCurrentOptionStyle(null); + this.removeVisualFocusAll(); + setTimeout(this.listbox.close.bind(this.listbox, false), 300); + +}; + +ComboboxList.prototype.handleButtonClick = function (event) { + if (this.listbox.isOpen()) { + this.listbox.close(true); + } + else { + this.listbox.open(); + } + this.domNode.focus(); + this.setVisualFocusTextbox(); +}; + + +// Initialize comboboxes + +window.addEventListener('load', function () { + + var comboboxes = document.querySelectorAll('.combobox-list [role="combobox"]'); + + for (var i = 0; i < comboboxes.length; i++) { + var combobox = new ComboboxList(comboboxes[i]); + combobox.init(); + } + +}); diff --git a/examples/combobox/js/listbox.js b/examples/combobox/js/listbox.js new file mode 100644 index 0000000000..4b3500b207 --- /dev/null +++ b/examples/combobox/js/listbox.js @@ -0,0 +1,226 @@ +/* +* This content is licensed according to the W3C Software License at +* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document +*/ +var Listbox = function (domNode, comboboxObj) { + var elementChildren, + msgPrefix = 'Listbox constructor argument domNode '; + + // Check whether domNode is a DOM element + if (!domNode instanceof Element) { + throw new TypeError(msgPrefix + 'is not a DOM Element.'); + } + + // Check whether domNode has child elements + if (domNode.childElementCount === 0) { + throw new Error(msgPrefix + 'has no element children.'); + } + + // Check whether domNode child elements are A elements + var childElement = domNode.firstElementChild; + while (childElement) { + var option = childElement.firstElementChild; + childElement = childElement.nextElementSibling; + } + + this.domNode = domNode; + this.combobox = comboboxObj; + + this.allOptions = []; + + this.options = []; // see PopupMenu init method + + this.firstOption = null; // see PopupMenu init method + this.lastOption = null; // see PopupMenu init method + + this.hasFocus = false; // see MenuItem handleFocus, handleBlur + this.hasHover = false; // see PopupMenu handleMouseover, handleMouseout +}; + +/* +* @method Listbox.prototype.init +* +* @desc +* Add domNode event listeners for mouseover and mouseout. Traverse +* domNode children to configure each option and populate.options +* array. Initialize firstOption and lastOption properties. +*/ +Listbox.prototype.init = function () { + var childElement, optionElement, optionElements, firstChildElement, option, textContent, numItems; + + // Configure the domNode itself + this.domNode.tabIndex = -1; + + this.domNode.setAttribute('role', 'listbox'); + + this.domNode.addEventListener('mouseover', this.handleMouseover.bind(this)); + this.domNode.addEventListener('mouseout', this.handleMouseout.bind(this)); + + // Traverse the element children of domNode: configure each with + // option role behavior and store reference in.options array. + optionElements = this.domNode.getElementsByTagName('LI'); + + for (var i = 0; i < optionElements.length; i++) { + + optionElement = optionElements[i]; + + if (!optionElement.firstElementChild && optionElement.getAttribute('role') != 'separator') { + option = new Option(optionElement, this); + option.init(); + this.allOptions.push(option); + } + } + + this.filterOptions(''); + +}; + +Listbox.prototype.filterOptions = function (filter, currentOption) { + + if (typeof filter !== 'string') { + filter = ''; + } + + var firstMatch = false, + i, + option, + textContent, + numItems; + + filter = filter.toLowerCase(); + + this.options = []; + this.firstChars = []; + this.domNode.innerHTML = ''; + + for (i = 0; i < this.allOptions.length; i++) { + option = this.allOptions[i]; + if (filter.length === 0 || option.textComparison.indexOf(filter) === 0) { + this.options.push(option); + textContent = option.textContent.trim(); + this.firstChars.push(textContent.substring(0, 1).toLowerCase()); + this.domNode.appendChild(option.domNode); + } + } + + // Use populated.options array to initialize firstOption and lastOption. + numItems = this.options.length; + if (numItems > 0) { + this.firstOption = this.options[0]; + this.lastOption = this.options[numItems - 1]; + + if (currentOption && this.options.indexOf(currentOption) >= 0) { + option = currentOption; + } + else { + option = this.firstOption; + } + } + else { + this.firstOption = false; + option = false; + this.lastOption = false; + } + + return option; +}; + +Listbox.prototype.setCurrentOptionStyle = function (option) { + + for (var i = 0; i < this.options.length; i++) { + var opt = this.options[i]; + if (opt === option) { + opt.domNode.setAttribute('aria-selected', 'true'); + this.domNode.scrollTop = opt.domNode.offsetTop; + } + else { + opt.domNode.removeAttribute('aria-selected'); + } + } +}; + +Listbox.prototype.setOption = function (option) { + if (option) { + this.combobox.setOption(option); + this.combobox.setValue(option.textContent); + } +}; + +/* EVENT HANDLERS */ + +Listbox.prototype.handleMouseover = function (event) { + this.hasHover = true; +}; + +Listbox.prototype.handleMouseout = function (event) { + this.hasHover = false; + setTimeout(this.close.bind(this, false), 300); +}; + +/* FOCUS MANAGEMENT METHODS */ + + +Listbox.prototype.getFirstItem = function () { + return this.firstOption; +}; + +Listbox.prototype.getLastItem = function () { + return this.lastOption; +}; + +Listbox.prototype.getPreviousItem = function (currentOption) { + var index; + + if (currentOption !== this.firstOption) { + index = this.options.indexOf(currentOption); + return this.options[index - 1]; + } + return this.lastOption; +}; + +Listbox.prototype.getNextItem = function (currentOption) { + var index; + + if (currentOption !== this.lastOption) { + index = this.options.indexOf(currentOption); + return this.options[index + 1]; + } + return this.firstOption; +}; + +/* MENU DISPLAY METHODS */ + +Listbox.prototype.isOpen = function () { + return this.domNode.style.display === 'block'; +}; + +Listbox.prototype.isClosed = function () { + return this.domNode.style.display !== 'block'; +}; + +Listbox.prototype.hasOptions = function () { + return this.options.length; +}; + +Listbox.prototype.open = function () { + // set CSS properties + this.domNode.style.display = 'block'; + + // set aria-expanded attribute + this.combobox.domNode.setAttribute('aria-expanded', 'true'); +}; + +Listbox.prototype.close = function (force) { + if (typeof force !== 'boolean') { + force = false; + } + + if (force || (!this.hasFocus && !this.hasHover && !this.combobox.hasHover)) { + this.setCurrentOptionStyle(false); + this.domNode.style.display = 'none'; + this.combobox.domNode.setAttribute('aria-expanded', 'false'); + this.combobox.setActiveDescendant(false); + } +}; + + diff --git a/examples/combobox/js/listboxOption.js b/examples/combobox/js/listboxOption.js new file mode 100644 index 0000000000..8dd00dbfd1 --- /dev/null +++ b/examples/combobox/js/listboxOption.js @@ -0,0 +1,42 @@ +/* +* This content is licensed according to the W3C Software License at +* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document +*/ +var Option = function (domNode, listboxObj) { + + this.domNode = domNode; + this.listbox = listboxObj; + this.textContent = domNode.textContent; + this.textComparison = domNode.textContent.toLowerCase(); + +}; + +Option.prototype.init = function () { + + if (!this.domNode.getAttribute('role')) { + this.domNode.setAttribute('role', 'option'); + } + + this.domNode.addEventListener('click', this.handleClick.bind(this)); + this.domNode.addEventListener('mouseover', this.handleMouseover.bind(this)); + this.domNode.addEventListener('mouseout', this.handleMouseout.bind(this)); + +}; + +/* EVENT HANDLERS */ + +Option.prototype.handleClick = function (event) { + this.listbox.setOption(this); + this.listbox.close(true); +}; + +Option.prototype.handleMouseover = function (event) { + this.listbox.hasHover = true; + this.listbox.open(); + +}; + +Option.prototype.handleMouseout = function (event) { + this.listbox.hasHover = false; + setTimeout(this.listbox.close.bind(this.listbox, false), 300); +}; From 92d529bc6479bf6c26031882f78b4a436483d2b8 Mon Sep 17 00:00:00 2001 From: Matt King Date: Mon, 9 Sep 2019 16:11:58 -0700 Subject: [PATCH 02/54] Update documentation for aria-controls --- examples/combobox/combobox-autocomplete-list.html | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/examples/combobox/combobox-autocomplete-list.html b/examples/combobox/combobox-autocomplete-list.html index 851fbb0a9a..7b780ee5e8 100644 --- a/examples/combobox/combobox-autocomplete-list.html +++ b/examples/combobox/combobox-autocomplete-list.html @@ -306,18 +306,13 @@

Textbox

input[type="text"] Indicates that the combobox can popup another element to suggest values. - + - aria-owns="#IDREF" + aria-controls="#IDREF" input[type="text"] - - - + Identifies the element that serves as the popup. From e33a189f513ce779d1c7804cd2a61103d44ebd9b Mon Sep 17 00:00:00 2001 From: Matt King Date: Mon, 9 Sep 2019 16:24:07 -0700 Subject: [PATCH 03/54] Remove references to ArIA 1.0 pattern --- examples/combobox/combobox-autocomplete-list.html | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/examples/combobox/combobox-autocomplete-list.html b/examples/combobox/combobox-autocomplete-list.html index 7b780ee5e8..3307be7fe3 100644 --- a/examples/combobox/combobox-autocomplete-list.html +++ b/examples/combobox/combobox-autocomplete-list.html @@ -31,7 +31,7 @@

Combobox With List Autocomplete Example

The below combobox for choosing the name of a US state or territory demonstrates the - ARIA 1.0 design pattern for combobox. + ARIA design pattern for combobox. The design pattern describes four types of autocomplete behavior. This example illustrates the autocomplete behavior known as list autocomplete with manual selection. If the user types one or more characters in the edit box and the typed characters match the beginning of the name of one or more states or territories, a listbox popup appears containing the matching names. @@ -283,12 +283,7 @@

Textbox

input[type="text"] - -
    -
  • Identifies the input as a combobox.
  • -
  • Note: The primary difference between the ARIA 1.0 pattern and the ARIA 1.1 pattern is the placement of the combobox role.
  • -
- + Identifies the input as a combobox. @@ -414,7 +409,7 @@

Javascript and CSS Source Code

  • CSS: - combobox-1.0.css + combobox.css
  • CSS: @@ -422,7 +417,7 @@

    Javascript and CSS Source Code

  • Javascript: - combobox-1.0-list.js + combobox-list.js
  • Javascript: From c7c77c1408c7c874342466bd802d0278834f694f Mon Sep 17 00:00:00 2001 From: Matt King Date: Fri, 8 Nov 2019 09:45:29 -0800 Subject: [PATCH 04/54] Move 1.0 examples to root and delete 1.0 and 1.1 directories --- .../combobox-autocomplete-list.html | 471 -------------- .../aria1.0pattern/css/combobox-1.0.css | 36 -- .../combobox/aria1.0pattern/css/listbox.css | 60 -- .../aria1.0pattern/js/combobox-1.0-list.js | 377 ------------ .../combobox/aria1.0pattern/js/listbox.js | 226 ------- .../aria1.0pattern/js/listboxOption.js | 42 -- .../aria1.1pattern/css/combobox-1.1.css | 93 --- .../combobox/aria1.1pattern/grid-combo.html | 455 -------------- .../imgs/arrow_drop_down_grey_27x27.png | Bin 220 -> 0 bytes .../aria1.1pattern/js/grid-combo-example.js | 99 --- .../aria1.1pattern/js/grid-combobox.js | 312 ---------- .../js/listbox-combo-example.js | 139 ----- .../aria1.1pattern/js/listbox-combobox.js | 259 -------- .../aria1.1pattern/listbox-combo.html | 579 ------------------ .../combobox-autocomplete-both.html | 0 .../combobox-autocomplete-none.html | 0 16 files changed, 3148 deletions(-) delete mode 100644 examples/combobox/aria1.0pattern/combobox-autocomplete-list.html delete mode 100644 examples/combobox/aria1.0pattern/css/combobox-1.0.css delete mode 100644 examples/combobox/aria1.0pattern/css/listbox.css delete mode 100644 examples/combobox/aria1.0pattern/js/combobox-1.0-list.js delete mode 100644 examples/combobox/aria1.0pattern/js/listbox.js delete mode 100644 examples/combobox/aria1.0pattern/js/listboxOption.js delete mode 100644 examples/combobox/aria1.1pattern/css/combobox-1.1.css delete mode 100644 examples/combobox/aria1.1pattern/grid-combo.html delete mode 100644 examples/combobox/aria1.1pattern/imgs/arrow_drop_down_grey_27x27.png delete mode 100644 examples/combobox/aria1.1pattern/js/grid-combo-example.js delete mode 100644 examples/combobox/aria1.1pattern/js/grid-combobox.js delete mode 100644 examples/combobox/aria1.1pattern/js/listbox-combo-example.js delete mode 100644 examples/combobox/aria1.1pattern/js/listbox-combobox.js delete mode 100644 examples/combobox/aria1.1pattern/listbox-combo.html rename examples/combobox/{aria1.0pattern => }/combobox-autocomplete-both.html (100%) rename examples/combobox/{aria1.0pattern => }/combobox-autocomplete-none.html (100%) diff --git a/examples/combobox/aria1.0pattern/combobox-autocomplete-list.html b/examples/combobox/aria1.0pattern/combobox-autocomplete-list.html deleted file mode 100644 index 9f74bc4fde..0000000000 --- a/examples/combobox/aria1.0pattern/combobox-autocomplete-list.html +++ /dev/null @@ -1,471 +0,0 @@ - - - - -Legacy ARIA 1.0 Combobox With List Autocomplete Example | WAI-ARIA Authoring Practices 1.2 - - - - - - - - - - - - - - - - - -
    -

    Legacy ARIA 1.0 Combobox With List Autocomplete Example

    -

    - NOTE: Please provide feedback on this example page in - issue 554. -

    -

    - The below combobox for choosing the name of a US state or territory demonstrates the - ARIA 1.0 design pattern for combobox. - The design pattern describes four types of autocomplete behavior. - This example illustrates the autocomplete behavior known as list autocomplete with manual selection. - If the user types one or more characters in the edit box and the typed characters match the beginning of the name of one or more states or territories, a listbox popup appears containing the matching names. - When the listbox appears, a suggested name is not automatically selected. - Thus, after typing, if the user tabs or clicks out of the combobox without choosing a value from the listbox, the typed string becomes the value of the combobox. - Note that this implementation enables users to input the name of a state or territory, but it does not prevent input of any other arbitrary value. -

    -

    Similar examples include:

    - - -
    -

    Example

    - -
    -
    - -
    - - -
    -
      -
    • Alabama
    • -
    • Alaska
    • -
    • American Samoa
    • -
    • Arizona
    • -
    • Arkansas
    • -
    • California
    • -
    • Colorado
    • -
    • Connecticut
    • -
    • Delaware
    • -
    • District of Columbia
    • -
    • Florida
    • -
    • Georgia
    • -
    • Guam
    • -
    • Hawaii
    • -
    • Idaho
    • -
    • Illinois
    • -
    • Indiana
    • -
    • Iowa
    • -
    • Kansas
    • -
    • Kentucky
    • -
    • Louisiana
    • -
    • Maine
    • -
    • Maryland
    • -
    • Massachusetts
    • -
    • Michigan
    • -
    • Minnesota
    • -
    • Mississippi
    • -
    • Missouri
    • -
    • Montana
    • -
    • Nebraska
    • -
    • Nevada
    • -
    • New Hampshire
    • -
    • New Jersey
    • -
    • New Mexico
    • -
    • New York
    • -
    • North Carolina
    • -
    • North Dakota
    • -
    • Northern Marianas Islands
    • -
    • Ohio
    • -
    • Oklahoma
    • -
    • Oregon
    • -
    • Pennsylvania
    • -
    • Puerto Rico
    • -
    • Rhode Island
    • -
    • South Carolina
    • -
    • South Dakota
    • -
    • Tennessee
    • -
    • Texas
    • -
    • Utah
    • -
    • Vermont
    • -
    • Virginia
    • -
    • Virgin Islands
    • -
    • Washington
    • -
    • West Virginia
    • -
    • Wisconsin
    • -
    • Wyoming
    • -
    -
    -
    - -
    - -
    -

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

    -

    Textbox

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    KeyFunction
    Down Arrow -
      -
    • If the textbox is not empty and the listbox is displayed, moves visual focus to the first suggested value.
    • -
    • If the textbox is empty and the listbox is not displayed, opens the listbox and moves visual focus to the first option.
    • -
    • In both cases DOM focus remains on the textbox.
    • -
    -
    Up Arrow -
      -
    • If the textbox is not empty and the listbox is displayed, moves visual focus to the last suggested value.
    • -
    • If the textbox is empty, first opens the listbox if it is not already displayed and then moves visual focus to the last option.
    • -
    • In both cases DOM focus remains on the textbox.
    • -
    -
    EnterCloses the listbox.
    Escape -
      -
    • Clears the textbox.
    • -
    • If the listbox is displayed, closes it.
    • -
    -
    Standard single line text editing keys -
      -
    • Keys used for cursor movement and text manipulation, such as Delete and Shift + Right Arrow.
    • -
    • An HTML input with type="text" is used for the textbox so the browser will provide platform-specific editing keys.
    • -
    -
    -

    Listbox Popup

    -

    - NOTE: When visual focus is in the listbox, DOM focus remains on the textbox and the value of aria-activedescendant on the textbox 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 textbox value to the content of the focused option in the listbox.
    • -
    • Closes the listbox.
    • -
    • Sets visual focus on the textbox.
    • -
    -
    Escape -
      -
    • Clears the textbox.
    • -
    • Closes the listbox.
    • -
    • Sets visual focus on the textbox.
    • -
    -
    Down Arrow -
      -
    • Moves visual focus to the next option.
    • -
    • If visual focus is on the last option, moves visual focus to the first option.
    • -
    • Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
    • -
    -
    Up Arrow -
      -
    • Moves visual focus to the previous option.
    • -
    • If visual focus is on the first option, moves visual focus to the last option.
    • -
    • Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
    • -
    -
    Right ArrowMoves visual focus to the textbox and moves the editing cursor one character to the right.
    Left ArrowMoves visual focus to the textbox and moves the editing cursor one character to the left.
    HomeMoves visual focus to the textbox and places the editing cursor at the beginning of the field.
    EndMoves visual focus to the textbox and places the editing cursor at the end of the field.
    Printable Characters -
      -
    • Moves visual focus to the textbox.
    • -
    • Types the character in the textbox.
    • -
    -
    -
    - -
    -

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

    -

    Textbox

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    RoleAttributeElementUsage
    - combobox - input[type="text"] -
      -
    • Identifies the input as a combobox.
    • -
    • Note: The primary difference between the ARIA 1.0 pattern and the ARIA 1.1 pattern is the placement of the combobox role.
    • -
    -
    - aria-autocomplete="list" - input[type="text"]Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions are related to the string that is present in the textbox.
    - aria-haspopup="true" - input[type="text"]Indicates that the combobox can popup another element to suggest values.
    - aria-owns="#IDREF" - input[type="text"] -
      -
    • Identifies the element that serves as the popup.
    • -
    • Note: In the ARIA 1.1 combobox pattern, the combobox uses aria-controls instead of aria-owns.
    • -
    -
    - aria-expanded="false" - input[type="text"]Indicates that the popup element is not displayed.
    - aria-expanded="true" - input[type="text"]Indicates that the popup element is displayed.
    - aria-activedescendant="IDREF" - input[type="text"] -
      -
    • 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 - - ul - Identifies the ul element as a listbox.
    - aria-label="States" - ulProvides a label for the listbox.
    - option - li -
      -
    • 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/aria1.0pattern/css/combobox-1.0.css b/examples/combobox/aria1.0pattern/css/combobox-1.0.css deleted file mode 100644 index 4240f8a17a..0000000000 --- a/examples/combobox/aria1.0pattern/css/combobox-1.0.css +++ /dev/null @@ -1,36 +0,0 @@ -.annotate { - font-style: italic; - color: #366ed4; -} - -.combobox-list { - position: relative; -} - -.combobox-inline label, -.combobox-list label { - margin: 0; - padding: 0; - display: block; -} - -.combobox-list .group input, -.combobox-list .group button { - display: inline; - background-color: #eee; -} - -.combobox-list .group input.focus { - background-color: #d6e8f5; - outline-color: #348ccb; -} - -.combobox-list .group button { - margin: 0; - padding: 0 0.125em 0 0.125em; - position: relative; - top: 1px; - left: -2px; - font-size: 85%; - background-color: #eee; -} diff --git a/examples/combobox/aria1.0pattern/css/listbox.css b/examples/combobox/aria1.0pattern/css/listbox.css deleted file mode 100644 index fb238fc330..0000000000 --- a/examples/combobox/aria1.0pattern/css/listbox.css +++ /dev/null @@ -1,60 +0,0 @@ -ul[role="listbox"] { - margin: 0; - padding: 0; - position: absolute; - top: 3em; - list-style: none; - background-color: #eee; - display: none; - border: 2px #333 solid; - height: 12em; - width: 10em; - overflow: scroll; -} - -ul[role="listbox"] li[role="option"] { - margin: 0; - padding: 0; - padding-left: 0.125em; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; -} - -[role="listbox"].focus { - border-color: #348ccb; -} - -[role="listbox"] [role="option"] { - display: block; - margin: 0.25em; - padding: 0; - background-color: #eee; - font-size: 100%; -} - -/* focus and hover styling */ - -button:focus, -button:hover, -input:focus, -input:hover { - outline: 2px solid black; -} - -input:focus, -input:hover { - background-color: #eee; -} - -[role="listbox"] [role="option"][aria-selected="true"] { - background-color: #ccc; -} - -[role="listbox"].focus [role="option"][aria-selected="true"] { - background-color: #aed2ea; - border-color: #348ccb; -} - -[role="listbox"] li[role="option"]:hover { - background-color: #c2ddef; -} diff --git a/examples/combobox/aria1.0pattern/js/combobox-1.0-list.js b/examples/combobox/aria1.0pattern/js/combobox-1.0-list.js deleted file mode 100644 index da1cb52878..0000000000 --- a/examples/combobox/aria1.0pattern/js/combobox-1.0-list.js +++ /dev/null @@ -1,377 +0,0 @@ -/* -* This content is licensed according to the W3C Software License at -* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document -*/ -var ComboboxList = function (domNode) { - - this.domNode = domNode; - this.listbox = false; - this.option = false; - - this.hasFocus = false; - this.hasHover = false; - this.filter = ''; - this.isNone = false; - this.isList = false; - this.isBoth = false; - - this.keyCode = Object.freeze({ - 'BACKSPACE': 8, - 'TAB': 9, - 'RETURN': 13, - 'ESC': 27, - 'SPACE': 32, - 'PAGEUP': 33, - 'PAGEDOWN': 34, - 'END': 35, - 'HOME': 36, - 'LEFT': 37, - 'UP': 38, - 'RIGHT': 39, - 'DOWN': 40 - }); -}; - -ComboboxList.prototype.init = function () { - - this.domNode.setAttribute('aria-haspopup', 'true'); - - var autocomplete = this.domNode.getAttribute('aria-autocomplete'); - - if (typeof autocomplete === 'string') { - autocomplete = autocomplete.toLowerCase(); - this.isNone = autocomplete === 'none'; - this.isList = autocomplete === 'list'; - this.isBoth = autocomplete === 'both'; - } - else { - // default value of autocomplete - this.isNone = true; - } - - this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); - this.domNode.addEventListener('keyup', this.handleKeyup.bind(this)); - this.domNode.addEventListener('click', this.handleClick.bind(this)); - this.domNode.addEventListener('focus', this.handleFocus.bind(this)); - this.domNode.addEventListener('blur', this.handleBlur.bind(this)); - - // initialize pop up menus - - var listbox = document.getElementById(this.domNode.getAttribute('aria-owns')); - - if (listbox) { - this.listbox = new Listbox(listbox, this); - this.listbox.init(); - } - - // Open Button - - var button = this.domNode.nextElementSibling; - - if (button && button.tagName === 'BUTTON') { - button.addEventListener('click', this.handleButtonClick.bind(this)); - } - -}; - -ComboboxList.prototype.setActiveDescendant = function (option) { - if (option && this.listbox.hasFocus) { - this.domNode.setAttribute('aria-activedescendant', option.domNode.id); - } - else { - this.domNode.setAttribute('aria-activedescendant', ''); - } -}; - -ComboboxList.prototype.setValue = function (value) { - this.filter = value; - this.domNode.value = this.filter; - this.domNode.setSelectionRange(this.filter.length,this.filter.length); - if (this.isList || this.isBoth) { - this.listbox.filterOptions(this.filter, this.option); - } -}; - -ComboboxList.prototype.setOption = function (option, flag) { - if (typeof flag !== 'boolean') { - flag = false; - } - - if (option) { - this.option = option; - this.listbox.setCurrentOptionStyle(this.option); - this.setActiveDescendant(this.option); - - if (this.isBoth) { - this.domNode.value = this.option.textContent; - if (flag) { - this.domNode.setSelectionRange(this.option.textContent.length,this.option.textContent.length); - } - else { - this.domNode.setSelectionRange(this.filter.length,this.option.textContent.length); - } - } - } -}; - -ComboboxList.prototype.setVisualFocusTextbox = function () { - this.listbox.domNode.classList.remove('focus'); - this.listbox.hasFocus = false; - this.domNode.classList.add('focus'); - this.hasFocus = true; - this.setActiveDescendant(false); -}; - -ComboboxList.prototype.setVisualFocusListbox = function () { - this.domNode.classList.remove('focus'); - this.hasFocus = false; - this.listbox.domNode.classList.add('focus'); - this.listbox.hasFocus = true; - this.setActiveDescendant(this.option); -}; - -ComboboxList.prototype.removeVisualFocusAll = function () { - this.domNode.classList.remove('focus'); - this.hasFocus = false; - this.listbox.domNode.classList.remove('focus'); - this.listbox.hasFocus = true; - this.option = false; - this.setActiveDescendant(false); -}; - -/* Event Handlers */ - -ComboboxList.prototype.handleKeydown = function (event) { - var tgt = event.currentTarget, - flag = false, - char = event.key, - shiftKey = event.shiftKey, - ctrlKey = event.ctrlKey, - altKey = event.altKey; - - switch (event.keyCode) { - - case this.keyCode.RETURN: - if ((this.listbox.hasFocus || this.isBoth) && this.option) { - this.setValue(this.option.textContent); - } - this.listbox.close(true); - flag = true; - break; - - case this.keyCode.DOWN: - - if (this.listbox.hasOptions()) { - if (this.listbox.hasFocus || (this.isBoth && this.option)) { - this.setOption(this.listbox.getNextItem(this.option), true); - } - else { - this.listbox.open(); - if (!altKey) { - this.setOption(this.listbox.getFirstItem(), true); - } - } - this.setVisualFocusListbox(); - } - flag = true; - break; - - case this.keyCode.UP: - - if (this.listbox.hasOptions()) { - if (this.listbox.hasFocus || (this.isBoth && this.option)) { - this.setOption(this.listbox.getPreviousItem(this.option), true); - } - else { - this.listbox.open(); - if (!altKey) { - this.setOption(this.listbox.getLastItem(), true); - } - } - this.setVisualFocusListbox(); - } - flag = true; - break; - - case this.keyCode.ESC: - this.listbox.close(true); - this.setVisualFocusTextbox(); - this.setValue(''); - this.option = false; - flag = true; - break; - - case this.keyCode.TAB: - this.listbox.close(true); - if (this.listbox.hasFocus) { - if (this.option) { - this.setValue(this.option.textContent); - } - } - break; - - default: - break; - } - - if (flag) { - event.stopPropagation(); - event.preventDefault(); - } - -}; - -ComboboxList.prototype.handleKeyup = function (event) { - var tgt = event.currentTarget, - flag = false, - option = false, - char = event.key; - - function isPrintableCharacter (str) { - return str.length === 1 && str.match(/\S/); - } - - if (isPrintableCharacter(char)) { - this.filter += char; - } - - // this is for the case when a selection in the textbox has been deleted - if (this.domNode.value.length < this.filter.length) { - this.filter = this.domNode.value; - this.option = false; - } - - if (event.keyCode === this.keyCode.ESC) { - return; - } - - switch (event.keyCode) { - - case this.keyCode.BACKSPACE: - this.setValue(this.domNode.value); - this.setVisualFocusTextbox(); - this.listbox.setCurrentOptionStyle(false); - this.option = false; - flag = true; - break; - - case this.keyCode.LEFT: - case this.keyCode.RIGHT: - case this.keyCode.HOME: - case this.keyCode.END: - if (this.isBoth) { - this.filter = this.domNode.value; - } - else { - this.option = false; - this.listbox.setCurrentOptionStyle(false); - } - - this.setVisualFocusTextbox(); - flag = true; - break; - - default: - if (isPrintableCharacter(char)) { - this.setVisualFocusTextbox(); - this.listbox.setCurrentOptionStyle(false); - flag = true; - } - - break; - } - - if (event.keyCode !== this.keyCode.RETURN) { - - if (this.isList || this.isBoth) { - option = this.listbox.filterOptions(this.filter, this.option); - if (option) { - if (this.listbox.isClosed()) { - if (this.domNode.value.length) { - this.listbox.open(); - } - } - - if (option.textComparison.indexOf(this.domNode.value.toLowerCase()) === 0) { - this.option = option; - if (this.isBoth || this.listbox.hasFocus) { - this.listbox.setCurrentOptionStyle(option); - if (this.isBoth && isPrintableCharacter(char)) { - this.setOption(option); - } - } - } - else { - this.option = false; - this.listbox.setCurrentOptionStyle(false); - } - } - else { - this.listbox.close(); - this.option = false; - this.setActiveDescendant(false); - } - } - else { - if (this.domNode.value.length) { - this.listbox.open(); - } - } - - } - - - if (flag) { - event.stopPropagation(); - event.preventDefault(); - } - -}; - -ComboboxList.prototype.handleClick = function (event) { - if (this.listbox.isOpen()) { - this.listbox.close(true); - } - else { - this.listbox.open(); - } -}; - -ComboboxList.prototype.handleFocus = function (event) { - this.setVisualFocusTextbox(); - this.option = false; - this.listbox.setCurrentOptionStyle(null); -}; - -ComboboxList.prototype.handleBlur = function (event) { - this.listbox.hasFocus = false; - this.listbox.setCurrentOptionStyle(null); - this.removeVisualFocusAll(); - setTimeout(this.listbox.close.bind(this.listbox, false), 300); - -}; - -ComboboxList.prototype.handleButtonClick = function (event) { - if (this.listbox.isOpen()) { - this.listbox.close(true); - } - else { - this.listbox.open(); - } - this.domNode.focus(); - this.setVisualFocusTextbox(); -}; - - -// Initialize comboboxes - -window.addEventListener('load', function () { - - var comboboxes = document.querySelectorAll('.combobox-list [role="combobox"]'); - - for (var i = 0; i < comboboxes.length; i++) { - var combobox = new ComboboxList(comboboxes[i]); - combobox.init(); - } - -}); diff --git a/examples/combobox/aria1.0pattern/js/listbox.js b/examples/combobox/aria1.0pattern/js/listbox.js deleted file mode 100644 index eba6f5a9a5..0000000000 --- a/examples/combobox/aria1.0pattern/js/listbox.js +++ /dev/null @@ -1,226 +0,0 @@ -/* -* This content is licensed according to the W3C Software License at -* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document -*/ -var Listbox = function (domNode, comboboxObj) { - var elementChildren, - msgPrefix = 'Listbox constructor argument domNode '; - - // Check whether domNode is a DOM element - if (!(domNode instanceof Element)) { - throw new TypeError(msgPrefix + 'is not a DOM Element.'); - } - - // Check whether domNode has child elements - if (domNode.childElementCount === 0) { - throw new Error(msgPrefix + 'has no element children.'); - } - - // Check whether domNode child elements are A elements - var childElement = domNode.firstElementChild; - while (childElement) { - var option = childElement.firstElementChild; - childElement = childElement.nextElementSibling; - } - - this.domNode = domNode; - this.combobox = comboboxObj; - - this.allOptions = []; - - this.options = []; // see PopupMenu init method - - this.firstOption = null; // see PopupMenu init method - this.lastOption = null; // see PopupMenu init method - - this.hasFocus = false; // see MenuItem handleFocus, handleBlur - this.hasHover = false; // see PopupMenu handleMouseover, handleMouseout -}; - -/* -* @method Listbox.prototype.init -* -* @desc -* Add domNode event listeners for mouseover and mouseout. Traverse -* domNode children to configure each option and populate.options -* array. Initialize firstOption and lastOption properties. -*/ -Listbox.prototype.init = function () { - var childElement, optionElement, optionElements, firstChildElement, option, textContent, numItems; - - // Configure the domNode itself - this.domNode.tabIndex = -1; - - this.domNode.setAttribute('role', 'listbox'); - - this.domNode.addEventListener('mouseover', this.handleMouseover.bind(this)); - this.domNode.addEventListener('mouseout', this.handleMouseout.bind(this)); - - // Traverse the element children of domNode: configure each with - // option role behavior and store reference in.options array. - optionElements = this.domNode.getElementsByTagName('LI'); - - for (var i = 0; i < optionElements.length; i++) { - - optionElement = optionElements[i]; - - if (!optionElement.firstElementChild && optionElement.getAttribute('role') != 'separator') { - option = new ListboxOption(optionElement, this); - option.init(); - this.allOptions.push(option); - } - } - - this.filterOptions(''); - -}; - -Listbox.prototype.filterOptions = function (filter, currentOption) { - - if (typeof filter !== 'string') { - filter = ''; - } - - var firstMatch = false, - i, - option, - textContent, - numItems; - - filter = filter.toLowerCase(); - - this.options = []; - this.firstChars = []; - this.domNode.innerHTML = ''; - - for (i = 0; i < this.allOptions.length; i++) { - option = this.allOptions[i]; - if (filter.length === 0 || option.textComparison.indexOf(filter) === 0) { - this.options.push(option); - textContent = option.textContent.trim(); - this.firstChars.push(textContent.substring(0, 1).toLowerCase()); - this.domNode.appendChild(option.domNode); - } - } - - // Use populated.options array to initialize firstOption and lastOption. - numItems = this.options.length; - if (numItems > 0) { - this.firstOption = this.options[0]; - this.lastOption = this.options[numItems - 1]; - - if (currentOption && this.options.indexOf(currentOption) >= 0) { - option = currentOption; - } - else { - option = this.firstOption; - } - } - else { - this.firstOption = false; - option = false; - this.lastOption = false; - } - - return option; -}; - -Listbox.prototype.setCurrentOptionStyle = function (option) { - - for (var i = 0; i < this.options.length; i++) { - var opt = this.options[i]; - if (opt === option) { - opt.domNode.setAttribute('aria-selected', 'true'); - this.domNode.scrollTop = opt.domNode.offsetTop; - } - else { - opt.domNode.removeAttribute('aria-selected'); - } - } -}; - -Listbox.prototype.setOption = function (option) { - if (option) { - this.combobox.setOption(option); - this.combobox.setValue(option.textContent); - } -}; - -/* EVENT HANDLERS */ - -Listbox.prototype.handleMouseover = function (event) { - this.hasHover = true; -}; - -Listbox.prototype.handleMouseout = function (event) { - this.hasHover = false; - setTimeout(this.close.bind(this, false), 300); -}; - -/* FOCUS MANAGEMENT METHODS */ - - -Listbox.prototype.getFirstItem = function () { - return this.firstOption; -}; - -Listbox.prototype.getLastItem = function () { - return this.lastOption; -}; - -Listbox.prototype.getPreviousItem = function (currentOption) { - var index; - - if (currentOption !== this.firstOption) { - index = this.options.indexOf(currentOption); - return this.options[index - 1]; - } - return this.lastOption; -}; - -Listbox.prototype.getNextItem = function (currentOption) { - var index; - - if (currentOption !== this.lastOption) { - index = this.options.indexOf(currentOption); - return this.options[index + 1]; - } - return this.firstOption; -}; - -/* MENU DISPLAY METHODS */ - -Listbox.prototype.isOpen = function () { - return this.domNode.style.display === 'block'; -}; - -Listbox.prototype.isClosed = function () { - return this.domNode.style.display !== 'block'; -}; - -Listbox.prototype.hasOptions = function () { - return this.options.length; -}; - -Listbox.prototype.open = function () { - // set CSS properties - this.domNode.style.display = 'block'; - - // set aria-expanded attribute - this.combobox.domNode.setAttribute('aria-expanded', 'true'); -}; - -Listbox.prototype.close = function (force) { - if (typeof force !== 'boolean') { - force = false; - } - - if (force || (!this.hasFocus && !this.hasHover && !this.combobox.hasHover)) { - this.setCurrentOptionStyle(false); - this.domNode.style.display = 'none'; - this.combobox.domNode.setAttribute('aria-expanded', 'false'); - this.combobox.setActiveDescendant(false); - } -}; - - diff --git a/examples/combobox/aria1.0pattern/js/listboxOption.js b/examples/combobox/aria1.0pattern/js/listboxOption.js deleted file mode 100644 index 0707760d0c..0000000000 --- a/examples/combobox/aria1.0pattern/js/listboxOption.js +++ /dev/null @@ -1,42 +0,0 @@ -/* -* This content is licensed according to the W3C Software License at -* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document -*/ -var ListboxOption = function (domNode, listboxObj) { - - this.domNode = domNode; - this.listbox = listboxObj; - this.textContent = domNode.textContent; - this.textComparison = domNode.textContent.toLowerCase(); - -}; - -ListboxOption.prototype.init = function () { - - if (!this.domNode.getAttribute('role')) { - this.domNode.setAttribute('role', 'option'); - } - - this.domNode.addEventListener('click', this.handleClick.bind(this)); - this.domNode.addEventListener('mouseover', this.handleMouseover.bind(this)); - this.domNode.addEventListener('mouseout', this.handleMouseout.bind(this)); - -}; - -/* EVENT HANDLERS */ - -ListboxOption.prototype.handleClick = function (event) { - this.listbox.setOption(this); - this.listbox.close(true); -}; - -ListboxOption.prototype.handleMouseover = function (event) { - this.listbox.hasHover = true; - this.listbox.open(); - -}; - -ListboxOption.prototype.handleMouseout = function (event) { - this.listbox.hasHover = false; - setTimeout(this.listbox.close.bind(this.listbox, false), 300); -}; diff --git a/examples/combobox/aria1.1pattern/css/combobox-1.1.css b/examples/combobox/aria1.1pattern/css/combobox-1.1.css deleted file mode 100644 index 25e9671a3d..0000000000 --- a/examples/combobox/aria1.1pattern/css/combobox-1.1.css +++ /dev/null @@ -1,93 +0,0 @@ -.annotate { - font-style: italic; - color: #366ed4; -} - -.hidden { - display: none; -} - -.combobox-wrapper { - display: inline-block; - position: relative; - font-size: 16px; -} - -.combobox-label { - font-size: 14px; - font-weight: bold; - margin-right: 5px; -} - -.listbox, -.grid { - min-width: 230px; - background: white; - border: 1px solid #ccc; - list-style: none; - margin: 0; - padding: 0; - position: absolute; - top: 1.7em; - z-index: 1; -} - -.listbox .result { - cursor: default; - margin: 0; -} - -.grid .result-row { - padding: 2px; - cursor: default; - margin: 0; -} - -.listbox .result:hover, -.grid .result-row:hover { - background: rgb(139, 189, 225); -} - -.listbox .focused, -.grid .focused { - background: rgb(139, 189, 225); -} - -.grid .focused-cell { - outline-style: dotted; - outline-color: green; -} - -.combobox-wrapper input { - font-size: inherit; - border: 1px solid #aaa; - border-radius: 2px; - line-height: 1.5em; - padding-right: 30px; - width: 200px; -} - -.combobox-dropdown { - position: absolute; - right: 0; - top: 0; - padding: 0 0 2px; - height: 1.5em; - border-radius: 0 2px 2px 0; - border: 1px solid #aaa; -} - -.grid .result-cell { - display: inline-block; - cursor: default; - margin: 0; - padding: 0 5px; -} - -.grid .result-cell:last-child { - float: right; - font-size: 12px; - font-weight: 200; - color: #333; - line-height: 24px; -} diff --git a/examples/combobox/aria1.1pattern/grid-combo.html b/examples/combobox/aria1.1pattern/grid-combo.html deleted file mode 100644 index 51c53bd95a..0000000000 --- a/examples/combobox/aria1.1pattern/grid-combo.html +++ /dev/null @@ -1,455 +0,0 @@ - - - - -ARIA 1.1 Combobox with Grid Popup Example | WAI-ARIA Authoring Practices 1.2 - - - - - - - - - - - - - - - - -
    -

    ARIA 1.1 Combobox with Grid Popup Example

    -

    - The following example combobox implements the - combobox design pattern - using a grid for the suggested values popup. -

    -

    - In this example, users can specify the name of a fruit or vegetable by either typing a value in the box or choosing from the set of values presented in a grid popup. - The popup becomes available after the textbox contains a character that matches the beginning of the name of one of the items in the set of value suggestions. - Users may type any value in the textbox; this implementation does not limit input to values that are in the set of value suggestions. -

    -

    - The grid that presents suggested values has two columns. - Each row of the grid represents one suggestion; column one contains the name of the fruit or vegetable and column two identifies whether it is a fruit or vegetable. -

    -

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

    -

    Textbox

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    KeyFunction
    Down ArrowIf the grid is displayed, moves focus to the first suggested value.
    Up ArrowIf the grid is displayed, moves focus to the last suggested value.
    Escape -
      -
    • Clears the textbox
    • -
    • If the grid is displayed, closes it.
    • -
    -
    Standard single line text editing keys -
      -
    • Keys used for cursor movement and text manipulation, such as Delete and Shift + Right Arrow.
    • -
    • An HTML input with type=text is used for the textbox so the browser will provide platform-specific editing keys.
    • -
    -
    -

    Grid Popup

    -

    - NOTE: When visual focus is in the grid, DOM focus remains on the textbox and the value of aria-activedescendant on the textbox is set to a value that refers to an element in the grid 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 textbox value to the content of the first cell in the row containing focus.
    • -
    • Closes the grid popup.
    • -
    • Sets focus on the textbox.
    • -
    -
    Escape -
      -
    • Closes the grid popup.
    • -
    • Sets focus on the textbox.
    • -
    • Clears the textbox.
    • -
    -
    Down Arrow -
      -
    • Moves focus to the next row.
    • -
    • If focus is in the last row, moves focus to the first row.
    • -
    • Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
    • -
    -
    Up Arrow -
      -
    • Moves focus to the previous row.
    • -
    • If focus is in the first row, moves focus to the last row.
    • -
    • Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
    • -
    -
    Right Arrow -
      -
    • Moves focus to the next cell.
    • -
    • If focus is in the last cell in a row, moves focus to the first cell in the next row.
    • -
    • If focus is in the last cell in the last row, moves focus to the first cell in the first row.
    • -
    -
    Left Arrow -
      -
    • Moves focus to the previous cell.
    • -
    • If focus is in the first cell in a row, moves focus to the last cell in the previous row.
    • -
    • If focus is in the first cell in the first row, moves focus to the last cell in the last row.
    • -
    -
    HomeMoves focus to the textbox and places the editing cursor at the beginning of the field.
    EndMoves focus to the textbox and places the editing cursor at the end of the field.
    Printable Characters -
      -
    • Moves focus to the textbox.
    • -
    • Types the character in the textbox.
    • -
    -
    -
    - -
    -

    Role, Property, State, and Tabindex Attributes

    -

    - The example comboboxes on this page implement 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 Container

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    RoleAttributeElementUsage
    - combobox - div -
      -
    • Identifies the element as a combobox.
    • -
    • Note: -
        -
      • Unlike this ARIA 1.1 combobox, an ARIA 1.0 pattern would have the combobox role on the textbox input instead of a parent container of the textbox.
      • -
      • The ARIA 1.1 pattern demonstrated on this page enables screen reader users to perceive both the input and the popup when using reading mode (see note below about aria-owns).
      • -
      -
    • -
    -
    - aria-haspopup=grid - divIndicates that the combobox can popup a grid to suggest values.
    - aria-owns=IDREF - div -
      -
    • Refers to the element that serves as the grid popup.
    • -
    • Tells browsers to arrange the screen reader reading order so the grid, when it is visible, immediately follows the other elements inside the combobox, regardless of where the grid element is in the DOM.
    • -
    -
    - aria-expanded=false - divIndicates that the popup element is not displayed.
    - aria-expanded=true - divIndicates that the popup element is displayed.
    -

    Textbox

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    RoleAttributeElementUsage
    - id="string" - input[type="text"] -
      -
    • Referenced by for attribute of label element to provide an accessible label.
    • -
    • Recommended labeling method for HTML input elements so clicking label focuses input.
    • -
    -
    - aria-autocomplete=list - input[type="text"]Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup.
    - aria-controls=IDREF - input[type="text"] -
      -
    • Identifies the popup element that lists suggested values.
    • -
    • Note: -
        -
      • In the ARIA 1.0 combobox pattern, the textbox has aria-owns instead of aria-controls.
      • -
      • In this ARIA 1.1 pattern, aria-owns is instead on the parent container so the popup element is a sibling of the textbox instead of a child of the textbox, making it perceivable by screen reader users as an element adjacent to the textbox when using a reading cursor or touch interface.
      • -
      -
    • -
    -
    - aria-activedescendant=IDREF - input[type="text"] -
      -
    • When a cell in the grid is visually indicated as having keyboard focus, refers to that cell.
    • -
    • 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. -
    • -
    -
    -

    Grid Popup

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    RoleAttributeElementUsage
    - grid - - div - Identifies the element as a grid.
    - aria-labelledby=IDREF - divProvides a label for the grid element of the combobox.
    - row - divIdentifies the element containing all the cells for a row.
    - aria-selected=true - div -
      -
    • Specified on a row in the grid when it is visually indicated as selected.
    • -
    • Occurs only when a cell in the grid is referenced by aria-activedescendant.
    • -
    -
    gridcelldivIdentifies the element containing the content for a single cell.
    -
    - -
    -

    Javascript and CSS Source Code

    - -
    - -
    -

    HTML Source Code

    - -
    - - - -
    -
    - - - diff --git a/examples/combobox/aria1.1pattern/imgs/arrow_drop_down_grey_27x27.png b/examples/combobox/aria1.1pattern/imgs/arrow_drop_down_grey_27x27.png deleted file mode 100644 index 7007ef59f0b45e9976e4883eda39106459a07b3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^(jd&i0wmS%+S~(D3dtTpz6=aiY77hwEes65fIY(YII@bfe1e0?qYEwiDyzopr0P1Kzk^lez diff --git a/examples/combobox/aria1.1pattern/js/grid-combo-example.js b/examples/combobox/aria1.1pattern/js/grid-combo-example.js deleted file mode 100644 index 812bd951a9..0000000000 --- a/examples/combobox/aria1.1pattern/js/grid-combo-example.js +++ /dev/null @@ -1,99 +0,0 @@ -/* -* This content is licensed according to the W3C Software License at -* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document -* -* ARIA Combobox Examples -*/ - -var FRUITS_AND_VEGGIES = [ - ['Apple', 'Fruit'], - ['Artichoke', 'Vegetable'], - ['Asparagus', 'Vegetable'], - ['Banana', 'Fruit'], - ['Beets', 'Vegetable'], - ['Bell pepper', 'Vegetable'], - ['Broccoli', 'Vegetable'], - ['Brussels sprout', 'Vegetable'], - ['Cabbage', 'Vegetable'], - ['Carrot', 'Vegetable'], - ['Cauliflower', 'Vegetable'], - ['Celery', 'Vegetable'], - ['Chard', 'Vegetable'], - ['Chicory', 'Vegetable'], - ['Corn', 'Vegetable'], - ['Cucumber', 'Vegetable'], - ['Daikon', 'Vegetable'], - ['Date', 'Fruit'], - ['Edamame', 'Vegetable'], - ['Eggplant', 'Vegetable'], - ['Elderberry', 'Fruit'], - ['Fennel', 'Vegetable'], - ['Fig', 'Fruit'], - ['Garlic', 'Vegetable'], - ['Grape', 'Fruit'], - ['Honeydew melon', 'Fruit'], - ['Iceberg lettuce', 'Vegetable'], - ['Jerusalem artichoke', 'Vegetable'], - ['Kale', 'Vegetable'], - ['Kiwi', 'Fruit'], - ['Leek', 'Vegetable'], - ['Lemon', 'Fruit'], - ['Mango', 'Fruit'], - ['Mangosteen', 'Fruit'], - ['Melon', 'Fruit'], - ['Mushroom', 'Fungus'], - ['Nectarine', 'Fruit'], - ['Okra', 'Vegetable'], - ['Olive', 'Vegetable'], - ['Onion', 'Vegetable'], - ['Orange', 'Fruit'], - ['Parship', 'Vegetable'], - ['Pea', 'Vegetable'], - ['Pear', 'Fruit'], - ['Pineapple', 'Fruit'], - ['Potato', 'Vegetable'], - ['Pumpkin', 'Fruit'], - ['Quince', 'Fruit'], - ['Radish', 'Vegetable'], - ['Rhubarb', 'Vegetable'], - ['Shallot', 'Vegetable'], - ['Spinach', 'Vegetable'], - ['Squash', 'Vegetable'], - ['Strawberry', 'Fruit'], - ['Sweet potato', 'Vegetable'], - ['Tomato', 'Fruit'], - ['Turnip', 'Vegetable'], - ['Ugli fruit', 'Fruit'], - ['Victoria plum', 'Fruit'], - ['Watercress', 'Vegetable'], - ['Watermelon', 'Fruit'], - ['Yam', 'Vegetable'], - ['Zucchini', 'Vegetable'] -]; - -function searchVeggies (searchString) { - var results = []; - - for (var i = 0; i < FRUITS_AND_VEGGIES.length; i++) { - var veggie = FRUITS_AND_VEGGIES[i][0].toLowerCase(); - if (veggie.indexOf(searchString.toLowerCase()) === 0) { - results.push(FRUITS_AND_VEGGIES[i]); - } - } - - return results; -} - -/** - * @function onload - * @desc Initialize the combobox examples once the page has loaded - */ -window.addEventListener('load', function () { - var ex1Combobox = new aria.GridCombobox( - document.getElementById('ex1-combobox'), - document.getElementById('ex1-input'), - document.getElementById('ex1-grid'), - searchVeggies - ); - -}); diff --git a/examples/combobox/aria1.1pattern/js/grid-combobox.js b/examples/combobox/aria1.1pattern/js/grid-combobox.js deleted file mode 100644 index 01a6e59bd6..0000000000 --- a/examples/combobox/aria1.1pattern/js/grid-combobox.js +++ /dev/null @@ -1,312 +0,0 @@ -/* -* This content is licensed according to the W3C Software License at -* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document -*/ - -/** - * @constructor - * - * @desc - * Combobox object representing the state and interactions for a combobox - * widget - * - * @param comboboxNode - * The DOM node pointing to the combobox - * @param input - * The input node - * @param grid - * The grid node to load results in - * @param searchFn - * The search function. The function accepts a search string and returns an - * array of results. - */ -aria.GridCombobox = function ( - comboboxNode, - input, - grid, - searchFn -) { - this.combobox = comboboxNode; - this.input = input; - this.grid = grid; - this.searchFn = searchFn; - this.activeRowIndex = -1; - this.activeColIndex = 0; - this.rowsCount = 0; - this.colsCount = 0; - this.gridFocused = false; - this.shown = false; - this.selectionCol = 0; - - this.setupEvents(); -}; - -aria.GridCombobox.prototype.setupEvents = function () { - document.body.addEventListener('click', this.checkHide.bind(this)); - this.input.addEventListener('keyup', this.checkKey.bind(this)); - this.input.addEventListener('keydown', this.setActiveItem.bind(this)); - this.input.addEventListener('focus', this.checkShow.bind(this)); - this.grid.addEventListener('click', this.clickItem.bind(this)); -}; - -aria.GridCombobox.prototype.checkKey = function (evt) { - var key = evt.which || evt.keyCode; - - switch (key) { - case aria.KeyCode.UP: - case aria.KeyCode.DOWN: - case aria.KeyCode.ESC: - case aria.KeyCode.RETURN: - evt.preventDefault(); - return; - case aria.KeyCode.LEFT: - case aria.KeyCode.RIGHT: - if (this.gridFocused) { - console.log('grid focused'); - evt.preventDefault(); - return; - } - // fall through - default: - this.updateResults(); - } -}; - -aria.GridCombobox.prototype.updateResults = function () { - var searchString = this.input.value; - var results = this.searchFn(searchString); - - this.hideResults(); - - if (!searchString) { - results = []; - } - - if (results.length) { - for (var row = 0; row < results.length; row++) { - var resultRow = document.createElement('div'); - resultRow.className = 'result-row'; - resultRow.setAttribute('role', 'row'); - resultRow.setAttribute('id', 'result-row-' + row); - for (var col = 0; col < results[row].length; col++) { - var resultCell = document.createElement('div'); - resultCell.className = 'result-cell'; - resultCell.setAttribute('role', 'gridcell'); - resultCell.setAttribute('id', 'result-item-' + row + 'x' + col); - resultCell.innerText = results[row][col]; - resultRow.appendChild(resultCell); - } - this.grid.appendChild(resultRow); - } - aria.Utils.removeClass(this.grid, 'hidden'); - this.combobox.setAttribute('aria-expanded', 'true'); - this.rowsCount = results.length; - this.colsCount = results.length ? results[0].length : 0; - this.shown = true; - } -}; - -aria.GridCombobox.prototype.getRowIndex = function (key) { - var activeRowIndex = this.activeRowIndex; - - switch (key) { - case aria.KeyCode.UP: - case aria.KeyCode.LEFT: - if (activeRowIndex <= 0) { - activeRowIndex = this.rowsCount - 1; - } - else { - activeRowIndex--; - } - break; - case aria.KeyCode.DOWN: - case aria.KeyCode.RIGHT: - if (activeRowIndex === -1 || activeRowIndex >= this.rowsCount - 1) { - activeRowIndex = 0; - } - else { - activeRowIndex++; - } - } - - return activeRowIndex; -}; - -aria.GridCombobox.prototype.setActiveItem = function (evt) { - var key = evt.which || evt.keyCode; - var activeRowIndex = this.activeRowIndex; - var activeColIndex = this.activeColIndex; - - if (key === aria.KeyCode.ESC) { - if (this.gridFocused) { - this.gridFocused = false; - this.removeFocusCell(this.activeRowIndex, this.activeColIndex); - this.activeRowIndex = -1; - this.activeColIndex = 0; - this.input.setAttribute( - 'aria-activedescendant', - '' - ); - } - else { - this.hideResults(); - setTimeout((function () { - // On Firefox, input does not get cleared here unless wrapped in - // a setTimeout - this.input.value = ''; - }).bind(this), 1); - } - return; - } - - if (this.rowsCount < 1) { - return; - } - - var prevActive = this.getItemAt(activeRowIndex, this.selectionCol); - var activeItem; - - switch (key) { - case aria.KeyCode.UP: - this.gridFocused = true; - activeRowIndex = this.getRowIndex(key); - evt.preventDefault(); - break; - case aria.KeyCode.DOWN: - this.gridFocused = true; - activeRowIndex = this.getRowIndex(key); - evt.preventDefault(); - break; - case aria.KeyCode.LEFT: - if (activeColIndex <= 0) { - activeColIndex = this.colsCount - 1; - activeRowIndex = this.getRowIndex(key); - } - else { - activeColIndex--; - } - if (this.gridFocused) { - evt.preventDefault(); - } - break; - case aria.KeyCode.RIGHT: - if (activeColIndex === -1 || activeColIndex >= this.colsCount - 1) { - activeColIndex = 0; - activeRowIndex = this.getRowIndex(key); - } - else { - activeColIndex++; - } - if (this.gridFocused) { - evt.preventDefault(); - } - break; - case aria.KeyCode.RETURN: - activeItem = this.getItemAt(activeRowIndex, this.selectionCol); - this.selectItem(activeItem); - this.gridFocused = false; - return; - case aria.KeyCode.TAB: - this.hideResults(); - return; - default: - return; - } - - if (prevActive) { - this.removeFocusCell(this.activeRowIndex, this.activeColIndex); - prevActive.setAttribute('aria-selected', 'false'); - } - - activeItem = this.getItemAt(activeRowIndex, activeColIndex); - this.activeRowIndex = activeRowIndex; - this.activeColIndex = activeColIndex; - - if (activeItem) { - this.input.setAttribute( - 'aria-activedescendant', - 'result-item-' + activeRowIndex + 'x' + activeColIndex - ); - this.focusCell(activeRowIndex, activeColIndex); - var selectedItem = this.getItemAt(activeRowIndex, this.selectionCol); - selectedItem.setAttribute('aria-selected', 'true'); - } - else { - this.input.setAttribute( - 'aria-activedescendant', - '' - ); - } -}; - -aria.GridCombobox.prototype.getItemAt = function (rowIndex, colIndex) { - return document.getElementById('result-item-' + rowIndex + 'x' + colIndex); -}; - -aria.GridCombobox.prototype.clickItem = function (evt) { - if (!evt.target) { - return; - } - - var row; - if (evt.target.getAttribute('role') === 'row') { - row = evt.target; - } - else if (evt.target.getAttribute('role') === 'gridcell') { - row = evt.target.parentNode; - } - else { - return; - } - - var selectItem = row.querySelector('.result-cell'); - this.selectItem(selectItem); -}; - -aria.GridCombobox.prototype.selectItem = function (item) { - if (item) { - this.input.value = item.innerText; - this.hideResults(); - } -}; - -aria.GridCombobox.prototype.checkShow = function (evt) { - this.updateResults(); -}; - -aria.GridCombobox.prototype.checkHide = function (evt) { - if (evt.target === this.input || this.combobox.contains(evt.target)) { - return; - } - this.hideResults(); -}; - -aria.GridCombobox.prototype.hideResults = function () { - this.gridFocused = false; - this.shown = false; - this.activeRowIndex = -1; - this.activeColIndex = 0; - this.grid.innerHTML = ''; - aria.Utils.addClass(this.grid, 'hidden'); - this.combobox.setAttribute('aria-expanded', 'false'); - this.rowsCount = 0; - this.colsCount = 0; - this.input.setAttribute( - 'aria-activedescendant', - '' - ); -}; - -aria.GridCombobox.prototype.removeFocusCell = function (rowIndex, colIndex) { - var row = document.getElementById('result-row-' + rowIndex); - aria.Utils.removeClass(row, 'focused'); - var cell = this.getItemAt(rowIndex, colIndex); - aria.Utils.removeClass(cell, 'focused-cell'); -}; - -aria.GridCombobox.prototype.focusCell = function (rowIndex, colIndex) { - var row = document.getElementById('result-row-' + rowIndex); - aria.Utils.addClass(row, 'focused'); - var cell = this.getItemAt(rowIndex, colIndex); - aria.Utils.addClass(cell, 'focused-cell'); -}; diff --git a/examples/combobox/aria1.1pattern/js/listbox-combo-example.js b/examples/combobox/aria1.1pattern/js/listbox-combo-example.js deleted file mode 100644 index 5882d9901c..0000000000 --- a/examples/combobox/aria1.1pattern/js/listbox-combo-example.js +++ /dev/null @@ -1,139 +0,0 @@ -/* -* This content is licensed according to the W3C Software License at -* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document -* -* ARIA Combobox Examples -*/ - -var FRUITS_AND_VEGGIES = [ - 'Apple', - 'Artichoke', - 'Asparagus', - 'Banana', - 'Beets', - 'Bell pepper', - 'Broccoli', - 'Brussels sprout', - 'Cabbage', - 'Carrot', - 'Cauliflower', - 'Celery', - 'Chard', - 'Chicory', - 'Corn', - 'Cucumber', - 'Daikon', - 'Date', - 'Edamame', - 'Eggplant', - 'Elderberry', - 'Fennel', - 'Fig', - 'Garlic', - 'Grape', - 'Honeydew melon', - 'Iceberg lettuce', - 'Jerusalem artichoke', - 'Kale', - 'Kiwi', - 'Leek', - 'Lemon', - 'Mango', - 'Mangosteen', - 'Melon', - 'Mushroom', - 'Nectarine', - 'Okra', - 'Olive', - 'Onion', - 'Orange', - 'Parship', - 'Pea', - 'Pear', - 'Pineapple', - 'Potato', - 'Pumpkin', - 'Quince', - 'Radish', - 'Rhubarb', - 'Shallot', - 'Spinach', - 'Squash', - 'Strawberry', - 'Sweet potato', - 'Tomato', - 'Turnip', - 'Ugli fruit', - 'Victoria plum', - 'Watercress', - 'Watermelon', - 'Yam', - 'Zucchini' -]; - -function searchVeggies (searchString) { - var results = []; - - for (var i = 0; i < FRUITS_AND_VEGGIES.length; i++) { - var veggie = FRUITS_AND_VEGGIES[i].toLowerCase(); - if (veggie.indexOf(searchString.toLowerCase()) === 0) { - results.push(FRUITS_AND_VEGGIES[i]); - } - } - - return results; -} - -/** - * @function onload - * @desc Initialize the combobox examples once the page has loaded - */ -window.addEventListener('load', function () { - var ex1Combobox = new aria.ListboxCombobox( - document.getElementById('ex1-combobox'), - document.getElementById('ex1-input'), - document.getElementById('ex1-listbox'), - searchVeggies, - false - ); - - var ex2Combobox = new aria.ListboxCombobox( - document.getElementById('ex2-combobox'), - document.getElementById('ex2-input'), - document.getElementById('ex2-listbox'), - searchVeggies, - true - ); - - var ex3Combobox = new aria.ListboxCombobox( - document.getElementById('ex3-combobox'), - document.getElementById('ex3-input'), - document.getElementById('ex3-listbox'), - searchVeggies, - true, - function () { - // on show - document.getElementById('ex3-combobox-arrow') - .setAttribute('aria-label', 'Hide vegetable options'); - }, - function () { - // on hide - document.getElementById('ex3-combobox-arrow') - .setAttribute('aria-label', 'Show vegetable options'); - } - ); - - document.getElementById('ex3-combobox-arrow').addEventListener( - 'click', - function () { - if (ex3Combobox.shown) { - document.getElementById('ex3-input').focus(); - ex3Combobox.hideListbox(); - } - else { - document.getElementById('ex3-input').focus(); - ex3Combobox.updateResults(true); - } - } - ); -}); diff --git a/examples/combobox/aria1.1pattern/js/listbox-combobox.js b/examples/combobox/aria1.1pattern/js/listbox-combobox.js deleted file mode 100644 index a263c9dcc7..0000000000 --- a/examples/combobox/aria1.1pattern/js/listbox-combobox.js +++ /dev/null @@ -1,259 +0,0 @@ -/* -* This content is licensed according to the W3C Software License at -* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document -*/ -/** - * @constructor - * - * @desc - * Combobox object representing the state and interactions for a combobox - * widget - * - * @param comboboxNode - * The DOM node pointing to the combobox - * @param input - * The input node - * @param listbox - * The listbox node to load results in - * @param searchFn - * The search function. The function accepts a search string and returns an - * array of results. - */ -aria.ListboxCombobox = function ( - comboboxNode, - input, - listbox, - searchFn, - shouldAutoSelect, - onShow, - onHide -) { - this.combobox = comboboxNode; - this.input = input; - this.listbox = listbox; - this.searchFn = searchFn; - this.shouldAutoSelect = shouldAutoSelect; - this.onShow = onShow || function () {}; - this.onHide = onHide || function () {}; - this.activeIndex = -1; - this.resultsCount = 0; - this.shown = false; - this.hasInlineAutocomplete = - input.getAttribute('aria-autocomplete') === 'both'; - - this.setupEvents(); -}; - -aria.ListboxCombobox.prototype.setupEvents = function () { - document.body.addEventListener('click', this.checkHide.bind(this)); - this.input.addEventListener('keyup', this.checkKey.bind(this)); - this.input.addEventListener('keydown', this.setActiveItem.bind(this)); - this.input.addEventListener('focus', this.checkShow.bind(this)); - this.input.addEventListener('blur', this.checkSelection.bind(this)); - this.listbox.addEventListener('click', this.clickItem.bind(this)); -}; - -aria.ListboxCombobox.prototype.checkKey = function (evt) { - var key = evt.which || evt.keyCode; - - switch (key) { - case aria.KeyCode.UP: - case aria.KeyCode.DOWN: - case aria.KeyCode.ESC: - case aria.KeyCode.RETURN: - evt.preventDefault(); - return; - default: - this.updateResults(false); - } - - if (this.hasInlineAutocomplete) { - switch (key) { - case aria.KeyCode.BACKSPACE: - return; - default: - this.autocompleteItem(); - } - } -}; - -aria.ListboxCombobox.prototype.updateResults = function (shouldShowAll) { - var searchString = this.input.value; - var results = this.searchFn(searchString); - - this.hideListbox(); - - if (!shouldShowAll && !searchString) { - results = []; - } - - if (results.length) { - for (var i = 0; i < results.length; i++) { - var resultItem = document.createElement('li'); - resultItem.className = 'result'; - resultItem.setAttribute('role', 'option'); - resultItem.setAttribute('id', 'result-item-' + i); - resultItem.innerText = results[i]; - if (this.shouldAutoSelect && i === 0) { - resultItem.setAttribute('aria-selected', 'true'); - aria.Utils.addClass(resultItem, 'focused'); - this.activeIndex = 0; - } - this.listbox.appendChild(resultItem); - } - aria.Utils.removeClass(this.listbox, 'hidden'); - this.combobox.setAttribute('aria-expanded', 'true'); - this.resultsCount = results.length; - this.shown = true; - this.onShow(); - } -}; - -aria.ListboxCombobox.prototype.setActiveItem = function (evt) { - var key = evt.which || evt.keyCode; - var activeIndex = this.activeIndex; - - if (key === aria.KeyCode.ESC) { - this.hideListbox(); - setTimeout((function () { - // On Firefox, input does not get cleared here unless wrapped in - // a setTimeout - this.input.value = ''; - }).bind(this), 1); - return; - } - - if (this.resultsCount < 1) { - if (this.hasInlineAutocomplete && (key === aria.KeyCode.DOWN || key === aria.KeyCode.UP)) { - this.updateResults(true); - } - else { - return; - } - } - - var prevActive = this.getItemAt(activeIndex); - var activeItem; - - switch (key) { - case aria.KeyCode.UP: - if (activeIndex <= 0) { - activeIndex = this.resultsCount - 1; - } - else { - activeIndex--; - } - break; - case aria.KeyCode.DOWN: - if (activeIndex === -1 || activeIndex >= this.resultsCount - 1) { - activeIndex = 0; - } - else { - activeIndex++; - } - break; - case aria.KeyCode.RETURN: - activeItem = this.getItemAt(activeIndex); - this.selectItem(activeItem); - return; - case aria.KeyCode.TAB: - this.checkSelection(); - this.hideListbox(); - return; - default: - return; - } - - evt.preventDefault(); - - activeItem = this.getItemAt(activeIndex); - this.activeIndex = activeIndex; - - if (prevActive) { - aria.Utils.removeClass(prevActive, 'focused'); - prevActive.setAttribute('aria-selected', 'false'); - } - - if (activeItem) { - this.input.setAttribute( - 'aria-activedescendant', - 'result-item-' + activeIndex - ); - aria.Utils.addClass(activeItem, 'focused'); - activeItem.setAttribute('aria-selected', 'true'); - if (this.hasInlineAutocomplete) { - this.input.value = activeItem.innerText; - } - } - else { - this.input.setAttribute( - 'aria-activedescendant', - '' - ); - } -}; - -aria.ListboxCombobox.prototype.getItemAt = function (index) { - return document.getElementById('result-item-' + index); -}; - -aria.ListboxCombobox.prototype.clickItem = function (evt) { - if (evt.target && evt.target.nodeName == 'LI') { - this.selectItem(evt.target); - } -}; - -aria.ListboxCombobox.prototype.selectItem = function (item) { - if (item) { - this.input.value = item.innerText; - this.hideListbox(); - } -}; - -aria.ListboxCombobox.prototype.checkShow = function (evt) { - this.updateResults(false); -}; - -aria.ListboxCombobox.prototype.checkHide = function (evt) { - if (evt.target === this.input || this.combobox.contains(evt.target)) { - return; - } - this.hideListbox(); -}; - -aria.ListboxCombobox.prototype.hideListbox = function () { - this.shown = false; - this.activeIndex = -1; - this.listbox.innerHTML = ''; - aria.Utils.addClass(this.listbox, 'hidden'); - this.combobox.setAttribute('aria-expanded', 'false'); - this.resultsCount = 0; - this.input.setAttribute( - 'aria-activedescendant', - '' - ); - this.onHide(); -}; - -aria.ListboxCombobox.prototype.checkSelection = function () { - if (this.activeIndex < 0) { - return; - } - var activeItem = this.getItemAt(this.activeIndex); - this.selectItem(activeItem); -}; - -aria.ListboxCombobox.prototype.autocompleteItem = function () { - var autocompletedItem = this.listbox.querySelector('.focused'); - var inputText = this.input.value; - - if (!autocompletedItem || !inputText) { - return; - } - - var autocomplete = autocompletedItem.innerText; - if (inputText !== autocomplete) { - this.input.value = autocomplete; - this.input.setSelectionRange(inputText.length, autocomplete.length); - } -}; diff --git a/examples/combobox/aria1.1pattern/listbox-combo.html b/examples/combobox/aria1.1pattern/listbox-combo.html deleted file mode 100644 index dbce52ce70..0000000000 --- a/examples/combobox/aria1.1pattern/listbox-combo.html +++ /dev/null @@ -1,579 +0,0 @@ - - - - -ARIA 1.1 Combobox with Listbox Popup Examples | WAI-ARIA Authoring Practices 1.2 - - - - - - - - - - - - - - - - -
    -

    ARIA 1.1 Combobox with Listbox Popup Examples

    -

    - The following three example comboboxes implement the ARIA 1.1 form of the - combobox design pattern - using a listbox popup. - Each of the three comboboxes also demonstrates a different form of the autocomplete behaviors described in the design pattern. -

    -

    - In these examples, users can specify the name of a fruit or vegetable by either typing a value in the box or choosing from the list. - The list becomes available after the textbox contains a character that matches the beginning of the name of one of the items in the list of suggested values. - Users may type any value in the textbox; this implementation does not limit input to values that are in the list of suggested values. -

    -

    Similar examples include:

    - - -
    -

    Examples

    -

    Example 1: List Autocomplete with Manual Selection

    - -
    - -
    - - -
    -
    - -

    Notes

    -

    List autocomplete with manual selection means that:

    -
      -
    1. - When the listbox popup is displayed, it contains suggested values that complete or logically correspond to the characters typed in the textbox. - In this implementation, the values in the listbox have names that start with the characters typed in the textbox. -
    2. -
    3. Users may set the value of the combobox by choosing a value from the list of suggested values.
    4. -
    5. If the user does not choose a value from the listbox before moving focus outside the combobox, the value that the user typed, if any, becomes the value of the combobox.
    6. -
    -

    Example 2: List Autocomplete with Automatic Selection

    - -
    - -
    - - -
    -
    - -

    Notes

    -

    List autocomplete with automatic selection means that:

    -
      -
    1. - When the listbox popup is displayed, it contains suggested values that complete or logically correspond to the characters typed in the textbox. - In this implementation, the values in the listbox have names that start with the characters typed in the textbox. -
    2. -
    3. The first suggestion is automatically highlighted as selected.
    4. -
    5. The automatically selected suggestion becomes the value of the textbox when the combobox loses focus unless the user chooses a different suggestion or changes the character string in the textbox.
    6. -
    -

    Example 3: List with Inline Autocomplete

    - -
    - -
    - - -
    -
    - -

    Notes

    -

    List with inline autocomplete means that:

    -
      -
    1. With the exception of one additional feature, this example has the same autocomplete behavior as the previous example that has list with automatic selection.
    2. -
    3. The additional feature is that the portion of the selected suggestion that has not been typed by the user, a completion string, appears inline after the input cursor in the textbox.
    4. -
    5. The inline completion string is visually highlighted and has a selected state.
    6. -
    -
    - -
    -

    Keyboard Support

    -

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

    -

    Textbox

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    KeyFunction
    Down Arrow -
      -
    • If the listbox is displayed: -
        -
      • Example 1: Moves focus to the first suggested value.
      • -
      • Examples 2 and 3: Moves focus to the second suggested value. Note that the first value is automatically selected.
      • -
      -
    • -
    • If the listbox is not displayed, in example 3 only, opens the listbox and moves focus to the first value.
    • -
    -
    Up Arrow -
      -
    • If the listbox is displayed, moves focus to the last suggested value.
    • -
    • If the listbox is not displayed, in example 3 only, opens the listbox and moves focus to the last value.
    • -
    -
    Enter -
      -
    • Example 1: Does nothing.
    • -
    • Examples 2 and 3: If the listbox is displayed and the first option is automatically selected: -
        -
      • Sets the textbox value to the content of the selected option.
      • -
      • Closes the listbox.
      • -
      -
    • -
    -
    Escape -
      -
    • Clears the textbox
    • -
    • If the listbox is displayed, closes it.
    • -
    -
    Standard single line text editing keys -
      -
    • Keys used for cursor movement and text manipulation, such as Delete and Shift + Right Arrow.
    • -
    • An HTML input with type=text is used for the textbox so the browser will provide platform-specific editing keys.
    • -
    -
    -

    Listbox Popup

    -

    - NOTE: When visual focus is in the listbox, DOM focus remains on the textbox and the value of aria-activedescendant on the textbox 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 textbox value to the content of the focused option in the listbox.
    • -
    • Closes the listbox.
    • -
    • Sets focus on the textbox.
    • -
    -
    Escape -
      -
    • Closes the listbox.
    • -
    • Sets focus on the textbox.
    • -
    • Clears the textbox.
    • -
    -
    Down Arrow -
      -
    • Moves focus to the next option.
    • -
    • If focus is on the last option, moves focus to the first option.
    • -
    • Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
    • -
    -
    Up Arrow -
      -
    • Moves focus to the previous option.
    • -
    • If focus is on the first option, moves focus to the last option.
    • -
    • Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
    • -
    -
    Right ArrowMoves focus to the textbox and moves the editing cursor one character to the right.
    Left ArrowMoves focus to the textbox and moves the editing cursor one character to the left.
    HomeMoves focus to the textbox and places the editing cursor at the beginning of the field.
    EndMoves focus to the textbox and places the editing cursor at the end of the field.
    Printable Characters -
      -
    • Moves focus to the textbox.
    • -
    • Types the character in the textbox.
    • -
    -
    -
    - -
    -

    Role, Property, State, and Tabindex Attributes

    -

    - The example comboboxes on this page implement 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 Container

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    RoleAttributeElementUsage
    - combobox - div -
      -
    • Identifies the element as a combobox.
    • -
    • Note: -
        -
      • Unlike this ARIA 1.1 combobox, an ARIA 1.0 pattern would have the combobox role on the textbox input instead of a parent container of the textbox.
      • -
      • The ARIA 1.1 pattern demonstrated on this page enables screen reader users to perceive both the input and the popup when using reading mode (see note below about aria-owns).
      • -
      -
    • -
    -
    - aria-haspopup=listbox - div -
      -
    • Indicates that the combobox can popup a listbox to suggest values.
    • -
    • This is the default value for elements with the combobox role.
    • -
    -
    - aria-owns=IDREF - div -
      -
    • Refers to the element that serves as the listbox popup.
    • -
    • Tells browsers to arrange the screen reader reading order so the listbox, when it is visible, immediately follows the other elements inside the combobox, regardless of where the listbox element is in the DOM.
    • -
    -
    - aria-expanded=false - divIndicates that the popup element is not displayed.
    - aria-expanded=true - divIndicates that the popup element is displayed.
    -

    Textbox

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    RoleAttributeElementUsage
    - id="string" - input[type="text"] -
      -
    • Referenced by for attribute of label element to provide an accessible label.
    • -
    • Recommended labeling method for HTML input elements so clicking label focuses input.
    • -
    -
    - aria-autocomplete=list - input[type="text"]Examples 1 and 2: Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions are related to the string that is present in the textbox.
    - aria-autocomplete=both - input[type="text"]Example 3: Indicates that the autocomplete behavior of the text input is to both show an inline completion string and suggest a list of possible values in a popup where the suggestions are related to the string that is present in the textbox.
    - aria-controls=IDREF - input[type="text"] -
      -
    • Identifies the popup element that lists suggested values.
    • -
    • Note: -
        -
      • In the ARIA 1.0 combobox pattern, the textbox has aria-owns instead of aria-controls.
      • -
      • In this ARIA 1.1 pattern, aria-owns is instead on the parent container so the popup element is a sibling of the textbox instead of a child of the textbox, making it perceivable by screen reader users as an element adjacent to the textbox when using a reading cursor or touch interface.
      • -
      -
    • -
    -
    - aria-activedescendant=IDREF - input[type="text"] -
      -
    • 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 - - ul - Identifies the ul element as a listbox.
    - aria-labelledby=IDREF - ulProvides a label for the listbox element of the combobox.
    - option - li -
      -
    • 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.
    • -
    • In example 1, occurs only when an option in the list is referenced by aria-activedescendant.
    • -
    • In examples 2 and 3, also occurs when focus is in the textbox and the first option is automatically selected.
    • -
    -
    -
    - -
    -

    Javascript and CSS Source Code

    - -
    - -
    -

    HTML Source Code

    -

    Example 1

    - -
    - -

    Example 2

    - -
    - -

    Example 3

    - -
    - - -
    -
    - - - diff --git a/examples/combobox/aria1.0pattern/combobox-autocomplete-both.html b/examples/combobox/combobox-autocomplete-both.html similarity index 100% rename from examples/combobox/aria1.0pattern/combobox-autocomplete-both.html rename to examples/combobox/combobox-autocomplete-both.html diff --git a/examples/combobox/aria1.0pattern/combobox-autocomplete-none.html b/examples/combobox/combobox-autocomplete-none.html similarity index 100% rename from examples/combobox/aria1.0pattern/combobox-autocomplete-none.html rename to examples/combobox/combobox-autocomplete-none.html From 7d5c0c57f7f282ae21bd0e701f75cdd9dac43e8d Mon Sep 17 00:00:00 2001 From: Matt King Date: Fri, 8 Nov 2019 14:38:36 -0800 Subject: [PATCH 05/54] Update list of examples --- aria-practices.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/aria-practices.html b/aria-practices.html index 666abbf810..10ae7ffdb3 100644 --- a/aria-practices.html +++ b/aria-practices.html @@ -788,11 +788,10 @@

    Combo Box

    Examples

    From 93be0a9185c7bf33fc0c13b860422332e24e63d0 Mon Sep 17 00:00:00 2001 From: Matt King Date: Fri, 8 Nov 2019 15:04:11 -0800 Subject: [PATCH 06/54] Change 1.1 to 1.2 in title and footer of autocomplete-list example page --- examples/combobox/combobox-autocomplete-list.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/combobox/combobox-autocomplete-list.html b/examples/combobox/combobox-autocomplete-list.html index 3307be7fe3..133789ff80 100644 --- a/examples/combobox/combobox-autocomplete-list.html +++ b/examples/combobox/combobox-autocomplete-list.html @@ -2,7 +2,7 @@ -Combobox With List Autocomplete Example | WAI-ARIA Authoring Practices 1.1 +Combobox With List Autocomplete Example | WAI-ARIA Authoring Practices 1.2 @@ -444,7 +444,7 @@

    HTML Source Code

    From c0634d9b3e9a7baea0da5be3efc42e5ff9581882 Mon Sep 17 00:00:00 2001 From: Matt King Date: Sat, 9 Nov 2019 15:39:41 -0800 Subject: [PATCH 07/54] Update paths in and revise text of combobox-autocomplete-both.html --- .../combobox/combobox-autocomplete-both.html | 67 +++++++------------ 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/examples/combobox/combobox-autocomplete-both.html b/examples/combobox/combobox-autocomplete-both.html index 92708ef296..546520c9ae 100644 --- a/examples/combobox/combobox-autocomplete-both.html +++ b/examples/combobox/combobox-autocomplete-both.html @@ -2,40 +2,36 @@ -Legacy ARIA 1.0 Combobox With Both List and Inline Autocomplete Example | WAI-ARIA Authoring Practices 1.2 +Combobox With Both List and Inline Autocomplete Example | WAI-ARIA Authoring Practices 1.2 - - - - + + + + - + - +
    -

    Legacy ARIA 1.0 Combobox With Both List and Inline Autocomplete Example

    -

    - NOTE: Please provide feedback in - issue 553. -

    +

    Combobox With Both List and Inline Autocomplete Example

    The below combobox for choosing the name of a US state or territory demonstrates the - ARIA 1.0 design pattern for combobox. + ARIA 1.0 design pattern for combobox. The design pattern describes four types of autocomplete behavior. This example illustrates the autocomplete behavior referred to in the pattern as list with inline completion. If the user types one or more characters in the edit box and the typed characters match the beginning of the name of one or more states or territories, @@ -46,10 +42,9 @@

    Legacy ARIA 1.0 Combobox With Both List and Inline Autocomplete Example

    Similar examples include:

    Example

    @@ -59,7 +54,7 @@

    Example

    @@ -131,7 +126,7 @@

    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. + Keyboard Interaction section of the combobox design pattern.

    Textbox

    @@ -196,7 +191,7 @@

    Listbox Popup

    NOTE: When visual focus is in the listbox, DOM focus remains on the textbox and the value of aria-activedescendant on the textbox 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. + Using aria-activedescendant to Manage Focus.

    @@ -280,7 +275,7 @@

    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. + Roles, States, and Properties section of the combobox design pattern.

    Textbox

    @@ -299,12 +294,7 @@

    Textbox

    - + @@ -322,18 +312,13 @@

    Textbox

    - + - + @@ -364,7 +349,7 @@

    Textbox

  • 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. + Using aria-activedescendant to Manage Focus.
  • @@ -432,11 +417,10 @@

    Listbox Popup

    Javascript and CSS Source Code

    -
    • CSS: - combobox-1.0.css + combobox.css
    • CSS: @@ -444,7 +428,7 @@

      Javascript and CSS Source Code

    • Javascript: - combobox-1.0-list.js + combobox-1.0-list.js
    • Javascript: @@ -467,7 +451,6 @@

      HTML Source Code

      - - + + + + - + - +
      -

      Legacy ARIA 1.0 Combobox without Autocomplete Example

      -

      - NOTE: Please provide feedback on this example page in - issue 555. -

      +

      Combobox without Autocomplete Example

      The below combobox that enables users to choose a term from a hypothetical list of previously searched terms demonstrates the - ARIA 1.0 design pattern for combobox. + ARIA design pattern for combobox. The design pattern describes four types of autocomplete behavior. This example illustrates the autocomplete behavior known as no autocomplete. The terms that appear in the listbox popup are not related to the string that is present in the textbox. @@ -43,11 +39,11 @@

      Legacy ARIA 1.0 Combobox without Autocomplete Example

      Similar examples include:

      +

      Example

      @@ -60,7 +56,7 @@

      Example

      aria-autocomplete="none" aria-expanded="false" aria-haspopup="true" - aria-owns="cb1-listbox" + aria-controls="cb1-listbox" />
    input[type="text"] -
      -
    • Identifies the input as a combobox.
    • -
    • Note: The primary difference between the ARIA 1.0 pattern and the ARIA 1.1 pattern is the placement of the combobox role.
    • -
    -
    Identifies the input as a combobox.
    input[type="text"] Indicates that the combobox can popup another element to suggest values.
    aria-owns="#IDREF" input[type="text"] -
      -
    • Identifies the element that serves as the popup.
    • -
    • Note: In the ARIA 1.1 combobox pattern, the combobox uses aria-controls instead of aria-owns.
    • -
    -
    Identifies the element that serves as the popup.
    @@ -143,7 +139,7 @@

    Listbox Popup

    NOTE: When visual focus is in the listbox, DOM focus remains on the textbox and the value of aria-activedescendant on the textbox 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. + Using aria-activedescendant to Manage Focus.

    @@ -227,7 +223,7 @@

    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. + Roles, States, and Properties section of the combobox design pattern.

    Textbox

    @@ -246,12 +242,7 @@

    Textbox

    - + @@ -269,18 +260,13 @@

    Textbox

    - + - + @@ -311,7 +297,7 @@

    Textbox

  • 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. + Using aria-activedescendant to Manage Focus.
  • @@ -382,7 +368,7 @@

    Javascript and CSS Source Code

    • CSS: - combobox-1.0.css + combobox.css
    • CSS: @@ -390,7 +376,7 @@

      Javascript and CSS Source Code

    • Javascript: - combobox-1.0-list.js + combobox-list.js
    • Javascript: @@ -415,7 +401,7 @@

      HTML Source Code

      From 252768bb378877a08d84f8240f6f98090570590f Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 10 Nov 2019 07:31:20 -0600 Subject: [PATCH 10/54] added grid combobox example to branch --- examples/combobox/css/grid-combo.css | 93 +++++ examples/combobox/grid-combo.html | 421 +++++++++++++++++++++ examples/combobox/js/grid-combo-example.js | 98 +++++ examples/combobox/js/grid-combo.js | 312 +++++++++++++++ test/tests/combobox_grid-combo.js | 58 +-- 5 files changed, 941 insertions(+), 41 deletions(-) create mode 100644 examples/combobox/css/grid-combo.css create mode 100644 examples/combobox/grid-combo.html create mode 100644 examples/combobox/js/grid-combo-example.js create mode 100644 examples/combobox/js/grid-combo.js diff --git a/examples/combobox/css/grid-combo.css b/examples/combobox/css/grid-combo.css new file mode 100644 index 0000000000..25e9671a3d --- /dev/null +++ b/examples/combobox/css/grid-combo.css @@ -0,0 +1,93 @@ +.annotate { + font-style: italic; + color: #366ed4; +} + +.hidden { + display: none; +} + +.combobox-wrapper { + display: inline-block; + position: relative; + font-size: 16px; +} + +.combobox-label { + font-size: 14px; + font-weight: bold; + margin-right: 5px; +} + +.listbox, +.grid { + min-width: 230px; + background: white; + border: 1px solid #ccc; + list-style: none; + margin: 0; + padding: 0; + position: absolute; + top: 1.7em; + z-index: 1; +} + +.listbox .result { + cursor: default; + margin: 0; +} + +.grid .result-row { + padding: 2px; + cursor: default; + margin: 0; +} + +.listbox .result:hover, +.grid .result-row:hover { + background: rgb(139, 189, 225); +} + +.listbox .focused, +.grid .focused { + background: rgb(139, 189, 225); +} + +.grid .focused-cell { + outline-style: dotted; + outline-color: green; +} + +.combobox-wrapper input { + font-size: inherit; + border: 1px solid #aaa; + border-radius: 2px; + line-height: 1.5em; + padding-right: 30px; + width: 200px; +} + +.combobox-dropdown { + position: absolute; + right: 0; + top: 0; + padding: 0 0 2px; + height: 1.5em; + border-radius: 0 2px 2px 0; + border: 1px solid #aaa; +} + +.grid .result-cell { + display: inline-block; + cursor: default; + margin: 0; + padding: 0 5px; +} + +.grid .result-cell:last-child { + float: right; + font-size: 12px; + font-weight: 200; + color: #333; + line-height: 24px; +} diff --git a/examples/combobox/grid-combo.html b/examples/combobox/grid-combo.html new file mode 100644 index 0000000000..d7598f685c --- /dev/null +++ b/examples/combobox/grid-combo.html @@ -0,0 +1,421 @@ + + + + +ARIA 1.2 Combobox with Grid Popup Example | WAI-ARIA Authoring Practices 1.1 + + + + + + + + + + + + + + + + +
      +

      ARIA 1.2 Combobox with Grid Popup Example

      +

      + The following example combobox implements the + combobox design pattern + using a grid for the suggested values popup. +

      +

      + In this example, users can specify the name of a fruit or vegetable by either typing a value in the box or choosing from the set of values presented in a grid popup. + The popup becomes available after the textbox contains a character that matches the beginning of the name of one of the items in the set of value suggestions. + Users may type any value in the textbox; this implementation does not limit input to values that are in the set of value suggestions. +

      +

      + The grid that presents suggested values has two columns. + Each row of the grid represents one suggestion; column one contains the name of the fruit or vegetable and column two identifies whether it is a fruit or vegetable. +

      +

      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. +

      +

      Textbox

      +
    input[type="text"] -
      -
    • Identifies the input as a combobox.
    • -
    • Note: The primary difference between the ARIA 1.0 pattern and the ARIA 1.1 pattern is the placement of the combobox role.
    • -
    -
    Identifies the input as a combobox.
    input[type="text"] Indicates that the combobox can popup another element to suggest values.
    - aria-owns="#IDREF" + aria-controls="#IDREF" input[type="text"] -
      -
    • Identifies the element that serves as the popup.
    • -
    • Note: In the ARIA 1.1 combobox pattern, the combobox uses aria-controls instead of aria-owns.
    • -
    -
    Identifies the element that serves as the popup.
    + + + + + + + + + + + + + + + + + + + + + + + + +
    KeyFunction
    Down ArrowIf the grid is displayed, moves focus to the first suggested value.
    Up ArrowIf the grid is displayed, moves focus to the last suggested value.
    Escape +
      +
    • Clears the textbox
    • +
    • If the grid is displayed, closes it.
    • +
    +
    Standard single line text editing keys +
      +
    • Keys used for cursor movement and text manipulation, such as Delete and Shift + Right Arrow.
    • +
    • An HTML input with type=text is used for the textbox so the browser will provide platform-specific editing keys.
    • +
    +
    +

    Grid Popup

    +

    + NOTE: When visual focus is in the grid, DOM focus remains on the textbox and the value of aria-activedescendant on the textbox is set to a value that refers to an element in the grid 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 textbox value to the content of the first cell in the row containing focus.
    • +
    • Closes the grid popup.
    • +
    • Sets focus on the textbox.
    • +
    +
    Escape +
      +
    • Closes the grid popup.
    • +
    • Sets focus on the textbox.
    • +
    • Clears the textbox.
    • +
    +
    Down Arrow +
      +
    • Moves focus to the next row.
    • +
    • If focus is in the last row, moves focus to the first row.
    • +
    • Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
    • +
    +
    Up Arrow +
      +
    • Moves focus to the previous row.
    • +
    • If focus is in the first row, moves focus to the last row.
    • +
    • Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
    • +
    +
    Right Arrow +
      +
    • Moves focus to the next cell.
    • +
    • If focus is in the last cell in a row, moves focus to the first cell in the next row.
    • +
    • If focus is in the last cell in the last row, moves focus to the first cell in the first row.
    • +
    +
    Left Arrow +
      +
    • Moves focus to the previous cell.
    • +
    • If focus is in the first cell in a row, moves focus to the last cell in the previous row.
    • +
    • If focus is in the first cell in the first row, moves focus to the last cell in the last row.
    • +
    +
    HomeMoves focus to the textbox and places the editing cursor at the beginning of the field.
    EndMoves focus to the textbox and places the editing cursor at the end of the field.
    Printable Characters +
      +
    • Moves focus to the textbox.
    • +
    • Types the character in the textbox.
    • +
    +
    +
    + +
    +

    Role, Property, State, and Tabindex Attributes

    +

    + The example comboboxes on this page implement 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. +

    +

    Textbox

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    RoleAttributeElementUsage
    + combobox + input[type="text"] +
      +
    • Identifies the element as a combobox.
    • +
    • Note: Unlike this ARIA 1.2 combobox, an ARIA 1.1 pattern would have the combobox role on a container element of the textbox input instead of the textbox itself.
    + + +
    + aria-haspopup=grid + input[type="text"]Indicates that the combobox can popup a grid to suggest values.
    + aria-expanded=false + input[type="text"]Indicates that the popup element is not displayed.
    + aria-expanded=true + input[type="text"]Indicates that the popup element is displayed.
    + id="string" + input[type="text"] +
      +
    • Referenced by for attribute of label element to provide an accessible label.
    • +
    • Recommended labeling method for HTML input elements so clicking label focuses input.
    • +
    +
    + aria-autocomplete=list + input[type="text"]Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup.
    + aria-controls=IDREF + input[type="text"] +
      +
    • Identifies the popup element that lists suggested values.
    • +
    • Note: +
        +
      • In the ARIA 1.0 combobox pattern, the textbox has aria-owns instead of aria-controls.
      • +
      • In this ARIA 1.1 pattern, aria-owns is instead on the parent container so the popup element is a sibling of the textbox instead of a child of the textbox, making it perceivable by screen reader users as an element adjacent to the textbox when using a reading cursor or touch interface.
      • +
      +
    • +
    +
    + aria-activedescendant=IDREF + input[type="text"] +
      +
    • When a cell in the grid is visually indicated as having keyboard focus, refers to that cell.
    • +
    • 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. +
    • +
    +
    +

    Grid Popup

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    RoleAttributeElementUsage
    + grid + + div + Identifies the element as a grid.
    + aria-labelledby=IDREF + divProvides a label for the grid element of the combobox.
    + row + divIdentifies the element containing all the cells for a row.
    + aria-selected=true + div +
      +
    • Specified on a row in the grid when it is visually indicated as selected.
    • +
    • Occurs only when a cell in the grid is referenced by aria-activedescendant.
    • +
    +
    gridcelldivIdentifies the element containing the content for a single cell.
    +
    + +
    +

    Javascript and CSS Source Code

    + +
    + +
    +

    HTML Source Code

    + +
    + + + +
    +
    + + + diff --git a/examples/combobox/js/grid-combo-example.js b/examples/combobox/js/grid-combo-example.js new file mode 100644 index 0000000000..0f641a2977 --- /dev/null +++ b/examples/combobox/js/grid-combo-example.js @@ -0,0 +1,98 @@ +/* +* This content is licensed according to the W3C Software License at +* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document +* +* ARIA Combobox Examples +*/ + +var FRUITS_AND_VEGGIES = [ + ['Apple', 'Fruit'], + ['Artichoke', 'Vegetable'], + ['Asparagus', 'Vegetable'], + ['Banana', 'Fruit'], + ['Beets', 'Vegetable'], + ['Bell pepper', 'Vegetable'], + ['Broccoli', 'Vegetable'], + ['Brussels sprout', 'Vegetable'], + ['Cabbage', 'Vegetable'], + ['Carrot', 'Vegetable'], + ['Cauliflower', 'Vegetable'], + ['Celery', 'Vegetable'], + ['Chard', 'Vegetable'], + ['Chicory', 'Vegetable'], + ['Corn', 'Vegetable'], + ['Cucumber', 'Vegetable'], + ['Daikon', 'Vegetable'], + ['Date', 'Fruit'], + ['Edamame', 'Vegetable'], + ['Eggplant', 'Vegetable'], + ['Elderberry', 'Fruit'], + ['Fennel', 'Vegetable'], + ['Fig', 'Fruit'], + ['Garlic', 'Vegetable'], + ['Grape', 'Fruit'], + ['Honeydew melon', 'Fruit'], + ['Iceberg lettuce', 'Vegetable'], + ['Jerusalem artichoke', 'Vegetable'], + ['Kale', 'Vegetable'], + ['Kiwi', 'Fruit'], + ['Leek', 'Vegetable'], + ['Lemon', 'Fruit'], + ['Mango', 'Fruit'], + ['Mangosteen', 'Fruit'], + ['Melon', 'Fruit'], + ['Mushroom', 'Fungus'], + ['Nectarine', 'Fruit'], + ['Okra', 'Vegetable'], + ['Olive', 'Vegetable'], + ['Onion', 'Vegetable'], + ['Orange', 'Fruit'], + ['Parship', 'Vegetable'], + ['Pea', 'Vegetable'], + ['Pear', 'Fruit'], + ['Pineapple', 'Fruit'], + ['Potato', 'Vegetable'], + ['Pumpkin', 'Fruit'], + ['Quince', 'Fruit'], + ['Radish', 'Vegetable'], + ['Rhubarb', 'Vegetable'], + ['Shallot', 'Vegetable'], + ['Spinach', 'Vegetable'], + ['Squash', 'Vegetable'], + ['Strawberry', 'Fruit'], + ['Sweet potato', 'Vegetable'], + ['Tomato', 'Fruit'], + ['Turnip', 'Vegetable'], + ['Ugli fruit', 'Fruit'], + ['Victoria plum', 'Fruit'], + ['Watercress', 'Vegetable'], + ['Watermelon', 'Fruit'], + ['Yam', 'Vegetable'], + ['Zucchini', 'Vegetable'] +]; + +function searchVeggies (searchString) { + var results = []; + + for (var i = 0; i < FRUITS_AND_VEGGIES.length; i++) { + var veggie = FRUITS_AND_VEGGIES[i][0].toLowerCase(); + if (veggie.indexOf(searchString.toLowerCase()) === 0) { + results.push(FRUITS_AND_VEGGIES[i]); + } + } + + return results; +} + +/** + * @function onload + * @desc Initialize the combobox examples once the page has loaded + */ +window.addEventListener('load', function () { + var ex1Combobox = new aria.GridCombobox( + document.getElementById('ex1-input'), + document.getElementById('ex1-grid'), + searchVeggies + ); + +}); diff --git a/examples/combobox/js/grid-combo.js b/examples/combobox/js/grid-combo.js new file mode 100644 index 0000000000..7df5aaa7f2 --- /dev/null +++ b/examples/combobox/js/grid-combo.js @@ -0,0 +1,312 @@ +/* +* This content is licensed according to the W3C Software License at +* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document +*/ + +/** + * @constructor + * + * @desc + * Combobox object representing the state and interactions for a combobox + * widget + * + * @param comboboxNode + * The DOM node pointing to the combobox + * @param input + * The input node + * @param grid + * The grid node to load results in + * @param searchFn + * The search function. The function accepts a search string and returns an + * array of results. + */ +aria.GridCombobox = function ( + input, + grid, + searchFn +) { + this.input = input; + this.grid = grid; + this.searchFn = searchFn; + this.activeRowIndex = -1; + this.activeColIndex = 0; + this.rowsCount = 0; + this.colsCount = 0; + this.gridFocused = false; + this.shown = false; + this.selectionCol = 0; + + this.setupEvents(); +}; + +aria.GridCombobox.prototype.setupEvents = function () { + document.body.addEventListener('click', this.handleBodyClick.bind(this)); + this.input.addEventListener('keyup', this.handleInputKeyUp.bind(this)); + this.input.addEventListener('keydown', this.handleInputKeyDown.bind(this)); + this.input.addEventListener('focus', this.handleInputFocus.bind(this)); + this.grid.addEventListener('click', this.handleGridClick.bind(this)); +}; + +aria.GridCombobox.prototype.handleBodyClick = function (evt) { + if (evt.target === this.input || this.grid.contains(evt.target)) { + return; + } + this.hideResults(); +}; + +aria.GridCombobox.prototype.handleInputKeyUp = function (evt) { + var key = evt.which || evt.keyCode; + + switch (key) { + case aria.KeyCode.UP: + case aria.KeyCode.DOWN: + case aria.KeyCode.ESC: + case aria.KeyCode.RETURN: + evt.preventDefault(); + return; + case aria.KeyCode.LEFT: + case aria.KeyCode.RIGHT: + if (this.gridFocused) { + console.log('grid focused'); + evt.preventDefault(); + return; + } + default: + this.updateResults(); + } +}; + + +aria.GridCombobox.prototype.handleInputKeyDown = function (evt) { + var key = evt.which || evt.keyCode; + var activeRowIndex = this.activeRowIndex; + var activeColIndex = this.activeColIndex; + + if (key === aria.KeyCode.ESC) { + if (this.gridFocused) { + this.gridFocused = false; + this.removeFocusCell(this.activeRowIndex, this.activeColIndex); + this.activeRowIndex = -1; + this.activeColIndex = 0; + this.input.setAttribute( + 'aria-activedescendant', + '' + ); + } + else { + this.hideResults(); + setTimeout((function () { + // On Firefox, input does not get cleared here unless wrapped in + // a setTimeout + this.input.value = ''; + }).bind(this), 1); + } + return; + } + + if (this.rowsCount < 1) { + return; + } + + var prevActive = this.getItemAt(activeRowIndex, this.selectionCol); + var activeItem; + + switch (key) { + case aria.KeyCode.UP: + this.gridFocused = true; + activeRowIndex = this.getRowIndex(key); + evt.preventDefault(); + break; + case aria.KeyCode.DOWN: + this.gridFocused = true; + activeRowIndex = this.getRowIndex(key); + evt.preventDefault(); + break; + case aria.KeyCode.LEFT: + if (activeColIndex <= 0) { + activeColIndex = this.colsCount - 1; + activeRowIndex = this.getRowIndex(key); + } + else { + activeColIndex--; + } + if (this.gridFocused) { + evt.preventDefault(); + } + break; + case aria.KeyCode.RIGHT: + if (activeColIndex === -1 || activeColIndex >= this.colsCount - 1) { + activeColIndex = 0; + activeRowIndex = this.getRowIndex(key); + } + else { + activeColIndex++; + } + if (this.gridFocused) { + evt.preventDefault(); + } + break; + case aria.KeyCode.RETURN: + activeItem = this.getItemAt(activeRowIndex, this.selectionCol); + this.selectItem(activeItem); + this.gridFocused = false; + return; + case aria.KeyCode.TAB: + this.hideResults(); + return; + default: + return; + } + + if (prevActive) { + this.removeFocusCell(this.activeRowIndex, this.activeColIndex); + prevActive.setAttribute('aria-selected', 'false'); + } + + activeItem = this.getItemAt(activeRowIndex, activeColIndex); + this.activeRowIndex = activeRowIndex; + this.activeColIndex = activeColIndex; + + if (activeItem) { + this.input.setAttribute( + 'aria-activedescendant', + 'result-item-' + activeRowIndex + 'x' + activeColIndex + ); + this.focusCell(activeRowIndex, activeColIndex); + var selectedItem = this.getItemAt(activeRowIndex, this.selectionCol); + selectedItem.setAttribute('aria-selected', 'true'); + } + else { + this.input.setAttribute( + 'aria-activedescendant', + '' + ); + } +}; + +aria.GridCombobox.prototype.handleInputFocus = function (evt) { + this.updateResults(); +}; + +aria.GridCombobox.prototype.handleGridClick = function (evt) { + if (!evt.target) { + return; + } + + var row; + if (evt.target.getAttribute('role') === 'row') { + row = evt.target; + } + else if (evt.target.getAttribute('role') === 'gridcell') { + row = evt.target.parentNode; + } + else { + return; + } + + var selectItem = row.querySelector('.result-cell'); + this.selectItem(selectItem); +}; + +aria.GridCombobox.prototype.updateResults = function () { + var searchString = this.input.value; + var results = this.searchFn(searchString); + + this.hideResults(); + + if (!searchString) { + results = []; + } + + if (results.length) { + for (var row = 0; row < results.length; row++) { + var resultRow = document.createElement('div'); + resultRow.className = 'result-row'; + resultRow.setAttribute('role', 'row'); + resultRow.setAttribute('id', 'result-row-' + row); + for (var col = 0; col < results[row].length; col++) { + var resultCell = document.createElement('div'); + resultCell.className = 'result-cell'; + resultCell.setAttribute('role', 'gridcell'); + resultCell.setAttribute('id', 'result-item-' + row + 'x' + col); + resultCell.innerText = results[row][col]; + resultRow.appendChild(resultCell); + } + this.grid.appendChild(resultRow); + } + aria.Utils.removeClass(this.grid, 'hidden'); + this.input.setAttribute('aria-expanded', 'true'); + this.rowsCount = results.length; + this.colsCount = results.length ? results[0].length : 0; + this.shown = true; + } +}; + +aria.GridCombobox.prototype.getRowIndex = function (key) { + var activeRowIndex = this.activeRowIndex; + + switch (key) { + case aria.KeyCode.UP: + case aria.KeyCode.LEFT: + if (activeRowIndex <= 0) { + activeRowIndex = this.rowsCount - 1; + } + else { + activeRowIndex--; + } + break; + case aria.KeyCode.DOWN: + case aria.KeyCode.RIGHT: + if (activeRowIndex === -1 || activeRowIndex >= this.rowsCount - 1) { + activeRowIndex = 0; + } + else { + activeRowIndex++; + } + } + + return activeRowIndex; +}; + + +aria.GridCombobox.prototype.getItemAt = function (rowIndex, colIndex) { + return document.getElementById('result-item-' + rowIndex + 'x' + colIndex); +}; + + +aria.GridCombobox.prototype.selectItem = function (item) { + if (item) { + this.input.value = item.innerText; + this.hideResults(); + } +}; + +aria.GridCombobox.prototype.hideResults = function () { + this.gridFocused = false; + this.shown = false; + this.activeRowIndex = -1; + this.activeColIndex = 0; + this.grid.innerHTML = ''; + aria.Utils.addClass(this.grid, 'hidden'); + this.input.setAttribute('aria-expanded', 'false'); + this.rowsCount = 0; + this.colsCount = 0; + this.input.setAttribute( + 'aria-activedescendant', + '' + ); +}; + +aria.GridCombobox.prototype.removeFocusCell = function (rowIndex, colIndex) { + var row = document.getElementById('result-row-' + rowIndex); + aria.Utils.removeClass(row, 'focused'); + var cell = this.getItemAt(rowIndex, colIndex); + aria.Utils.removeClass(cell, 'focused-cell'); +}; + +aria.GridCombobox.prototype.focusCell = function (rowIndex, colIndex) { + var row = document.getElementById('result-row-' + rowIndex); + aria.Utils.addClass(row, 'focused'); + var cell = this.getItemAt(rowIndex, colIndex); + aria.Utils.addClass(cell, 'focused-cell'); +}; diff --git a/test/tests/combobox_grid-combo.js b/test/tests/combobox_grid-combo.js index 7379bba394..798c7ffd15 100644 --- a/test/tests/combobox_grid-combo.js +++ b/test/tests/combobox_grid-combo.js @@ -7,10 +7,9 @@ const assertAttributeValues = require('../util/assertAttributeValues'); const assertAttributeDNE = require('../util/assertAttributeDNE'); const assertAriaRoles = require('../util/assertAriaRoles'); -const exampleFile = 'combobox/aria1.1pattern/grid-combo.html'; +const exampleFile = 'combobox/grid-combo.html'; const ex = { - comboboxSelector: '#ex1 [role="combobox"]', labelSelector: '#ex1 label', textboxSelector: '#ex1 input[type="text"]', gridSelector: '#ex1 [role="grid"]', @@ -46,55 +45,32 @@ const gridcellId = (row, column) => { }; // Attributes -ariaTest('Test for role="combobox"', exampleFile, 'combobox-role', async (t) => { +ariaTest('Test for role="combobox"', exampleFile, 'textbox-role', async (t) => { t.plan(1); - await assertAriaRoles(t, 'ex1', 'combobox', '1', 'div'); + await assertAriaRoles(t, 'ex1', 'combobox', '1', 'input'); }); -ariaTest('"aria-haspopup"=grid on combobox element', exampleFile, 'combobox-aria-haspopup', async (t) => { +ariaTest('"aria-haspopup"=grid on textbox element', exampleFile, 'textbox-aria-haspopup', async (t) => { t.plan(1); - await assertAttributeValues(t, ex.comboboxSelector, 'aria-haspopup', 'grid'); + await assertAttributeValues(t, ex.textboxSelector, 'aria-haspopup', 'grid'); }); -ariaTest('"aria-owns" attribute on combobox element', exampleFile, 'combobox-aria-owns', async (t) => { - t.plan(2); - - const popupId = await t.context.session - .findElement(By.css(ex.comboboxSelector)) - .getAttribute('aria-owns'); - - t.truthy( - popupId, - '"aria-owns" attribute should exist on: ' + ex.comboboxSelector - ); - - const popupElements = await t.context.session - .findElement(By.id('ex1')) - .findElements(By.id(popupId)); - - t.is( - popupElements.length, - 1, - 'There should be a element with id "' + popupId + '" as referenced by the aria-owns attribute' - ); -}); - -ariaTest('"aria-expanded" on combobox element', exampleFile, 'combobox-aria-expanded', async (t) => { +ariaTest('"aria-expanded" on textbox element', exampleFile, 'textbox-aria-expanded', async (t) => { t.plan(4); - const combobox = await t.context.session.findElement(By.css(ex.comboboxSelector)); + const textbox = await t.context.session.findElement(By.css(ex.textboxSelector)); // Check that aria-expanded is false and the grid is not visible before interacting t.is( - await combobox.getAttribute('aria-expanded'), + await textbox.getAttribute('aria-expanded'), 'false', - 'combobox element should have attribute "aria-expanded" set to false by default.' + 'textbox element should have attribute "aria-expanded" set to false by default.' ); const popupId = await t.context.session - .findElement(By.css(ex.comboboxSelector)) - .getAttribute('aria-owns'); + .findElement(By.css(ex.textboxSelector)) + .getAttribute('aria-controls'); const popupElement = await t.context.session .findElement(By.id('ex1')) @@ -114,9 +90,9 @@ ariaTest('"aria-expanded" on combobox element', exampleFile, 'combobox-aria-expa // Check that aria-expanded is true and the grid is visible t.is( - await combobox.getAttribute('aria-expanded'), + await textbox.getAttribute('aria-expanded'), 'true', - 'combobox element should have attribute "aria-expand" set to true after typing.' + 'textbox element should have attribute "aria-expand" set to true after typing.' ); t.true( @@ -175,7 +151,7 @@ ariaTest('"aria-controls" attribute on grid element', exampleFile, 'textbox-aria ); }); -ariaTest('"aria-activedescendant" on combobox element', exampleFile, 'textbox-aria-activedescendant', async (t) => { +ariaTest('"aria-activedescendant" on textbox element', exampleFile, 'textbox-aria-activedescendant', async (t) => { t.plan(1); await assertAttributeValues(t, ex.textboxSelector, 'aria-activedescendant', null); }); @@ -504,7 +480,7 @@ ariaTest('Test enter key press with focus on grid', // Confirm that the grid is closed - await assertAttributeValues(t, ex.comboboxSelector, 'aria-expanded', 'false'); + await assertAttributeValues(t, ex.textboxSelector, 'aria-expanded', 'false'); // Confirm that the value of the textbox is now set to the first option @@ -537,7 +513,7 @@ ariaTest('Test escape key press with focus on textbox', ); // Confirm the grid is closed and the textboxed is cleared - await assertAttributeValues(t, ex.comboboxSelector, 'aria-expanded', 'false'); + await assertAttributeValues(t, ex.textboxSelector, 'aria-expanded', 'false'); t.is( await t.context.session .findElement(By.css(ex.textboxSelector)) @@ -576,7 +552,7 @@ ariaTest.failing('Test escape key press with focus on popup', ); // Confirm the grid is closed and the textboxed is cleared - await assertAttributeValues(t, ex.comboboxSelector, 'aria-expanded', 'false'); + await assertAttributeValues(t, ex.textboxSelector, 'aria-expanded', 'false'); t.is( await t.context.session From 673d6abd444f9563ea0d30dfea691a04982e221a Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 10 Nov 2019 07:32:40 -0600 Subject: [PATCH 11/54] added grid combobox example to branch --- examples/combobox/js/grid-combo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/combobox/js/grid-combo.js b/examples/combobox/js/grid-combo.js index 7df5aaa7f2..2cf83afa47 100644 --- a/examples/combobox/js/grid-combo.js +++ b/examples/combobox/js/grid-combo.js @@ -67,10 +67,10 @@ aria.GridCombobox.prototype.handleInputKeyUp = function (evt) { case aria.KeyCode.LEFT: case aria.KeyCode.RIGHT: if (this.gridFocused) { - console.log('grid focused'); evt.preventDefault(); return; } + break; default: this.updateResults(); } From 34e1afc67152cd952f3c7dc058ee947b9e3e4e11 Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 10 Nov 2019 21:21:53 -0600 Subject: [PATCH 12/54] updated regression tests for updated autocomplete both, list and none examples --- test/tests/combobox_autocomplete-both.js | 13 +++++++------ test/tests/combobox_autocomplete-list.js | 12 ++++++------ test/tests/combobox_autocomplete-none.js | 12 ++++++------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/test/tests/combobox_autocomplete-both.js b/test/tests/combobox_autocomplete-both.js index 40007397f6..7ee38a4af3 100644 --- a/test/tests/combobox_autocomplete-both.js +++ b/test/tests/combobox_autocomplete-both.js @@ -7,7 +7,7 @@ const assertAriaLabelExists = require('../util/assertAriaLabelExists'); const assertAriaRoles = require('../util/assertAriaRoles'); const assertAriaSelectedAndActivedescendant = require('../util/assertAriaSelectedAndActivedescendant'); -const exampleFile = 'combobox/aria1.0pattern/combobox-autocomplete-both.html'; +const exampleFile = 'combobox/combobox-autocomplete-both.html'; const ex = { textboxSelector: '#ex1 input[type="text"]', @@ -36,6 +36,7 @@ const confirmCursorIndex = async (t, selector, cursorIndex) => { }; // Attributes + ariaTest('Test for role="combobox"', exampleFile, 'combobox-role', async (t) => { t.plan(1); await assertAriaRoles(t, 'ex1', 'combobox', '1', 'input'); @@ -51,16 +52,16 @@ ariaTest('"aria-haspopup" on combobox element', exampleFile, 'combobox-aria-hasp await assertAttributeValues(t, ex.textboxSelector, 'aria-haspopup', 'true'); }); -ariaTest('"aria-owns" attribute on combobox element', exampleFile, 'combobox-aria-owns', async (t) => { +ariaTest('"aria-controls" attribute on combobox element', exampleFile, 'combobox-aria-controls', async (t) => { t.plan(2); const popupId = await t.context.session .findElement(By.css(ex.textboxSelector)) - .getAttribute('aria-owns'); + .getAttribute('aria-controls'); t.truthy( popupId, - '"aria-owns" attribute should exist on: ' + ex.textboxSelector + '"aria-controls" attribute should exist on: ' + ex.textboxSelector ); const popupElements = await t.context.session @@ -70,7 +71,7 @@ ariaTest('"aria-owns" attribute on combobox element', exampleFile, 'combobox-ari t.is( popupElements.length, 1, - 'There should be a element with id "' + popupId + '" as referenced by the aria-owns attribute' + 'There should be a element with id "' + popupId + '" as referenced by the aria-controls attribute' ); }); @@ -89,7 +90,7 @@ ariaTest('"aria-expanded" on combobox element', exampleFile, 'combobox-aria-expa const popupId = await t.context.session .findElement(By.css(ex.textboxSelector)) - .getAttribute('aria-owns'); + .getAttribute('aria-controls'); const popupElement = await t.context.session .findElement(By.id('ex1')) diff --git a/test/tests/combobox_autocomplete-list.js b/test/tests/combobox_autocomplete-list.js index 11741f4231..4f7e5957c9 100644 --- a/test/tests/combobox_autocomplete-list.js +++ b/test/tests/combobox_autocomplete-list.js @@ -8,7 +8,7 @@ const assertAriaLabelExists = require('../util/assertAriaLabelExists'); const assertAriaRoles = require('../util/assertAriaRoles'); const assertAriaSelectedAndActivedescendant = require('../util/assertAriaSelectedAndActivedescendant'); -const exampleFile = 'combobox/aria1.0pattern/combobox-autocomplete-list.html'; +const exampleFile = 'combobox/combobox-autocomplete-list.html'; const ex = { textboxSelector: '#ex1 input[type="text"]', @@ -54,16 +54,16 @@ ariaTest('"aria-haspopup" on combobox element', exampleFile, 'combobox-aria-hasp await assertAttributeValues(t, ex.textboxSelector, 'aria-haspopup', 'true'); }); -ariaTest('"aria-owns" attribute on combobox element', exampleFile, 'combobox-aria-owns', async (t) => { +ariaTest('"aria-controls" attribute on combobox element', exampleFile, 'combobox-aria-controls', async (t) => { t.plan(2); const popupId = await t.context.session .findElement(By.css(ex.textboxSelector)) - .getAttribute('aria-owns'); + .getAttribute('aria-controls'); t.truthy( popupId, - '"aria-owns" attribute should exist on: ' + ex.textboxSelector + '"aria-controls" attribute should exist on: ' + ex.textboxSelector ); const popupElements = await t.context.session @@ -73,7 +73,7 @@ ariaTest('"aria-owns" attribute on combobox element', exampleFile, 'combobox-ari t.is( popupElements.length, 1, - 'There should be a element with id "' + popupId + '" as referenced by the aria-owns attribute' + 'There should be a element with id "' + popupId + '" as referenced by the aria-controls attribute' ); }); @@ -92,7 +92,7 @@ ariaTest('"aria-expanded" on combobox element', exampleFile, 'combobox-aria-expa const popupId = await t.context.session .findElement(By.css(ex.textboxSelector)) - .getAttribute('aria-owns'); + .getAttribute('aria-controls'); const popupElement = await t.context.session .findElement(By.id('ex1')) diff --git a/test/tests/combobox_autocomplete-none.js b/test/tests/combobox_autocomplete-none.js index 61d912d11e..71053d1354 100644 --- a/test/tests/combobox_autocomplete-none.js +++ b/test/tests/combobox_autocomplete-none.js @@ -8,7 +8,7 @@ const assertAriaLabelExists = require('../util/assertAriaLabelExists'); const assertAriaRoles = require('../util/assertAriaRoles'); const assertAriaSelectedAndActivedescendant = require('../util/assertAriaSelectedAndActivedescendant'); -const exampleFile = 'combobox/aria1.0pattern/combobox-autocomplete-none.html'; +const exampleFile = 'combobox/combobox-autocomplete-none.html'; const ex = { textboxSelector: '#ex1 input[type="text"]', @@ -54,16 +54,16 @@ ariaTest('"aria-haspopup" on combobox element', exampleFile, 'combobox-aria-hasp await assertAttributeValues(t, ex.textboxSelector, 'aria-haspopup', 'true'); }); -ariaTest('"aria-owns" attribute on combobox element', exampleFile, 'combobox-aria-owns', async (t) => { +ariaTest('"aria-controls" attribute on combobox element', exampleFile, 'combobox-aria-controls', async (t) => { t.plan(2); const popupId = await t.context.session .findElement(By.css(ex.textboxSelector)) - .getAttribute('aria-owns'); + .getAttribute('aria-controls'); t.truthy( popupId, - '"aria-owns" attribute should exist on: ' + ex.textboxSelector + '"aria-controls" attribute should exist on: ' + ex.textboxSelector ); const popupElements = await t.context.session @@ -73,7 +73,7 @@ ariaTest('"aria-owns" attribute on combobox element', exampleFile, 'combobox-ari t.is( popupElements.length, 1, - 'There should be a element with id "' + popupId + '" as referenced by the aria-owns attribute' + 'There should be a element with id "' + popupId + '" as referenced by the aria-controls attribute' ); }); @@ -92,7 +92,7 @@ ariaTest('"aria-expanded" on combobox element', exampleFile, 'combobox-aria-expa const popupId = await t.context.session .findElement(By.css(ex.textboxSelector)) - .getAttribute('aria-owns'); + .getAttribute('aria-controls'); const popupElement = await t.context.session .findElement(By.id('ex1')) From 4057291eeb48863a415f72e9f0081176951f2d07 Mon Sep 17 00:00:00 2001 From: Matt King Date: Sun, 10 Nov 2019 21:03:37 -0800 Subject: [PATCH 13/54] Remove ARIA 1.2 from grid combo title and change version in title tag to 1.2 --- examples/combobox/grid-combo.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/combobox/grid-combo.html b/examples/combobox/grid-combo.html index d7598f685c..5137caf25f 100644 --- a/examples/combobox/grid-combo.html +++ b/examples/combobox/grid-combo.html @@ -2,7 +2,7 @@ -ARIA 1.2 Combobox with Grid Popup Example | WAI-ARIA Authoring Practices 1.1 +Combobox with Grid Popup Example | WAI-ARIA Authoring Practices 1.2 @@ -27,7 +27,7 @@
-

ARIA 1.2 Combobox with Grid Popup Example

+

Combobox with Grid Popup Example

The following example combobox implements the combobox design pattern From 478787307ed718674379f0bfefba46b1999d4346 Mon Sep 17 00:00:00 2001 From: Matt King Date: Sun, 10 Nov 2019 21:07:26 -0800 Subject: [PATCH 14/54] Update list of similar examples and fix design pattern URI --- examples/combobox/grid-combo.html | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/combobox/grid-combo.html b/examples/combobox/grid-combo.html index 5137caf25f..3bc642aa71 100644 --- a/examples/combobox/grid-combo.html +++ b/examples/combobox/grid-combo.html @@ -30,7 +30,7 @@

Combobox with Grid Popup Example

The following example combobox implements the - combobox design pattern + combobox design pattern using a grid for the suggested values popup.

@@ -44,8 +44,11 @@

Combobox with Grid Popup Example

Similar examples include:

+

Example

From 5cf2c0e17218a96ed91153add29bc3d9c876903b Mon Sep 17 00:00:00 2001 From: Matt King Date: Sun, 10 Nov 2019 21:15:12 -0800 Subject: [PATCH 15/54] Fix URIs for main doc in keyboard section --- examples/combobox/grid-combo.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/combobox/grid-combo.html b/examples/combobox/grid-combo.html index 3bc642aa71..3e7b34e039 100644 --- a/examples/combobox/grid-combo.html +++ b/examples/combobox/grid-combo.html @@ -82,7 +82,7 @@

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. + Keyboard Interaction section of the combobox design pattern.

Textbox

@@ -126,7 +126,7 @@

Grid Popup

NOTE: When visual focus is in the grid, DOM focus remains on the textbox and the value of aria-activedescendant on the textbox is set to a value that refers to an element in the grid 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. + Using aria-activedescendant to Manage Focus.

From d0187c3d923e09d972e9ca1e5df3f07b6596d2de Mon Sep 17 00:00:00 2001 From: Matt King Date: Sun, 10 Nov 2019 22:37:33 -0800 Subject: [PATCH 16/54] Editorial revisions to states and props table in grid combo page --- examples/combobox/grid-combo.html | 64 ++++++++++++------------------- 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/examples/combobox/grid-combo.html b/examples/combobox/grid-combo.html index 3e7b34e039..8286cfaef4 100644 --- a/examples/combobox/grid-combo.html +++ b/examples/combobox/grid-combo.html @@ -222,7 +222,7 @@

Role, Property, State, and Tabindex Attributes

The example comboboxes on this page implement 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. + Roles, States, and Properties section of the combobox design pattern.

Textbox

@@ -235,45 +235,39 @@

Textbox

- + - + - + - + - + - + - + - + - + - + - + @@ -356,10 +340,10 @@

Grid Popup

- +
combobox input[type="text"] -
    -
  • Identifies the element as a combobox.
  • -
  • Note: Unlike this ARIA 1.2 combobox, an ARIA 1.1 pattern would have the combobox role on a container element of the textbox input instead of the textbox itself.
- - -
Identifies the element as a combobox.
- aria-haspopup=grid + aria-haspopup="grid" input[type="text"] Indicates that the combobox can popup a grid to suggest values.
- aria-expanded=false + aria-expanded="false" input[type="text"] Indicates that the popup element is not displayed.
- aria-expanded=true + aria-expanded="true" input[type="text"] Indicates that the popup element is displayed.
id="string" @@ -281,41 +275,31 @@

Textbox

input[type="text"]
    -
  • Referenced by for attribute of label element to provide an accessible label.
  • -
  • Recommended labeling method for HTML input elements so clicking label focuses input.
  • +
  • Referenced by for attribute of label element to provide an accessible name.
  • +
  • Recommended naming method for HTML input elements because clicking label focuses input.
- aria-autocomplete=list + aria-autocomplete="list" input[type="text"]Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup.Indicates that the autocomplete behavior of the input is to suggest a list of possible values in a popup.
- aria-controls=IDREF + aria-controls="IDREF" input[type="text"] -
    -
  • Identifies the popup element that lists suggested values.
  • -
  • Note: -
      -
    • In the ARIA 1.0 combobox pattern, the textbox has aria-owns instead of aria-controls.
    • -
    • In this ARIA 1.1 pattern, aria-owns is instead on the parent container so the popup element is a sibling of the textbox instead of a child of the textbox, making it perceivable by screen reader users as an element adjacent to the textbox when using a reading cursor or touch interface.
    • -
    -
  • -
-
Identifies the popup element that lists suggested values.
- aria-activedescendant=IDREF + aria-activedescendant="IDREF" input[type="text"] @@ -325,7 +309,7 @@

Textbox

  • 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. + Using aria-activedescendant to Manage Focus.
  • - aria-labelledby=IDREF + aria-labelledby="IDREF" divProvides a label for the grid element of the combobox.Provides a label for the grid element controlled by the combobox.
    @@ -372,7 +356,7 @@

    Grid Popup

    - aria-selected=true + aria-selected="true" div From e7369da4f642eec6e4892b3bbb5d26d2f80755e1 Mon Sep 17 00:00:00 2001 From: Matt King Date: Sun, 10 Nov 2019 22:39:37 -0800 Subject: [PATCH 17/54] Update footer nav link in grid combo page --- examples/combobox/grid-combo.html | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/examples/combobox/grid-combo.html b/examples/combobox/grid-combo.html index 8286cfaef4..3f8a418534 100644 --- a/examples/combobox/grid-combo.html +++ b/examples/combobox/grid-combo.html @@ -390,11 +390,6 @@

    HTML Source Code

    - @@ -50,12 +49,11 @@

    Editable Combobox With Both List and Inline Autocomplete Example

    Example

    -
    - + +
    + aria-expanded="false" aria-controls="lb1">
      diff --git a/examples/combobox/combobox-autocomplete-list.html b/examples/combobox/combobox-autocomplete-list.html index 2bd68d9a08..968624745e 100644 --- a/examples/combobox/combobox-autocomplete-list.html +++ b/examples/combobox/combobox-autocomplete-list.html @@ -13,7 +13,6 @@ - @@ -45,17 +44,16 @@

      Editable Combobox With List Autocomplete Example

    • 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.
    - +

    Example

    -
    +
    + aria-expanded="false" aria-controls="cb1-listbox">
      diff --git a/examples/combobox/combobox-autocomplete-none.html b/examples/combobox/combobox-autocomplete-none.html index 6c25e0c5e3..5b7a0e8cee 100644 --- a/examples/combobox/combobox-autocomplete-none.html +++ b/examples/combobox/combobox-autocomplete-none.html @@ -13,7 +13,6 @@ - @@ -43,23 +42,20 @@

      Editable Combobox without Autocomplete Example

    • 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.
    - +

    Example

    -
    +
    - + aria-controls="cb1-listbox"> +
    • weather
    • diff --git a/examples/combobox/css/combobox.css b/examples/combobox/css/combobox.css index 4240f8a17a..9dd5521330 100644 --- a/examples/combobox/css/combobox.css +++ b/examples/combobox/css/combobox.css @@ -1,36 +1,107 @@ -.annotate { - font-style: italic; - color: #366ed4; -} - .combobox-list { position: relative; } -.combobox-inline label, -.combobox-list label { +.combobox .group { + display: inline-flex; +} + +.combobox .group input, +.combobox .group button { + background-color: white; + color: black; + box-sizing: border-box; + height: 1.5rem; + padding: 0 0.25em; margin: 0; - padding: 0; - display: block; + vertical-align: bottom; + border: 1px solid gray; } -.combobox-list .group input, -.combobox-list .group button { - display: inline; - background-color: #eee; +.combobox .group input { + width: 10rem; + border-right: none; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + outline: none; +} + +.combobox .group button { + width: 1.5rem; + border-left: none; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + outline: none; + font-size: 85%; } -.combobox-list .group input.focus { +.combobox .group.focus input, +.combobox .group.focus button { background-color: #d6e8f5; - outline-color: #348ccb; + border-color: #348ccb; +} + +.combobox .group.focus { + outline: 2px solid black; + outline-offset: 1px; } -.combobox-list .group button { +ul[role="listbox"] { margin: 0; - padding: 0 0.125em 0 0.125em; - position: relative; - top: 1px; - left: -2px; - font-size: 85%; + padding: 0; + position: absolute; + left: 0; + top: 1.5rem; + list-style: none; background-color: #eee; + display: none; + box-sizing: border-box; + border: 1px gray solid; + border-radius: 3px; + max-height: 12em; + width: 11.5rem; + overflow: scroll; + overflow-x: hidden; +} + +ul[role="listbox"] li[role="option"] { + margin: 0; + padding: 0; + padding-left: 0.125em; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} + +[role="listbox"].focus { + border-color: #348ccb; +} + +[role="listbox"] [role="option"] { + display: block; + margin: 0.25em; + padding: 0; + background-color: #eee; + font-size: 100%; +} + +/* focus and hover styling */ + +[role="listbox"] [role="option"][aria-selected="true"] { + background-color: #ccc; +} + +[role="listbox"].focus [role="option"][aria-selected="true"] { + background-color: #aed2ea; + border-color: #348ccb; +} + +@media (forced-colors: active), (-ms-high-contrast: active) { + [role="listbox"].focus [role="option"][aria-selected="true"] { + background-color: highlight; + color: highlighttext; + } +} + +[role="listbox"] li[role="option"]:hover { + background-color: #c2ddef; } diff --git a/examples/combobox/css/grid-combo.css b/examples/combobox/css/grid-combo.css index 25e9671a3d..29324bea16 100644 --- a/examples/combobox/css/grid-combo.css +++ b/examples/combobox/css/grid-combo.css @@ -1,8 +1,3 @@ -.annotate { - font-style: italic; - color: #366ed4; -} - .hidden { display: none; } diff --git a/examples/combobox/css/listbox.css b/examples/combobox/css/listbox.css deleted file mode 100644 index fb238fc330..0000000000 --- a/examples/combobox/css/listbox.css +++ /dev/null @@ -1,60 +0,0 @@ -ul[role="listbox"] { - margin: 0; - padding: 0; - position: absolute; - top: 3em; - list-style: none; - background-color: #eee; - display: none; - border: 2px #333 solid; - height: 12em; - width: 10em; - overflow: scroll; -} - -ul[role="listbox"] li[role="option"] { - margin: 0; - padding: 0; - padding-left: 0.125em; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; -} - -[role="listbox"].focus { - border-color: #348ccb; -} - -[role="listbox"] [role="option"] { - display: block; - margin: 0.25em; - padding: 0; - background-color: #eee; - font-size: 100%; -} - -/* focus and hover styling */ - -button:focus, -button:hover, -input:focus, -input:hover { - outline: 2px solid black; -} - -input:focus, -input:hover { - background-color: #eee; -} - -[role="listbox"] [role="option"][aria-selected="true"] { - background-color: #ccc; -} - -[role="listbox"].focus [role="option"][aria-selected="true"] { - background-color: #aed2ea; - border-color: #348ccb; -} - -[role="listbox"] li[role="option"]:hover { - background-color: #c2ddef; -} diff --git a/examples/combobox/grid-combo.html b/examples/combobox/grid-combo.html index 5c877032ba..4f673657e9 100644 --- a/examples/combobox/grid-combo.html +++ b/examples/combobox/grid-combo.html @@ -48,7 +48,7 @@

      Editable Combobox with Grid Popup Example

    • 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.
    - +

    Example

    diff --git a/examples/combobox/js/combobox-list.js b/examples/combobox/js/combobox-list.js index 5f4c4073e4..ffaad6dfc1 100644 --- a/examples/combobox/js/combobox-list.js +++ b/examples/combobox/js/combobox-list.js @@ -117,13 +117,13 @@ ComboboxList.prototype.setOption = function (option, flag) { ComboboxList.prototype.setVisualFocusTextbox = function () { this.listbox.domNode.classList.remove('focus'); this.listbox.hasFocus = false; - this.domNode.classList.add('focus'); + this.domNode.parentNode.classList.add('focus'); // set the focus class to the parent for easier styling this.hasFocus = true; this.setActiveDescendant(false); }; ComboboxList.prototype.setVisualFocusListbox = function () { - this.domNode.classList.remove('focus'); + this.domNode.parentNode.classList.remove('focus'); this.hasFocus = false; this.listbox.domNode.classList.add('focus'); this.listbox.hasFocus = true; @@ -131,7 +131,7 @@ ComboboxList.prototype.setVisualFocusListbox = function () { }; ComboboxList.prototype.removeVisualFocusAll = function () { - this.domNode.classList.remove('focus'); + this.domNode.parentNode.classList.remove('focus'); this.hasFocus = false; this.listbox.domNode.classList.remove('focus'); this.listbox.hasFocus = true; From d2f0e5a45d31c3976417b33242bc2fb6b2fc554b Mon Sep 17 00:00:00 2001 From: Simon Pieters Date: Thu, 14 Nov 2019 17:09:29 +0100 Subject: [PATCH 51/54] Tweak styles of combobox examples 1-3 to look nicer --- .../combobox/combobox-autocomplete-both.html | 2 +- .../combobox/combobox-autocomplete-list.html | 2 +- .../combobox/combobox-autocomplete-none.html | 2 +- examples/combobox/css/combobox.css | 59 ++++++------------- 4 files changed, 22 insertions(+), 43 deletions(-) diff --git a/examples/combobox/combobox-autocomplete-both.html b/examples/combobox/combobox-autocomplete-both.html index a0d0391754..0a4b273c27 100644 --- a/examples/combobox/combobox-autocomplete-both.html +++ b/examples/combobox/combobox-autocomplete-both.html @@ -54,7 +54,7 @@

    Example

    - +
    • Alabama
    • diff --git a/examples/combobox/combobox-autocomplete-list.html b/examples/combobox/combobox-autocomplete-list.html index 968624745e..2d31e0eb34 100644 --- a/examples/combobox/combobox-autocomplete-list.html +++ b/examples/combobox/combobox-autocomplete-list.html @@ -54,7 +54,7 @@

      Example

      - +
      • Alabama
      • diff --git a/examples/combobox/combobox-autocomplete-none.html b/examples/combobox/combobox-autocomplete-none.html index 5b7a0e8cee..d7c97739b0 100644 --- a/examples/combobox/combobox-autocomplete-none.html +++ b/examples/combobox/combobox-autocomplete-none.html @@ -55,7 +55,7 @@

        Example

        aria-autocomplete="none" aria-expanded="false" aria-controls="cb1-listbox"> - +
    • weather
    • diff --git a/examples/combobox/css/combobox.css b/examples/combobox/css/combobox.css index 9dd5521330..d437c332ab 100644 --- a/examples/combobox/css/combobox.css +++ b/examples/combobox/css/combobox.css @@ -11,34 +11,26 @@ background-color: white; color: black; box-sizing: border-box; - height: 1.5rem; - padding: 0 0.25em; + height: 1.75rem; + padding: 0; margin: 0; vertical-align: bottom; border: 1px solid gray; } .combobox .group input { - width: 10rem; + width: 10.75rem; border-right: none; - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; outline: none; + font-size: 87.5%; + padding: 0.1em 0.3em; } .combobox .group button { - width: 1.5rem; + width: 1.25rem; border-left: none; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; outline: none; - font-size: 85%; -} - -.combobox .group.focus input, -.combobox .group.focus button { - background-color: #d6e8f5; - border-color: #348ccb; + font-size: 70%; } .combobox .group.focus { @@ -51,57 +43,44 @@ ul[role="listbox"] { padding: 0; position: absolute; left: 0; - top: 1.5rem; + top: 1.75rem; list-style: none; - background-color: #eee; + background-color: white; display: none; box-sizing: border-box; border: 1px gray solid; - border-radius: 3px; + border-top: none; max-height: 12em; - width: 11.5rem; + width: 12rem; overflow: scroll; overflow-x: hidden; + font-size: 87.5%; } ul[role="listbox"] li[role="option"] { - margin: 0; - padding: 0; - padding-left: 0.125em; border-top: 1px solid transparent; border-bottom: 1px solid transparent; -} - -[role="listbox"].focus { - border-color: #348ccb; -} - -[role="listbox"] [role="option"] { display: block; - margin: 0.25em; - padding: 0; - background-color: #eee; - font-size: 100%; + margin: 0; + padding: 0.1em 0.3em; } /* focus and hover styling */ -[role="listbox"] [role="option"][aria-selected="true"] { - background-color: #ccc; -} - [role="listbox"].focus [role="option"][aria-selected="true"] { - background-color: #aed2ea; - border-color: #348ccb; + background-color: #DEF; + border-color: #8CCBF2; } @media (forced-colors: active), (-ms-high-contrast: active) { [role="listbox"].focus [role="option"][aria-selected="true"] { + -ms-high-contrast-adjust: none; /* disable the backgrounds that Edge puts behind text */ background-color: highlight; color: highlighttext; + border-color: currentcolor; } } [role="listbox"] li[role="option"]:hover { - background-color: #c2ddef; + background-color: #DEF; } From ccd56b7bf239c85a191135fb8476ea0b3c4f6a78 Mon Sep 17 00:00:00 2001 From: Sarah Higley Date: Thu, 14 Nov 2019 09:54:22 -0800 Subject: [PATCH 52/54] fix constant option remove/append bug, fix label styling --- .../combobox/combobox-autocomplete-list.html | 2 +- .../combobox/combobox-autocomplete-none.html | 2 +- examples/combobox/js/combobox-list.js | 100 +++++++++++------- examples/combobox/js/listbox.js | 3 +- 4 files changed, 66 insertions(+), 41 deletions(-) diff --git a/examples/combobox/combobox-autocomplete-list.html b/examples/combobox/combobox-autocomplete-list.html index 2d31e0eb34..ed807f249a 100644 --- a/examples/combobox/combobox-autocomplete-list.html +++ b/examples/combobox/combobox-autocomplete-list.html @@ -49,8 +49,8 @@

      Editable Combobox With List Autocomplete Example

      Example

      +
      -
      diff --git a/examples/combobox/combobox-autocomplete-none.html b/examples/combobox/combobox-autocomplete-none.html index d7c97739b0..05ad2d7d92 100644 --- a/examples/combobox/combobox-autocomplete-none.html +++ b/examples/combobox/combobox-autocomplete-none.html @@ -47,8 +47,8 @@

      Editable Combobox without Autocomplete Example

      Example

      +
      -
      Date: Thu, 14 Nov 2019 10:15:48 -0800 Subject: [PATCH 53/54] Remove tests for aria-haspopup on listbox examples --- test/tests/combobox_autocomplete-both.js | 5 ----- test/tests/combobox_autocomplete-list.js | 5 ----- test/tests/combobox_autocomplete-none.js | 5 ----- 3 files changed, 15 deletions(-) diff --git a/test/tests/combobox_autocomplete-both.js b/test/tests/combobox_autocomplete-both.js index 53714385ef..879c30389a 100644 --- a/test/tests/combobox_autocomplete-both.js +++ b/test/tests/combobox_autocomplete-both.js @@ -47,11 +47,6 @@ ariaTest('"aria-autocomplete" on comboxbox element', exampleFile, 'combobox-aria await assertAttributeValues(t, ex.textboxSelector, 'aria-autocomplete', 'both'); }); -ariaTest('"aria-haspopup" on combobox element', exampleFile, 'combobox-aria-haspopup', async (t) => { - t.plan(1); - await assertAttributeValues(t, ex.textboxSelector, 'aria-haspopup', 'true'); -}); - ariaTest('"aria-controls" attribute on combobox element', exampleFile, 'combobox-aria-controls', async (t) => { t.plan(2); diff --git a/test/tests/combobox_autocomplete-list.js b/test/tests/combobox_autocomplete-list.js index 4f7e5957c9..63e3631c50 100644 --- a/test/tests/combobox_autocomplete-list.js +++ b/test/tests/combobox_autocomplete-list.js @@ -49,11 +49,6 @@ ariaTest('"aria-autocomplete" on comboxbox element', exampleFile, 'combobox-aria await assertAttributeValues(t, ex.textboxSelector, 'aria-autocomplete', 'list'); }); -ariaTest('"aria-haspopup" on combobox element', exampleFile, 'combobox-aria-haspopup', async (t) => { - t.plan(1); - await assertAttributeValues(t, ex.textboxSelector, 'aria-haspopup', 'true'); -}); - ariaTest('"aria-controls" attribute on combobox element', exampleFile, 'combobox-aria-controls', async (t) => { t.plan(2); diff --git a/test/tests/combobox_autocomplete-none.js b/test/tests/combobox_autocomplete-none.js index 71053d1354..0119089cfd 100644 --- a/test/tests/combobox_autocomplete-none.js +++ b/test/tests/combobox_autocomplete-none.js @@ -49,11 +49,6 @@ ariaTest('"aria-autocomplete" on comboxbox element', exampleFile, 'combobox-aria await assertAttributeValues(t, ex.textboxSelector, 'aria-autocomplete', 'none'); }); -ariaTest('"aria-haspopup" on combobox element', exampleFile, 'combobox-aria-haspopup', async (t) => { - t.plan(1); - await assertAttributeValues(t, ex.textboxSelector, 'aria-haspopup', 'true'); -}); - ariaTest('"aria-controls" attribute on combobox element', exampleFile, 'combobox-aria-controls', async (t) => { t.plan(2); From f5353541b6b19078e4ee39a9bf75bce5f24b2d42 Mon Sep 17 00:00:00 2001 From: Simon Pieters Date: Thu, 14 Nov 2019 20:38:46 +0100 Subject: [PATCH 54/54] Fix stylelint config to correctly ignore only forced-colors media feature --- .stylelintrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.stylelintrc b/.stylelintrc index 366b807b53..1796dab3a0 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -1,5 +1,6 @@ { "rules": { - "media-feature-name-whitelist": ["forced-colors", "-ms-high-contrast"] + "media-feature-name-no-unknown": [true, + "ignoreMediaFeatureNames": ["forced-colors"] ] } }