From a71342031b2a34e0bf4b71c82645ea3e94cba988 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Thu, 16 Feb 2023 20:43:49 -0600 Subject: [PATCH 01/52] updated listbox example to fix focus visibility issues and update coding practices and documentation --- .eslintrc.json | 2 +- .../patterns/listbox/examples/css/listbox.css | 11 +- .../examples/js/listbox-collapsible.js | 112 +- .../examples/js/listbox-rearrangeable.js | 15 +- .../listbox/examples/js/listbox-scrollable.js | 6 +- .../patterns/listbox/examples/js/listbox.js | 1127 ++++++++--------- .../patterns/listbox/examples/js/toolbar.js | 188 +-- .../listbox/examples/listbox-collapsible.html | 28 +- .../listbox/examples/listbox-grouped.html | 29 +- .../examples/listbox-rearrangeable.html | 22 +- .../listbox/examples/listbox-scrollable.html | 28 +- 11 files changed, 830 insertions(+), 738 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 02e73bdf48..138df8550a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -61,7 +61,7 @@ }, "rules": { "strict": 0, - "no-unused-vars": ["error", { "varsIgnorePattern": "SkipToConfig" }] + "no-unused-vars": ["error", { "varsIgnorePattern": "SkipTo" }] } }, { diff --git a/content/patterns/listbox/examples/css/listbox.css b/content/patterns/listbox/examples/css/listbox.css index 7fc920c580..30b5fc6645 100644 --- a/content/patterns/listbox/examples/css/listbox.css +++ b/content/patterns/listbox/examples/css/listbox.css @@ -44,14 +44,21 @@ [role="option"] { position: relative; display: block; - padding: 0 1em 0 1.5em; + margin: 2px; + padding: 2px 1em 2px 1.5em; line-height: 1.8em; + cursor: pointer; } -[role="option"].focused { +[role="listbox"]:focus [role="option"].focused { background: #bde4ff; } +[role="listbox"]:focus [role="option"].focused, +[role="option"]:hover { + outline: 2px solid currentcolor; +} + [role="option"][aria-selected="true"]::before { position: absolute; left: 0.5em; diff --git a/content/patterns/listbox/examples/js/listbox-collapsible.js b/content/patterns/listbox/examples/js/listbox-collapsible.js index 4c5733de93..f908d1c55a 100644 --- a/content/patterns/listbox/examples/js/listbox-collapsible.js +++ b/content/patterns/listbox/examples/js/listbox-collapsible.js @@ -6,71 +6,69 @@ * @description Initialize the listbox example once the page has loaded */ +var Listbox = Listbox || {}; + window.addEventListener('load', function () { - var button = document.getElementById('exp_button'); - var exListbox = new aria.Listbox(document.getElementById('exp_elem_list')); - new aria.ListboxButton(button, exListbox); + const button = document.getElementById('exp_button'); + const exListbox = new Listbox(document.getElementById('exp_elem_list')); + new ListboxButton(button, exListbox); }); -var aria = aria || {}; - -aria.ListboxButton = function (button, listbox) { - this.button = button; - this.listbox = listbox; - this.registerEvents(); -}; - -aria.ListboxButton.prototype.registerEvents = function () { - this.button.addEventListener('click', this.showListbox.bind(this)); - this.button.addEventListener('keyup', this.checkShow.bind(this)); - this.listbox.listboxNode.addEventListener( - 'blur', - this.hideListbox.bind(this) - ); - this.listbox.listboxNode.addEventListener( - 'keydown', - this.checkHide.bind(this) - ); - this.listbox.setHandleFocusChange(this.onFocusChange.bind(this)); -}; - -aria.ListboxButton.prototype.checkShow = function (evt) { - var key = evt.which || evt.keyCode; +class ListboxButton { + constructor(button, listbox) { + this.button = button; + this.listbox = listbox; + this.registerEvents(); + } - switch (key) { - case aria.KeyCode.UP: - case aria.KeyCode.DOWN: - evt.preventDefault(); - this.showListbox(); - this.listbox.checkKeyPress(evt); - break; + registerEvents() { + this.button.addEventListener('click', this.showListbox.bind(this)); + this.button.addEventListener('keyup', this.checkShow.bind(this)); + this.listbox.listboxNode.addEventListener( + 'blur', + this.hideListbox.bind(this) + ); + this.listbox.listboxNode.addEventListener( + 'keydown', + this.checkHide.bind(this) + ); + this.listbox.setHandleFocusChange(this.onFocusChange.bind(this)); } -}; -aria.ListboxButton.prototype.checkHide = function (evt) { - var key = evt.which || evt.keyCode; + checkShow(evt) { + switch (evt.key) { + case 'ArrowUp': + case 'ArrowDown': + evt.preventDefault(); + this.showListbox(); + this.listbox.checkKeyPress(evt); + break; + } + } - switch (key) { - case aria.KeyCode.RETURN: - case aria.KeyCode.ESC: - evt.preventDefault(); - this.hideListbox(); - this.button.focus(); - break; + checkHide(evt) { + switch (evt.key) { + case 'Enter': + case 'Escape': + evt.preventDefault(); + this.hideListbox(); + this.button.focus(); + break; + } } -}; -aria.ListboxButton.prototype.showListbox = function () { - aria.Utils.removeClass(this.listbox.listboxNode, 'hidden'); - this.button.setAttribute('aria-expanded', 'true'); - this.listbox.listboxNode.focus(); -}; + showListbox() { + this.listbox.listboxNode.classList.remove('hidden'); + this.button.setAttribute('aria-expanded', 'true'); + this.listbox.listboxNode.focus(); + } -aria.ListboxButton.prototype.hideListbox = function () { - aria.Utils.addClass(this.listbox.listboxNode, 'hidden'); - this.button.removeAttribute('aria-expanded'); -}; + hideListbox() { + this.listbox.listboxNode.classList.add('hidden'); + this.button.removeAttribute('aria-expanded'); + } -aria.ListboxButton.prototype.onFocusChange = function (focusedItem) { - this.button.innerText = focusedItem.innerText; -}; + onFocusChange(focusedItem) { + this.button.innerText = focusedItem.innerText; + } +} diff --git a/content/patterns/listbox/examples/js/listbox-rearrangeable.js b/content/patterns/listbox/examples/js/listbox-rearrangeable.js index 561b5d519a..0a496b47c4 100644 --- a/content/patterns/listbox/examples/js/listbox-rearrangeable.js +++ b/content/patterns/listbox/examples/js/listbox-rearrangeable.js @@ -3,10 +3,11 @@ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ -/* global aria */ - 'use strict'; +var Listbox = Listbox || {}; +var Toolbar = Toolbar || {}; + /** * ARIA Listbox Examples * @@ -18,13 +19,13 @@ window.addEventListener('load', function () { // This onload handle initializes two examples. Only initialize example if the example // can be found in the dom. if (document.getElementById('ss_imp_list')) { - var ex1ImportantListbox = new aria.Listbox( + var ex1ImportantListbox = new Listbox( document.getElementById('ss_imp_list') ); - var ex1UnimportantListbox = new aria.Listbox( + var ex1UnimportantListbox = new Listbox( document.getElementById('ss_unimp_list') ); - new aria.Toolbar(document.querySelector('[role="toolbar"]')); + new Toolbar(document.querySelector('[role="toolbar"]')); ex1ImportantListbox.enableMoveUpDown( document.getElementById('ex1-up'), @@ -71,10 +72,10 @@ window.addEventListener('load', function () { // This onload handle initializes two examples. Only initialize example if the example // can be found in the dom. if (document.getElementById('ms_imp_list')) { - var ex2ImportantListbox = new aria.Listbox( + var ex2ImportantListbox = new Listbox( document.getElementById('ms_imp_list') ); - var ex2UnimportantListbox = new aria.Listbox( + var ex2UnimportantListbox = new Listbox( document.getElementById('ms_unimp_list') ); diff --git a/content/patterns/listbox/examples/js/listbox-scrollable.js b/content/patterns/listbox/examples/js/listbox-scrollable.js index 4020406040..84210439bd 100644 --- a/content/patterns/listbox/examples/js/listbox-scrollable.js +++ b/content/patterns/listbox/examples/js/listbox-scrollable.js @@ -3,10 +3,10 @@ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ -/* global aria */ - 'use strict'; +var Listbox = Listbox || {}; + /** * ARIA Scrollable Listbox Example * @@ -15,5 +15,5 @@ */ window.addEventListener('load', function () { - new aria.Listbox(document.getElementById('ss_elem_list')); + new Listbox(document.getElementById('ss_elem_list')); }); diff --git a/content/patterns/listbox/examples/js/listbox.js b/content/patterns/listbox/examples/js/listbox.js index 8a24176bb5..757fe52dfc 100644 --- a/content/patterns/listbox/examples/js/listbox.js +++ b/content/patterns/listbox/examples/js/listbox.js @@ -5,11 +5,6 @@ 'use strict'; -/** - * @namespace aria - */ -var aria = aria || {}; - /** * @class * @description @@ -17,681 +12,671 @@ var aria = aria || {}; * @param listboxNode * The DOM node pointing to the listbox */ -aria.Listbox = function (listboxNode) { - this.listboxNode = listboxNode; - this.activeDescendant = this.listboxNode.getAttribute( - 'aria-activedescendant' - ); - this.multiselectable = this.listboxNode.hasAttribute('aria-multiselectable'); - this.moveUpDownEnabled = false; - this.siblingList = null; - this.startRangeIndex = 0; - this.upButton = null; - this.downButton = null; - this.moveButton = null; - this.keysSoFar = ''; - this.handleFocusChange = function () {}; - this.handleItemChange = function () {}; - this.registerEvents(); -}; - -/** - * @description - * Register events for the listbox interactions - */ -aria.Listbox.prototype.registerEvents = function () { - this.listboxNode.addEventListener('focus', this.setupFocus.bind(this)); - this.listboxNode.addEventListener('keydown', this.checkKeyPress.bind(this)); - this.listboxNode.addEventListener('click', this.checkClickItem.bind(this)); - - if (this.multiselectable) { - this.listboxNode.addEventListener( - 'mousedown', - this.checkMouseDown.bind(this) +class Listbox { + constructor(listboxNode) { + if (!listboxNode) return; + this.listboxNode = listboxNode; + this.activeDescendant = this.listboxNode.getAttribute( + 'aria-activedescendant' ); + this.multiselectable = this.listboxNode.hasAttribute( + 'aria-multiselectable' + ); + this.moveUpDownEnabled = false; + this.siblingList = null; + this.startRangeIndex = 0; + this.upButton = null; + this.downButton = null; + this.moveButton = null; + this.keysSoFar = ''; + this.handleFocusChange = function () {}; + this.handleItemChange = function () {}; + this.registerEvents(); + } + + registerEvents() { + this.listboxNode.addEventListener('focus', this.setupFocus.bind(this)); + this.listboxNode.addEventListener('keydown', this.checkKeyPress.bind(this)); + this.listboxNode.addEventListener('click', this.checkClickItem.bind(this)); + + if (this.multiselectable) { + this.listboxNode.addEventListener( + 'mousedown', + this.checkMouseDown.bind(this) + ); + } } -}; -/** - * @description - * If there is no activeDescendant, focus on the first option - */ -aria.Listbox.prototype.setupFocus = function () { - if (this.activeDescendant) { - return; + setupFocus() { + if (this.activeDescendant) { + const listitem = document.getElementById(this.activeDescendant); + listitem.scrollIntoView({ block: 'nearest', inline: 'nearest' }); + } } -}; -/** - * @description - * Focus on the first option - */ -aria.Listbox.prototype.focusFirstItem = function () { - var firstItem = this.listboxNode.querySelector('[role="option"]'); + focusFirstItem() { + var firstItem = this.listboxNode.querySelector('[role="option"]'); - if (firstItem) { - this.focusItem(firstItem); + if (firstItem) { + this.focusItem(firstItem); + } } -}; -/** - * @description - * Focus on the last option - */ -aria.Listbox.prototype.focusLastItem = function () { - var itemList = this.listboxNode.querySelectorAll('[role="option"]'); + focusLastItem() { + const itemList = this.listboxNode.querySelectorAll('[role="option"]'); - if (itemList.length) { - this.focusItem(itemList[itemList.length - 1]); + if (itemList.length) { + this.focusItem(itemList[itemList.length - 1]); + } } -}; -/** - * @description - * Handle various keyboard controls; UP/DOWN will shift focus; SPACE selects - * an item. - * @param evt - * The keydown event object - */ -aria.Listbox.prototype.checkKeyPress = function (evt) { - var key = evt.which || evt.keyCode; - var lastActiveId = this.activeDescendant; - var allOptions = this.listboxNode.querySelectorAll('[role="option"]'); - var currentItem = - document.getElementById(this.activeDescendant) || allOptions[0]; - var nextItem = currentItem; - - if (!currentItem) { - return; - } - - switch (key) { - case aria.KeyCode.PAGE_UP: - case aria.KeyCode.PAGE_DOWN: - if (this.moveUpDownEnabled) { - evt.preventDefault(); + checkKeyPress(evt) { + const lastActiveId = this.activeDescendant; + const allOptions = this.listboxNode.querySelectorAll('[role="option"]'); + const currentItem = + document.getElementById(this.activeDescendant) || allOptions[0]; + let nextItem = currentItem; - if (key === aria.KeyCode.PAGE_UP) { - this.moveUpItems(); - } else { - this.moveDownItems(); + if (!currentItem) { + return; + } + + switch (evt.key) { + case 'PageUp': + case 'PageDown': + evt.preventDefault(); + if (this.moveUpDownEnabled) { + if (evt.key === 'PageUp') { + this.moveUpItems(); + } else { + this.moveDownItems(); + } } - } - break; - case aria.KeyCode.UP: - case aria.KeyCode.DOWN: - if (!this.activeDescendant) { - // focus first option if no option was previously focused, and perform no other actions - this.focusItem(currentItem); break; - } - - if (this.moveUpDownEnabled && evt.altKey) { + case 'ArrowUp': + case 'ArrowDown': evt.preventDefault(); - if (key === aria.KeyCode.UP) { - this.moveUpItems(); + if (!this.activeDescendant) { + // focus first option if no option was previously focused, and perform no other actions + this.focusItem(currentItem); + break; + } + + if (this.moveUpDownEnabled && evt.altKey) { + evt.preventDefault(); + if (evt.key === 'ArrowUp') { + this.moveUpItems(); + } else { + this.moveDownItems(); + } + return; + } + + if (evt.key === 'ArrowUp') { + nextItem = this.findPreviousOption(currentItem); } else { - this.moveDownItems(); + nextItem = this.findNextOption(currentItem); } - return; - } - if (key === aria.KeyCode.UP) { - nextItem = this.findPreviousOption(currentItem); - } else { - nextItem = this.findNextOption(currentItem); - } + if (nextItem && this.multiselectable && event.shiftKey) { + this.selectRange(this.startRangeIndex, nextItem); + } - if (nextItem && this.multiselectable && event.shiftKey) { - this.selectRange(this.startRangeIndex, nextItem); - } + if (nextItem) { + this.focusItem(nextItem); + } - if (nextItem) { - this.focusItem(nextItem); + break; + + case 'Home': evt.preventDefault(); - } + this.focusFirstItem(); - break; - case aria.KeyCode.HOME: - evt.preventDefault(); - this.focusFirstItem(); + if (this.multiselectable && evt.shiftKey && evt.ctrlKey) { + this.selectRange(this.startRangeIndex, 0); + } + break; - if (this.multiselectable && evt.shiftKey && evt.ctrlKey) { - this.selectRange(this.startRangeIndex, 0); - } - break; - case aria.KeyCode.END: - evt.preventDefault(); - this.focusLastItem(); + case 'End': + evt.preventDefault(); + this.focusLastItem(); - if (this.multiselectable && evt.shiftKey && evt.ctrlKey) { - this.selectRange(this.startRangeIndex, allOptions.length - 1); - } - break; - case aria.KeyCode.SHIFT: - this.startRangeIndex = this.getElementIndex(currentItem, allOptions); - break; - case aria.KeyCode.SPACE: - evt.preventDefault(); - this.toggleSelectItem(nextItem); - break; - case aria.KeyCode.BACKSPACE: - case aria.KeyCode.DELETE: - case aria.KeyCode.RETURN: - if (!this.moveButton) { - return; - } + if (this.multiselectable && evt.shiftKey && evt.ctrlKey) { + this.selectRange(this.startRangeIndex, allOptions.length - 1); + } + break; - var keyshortcuts = this.moveButton.getAttribute('aria-keyshortcuts'); - if (key === aria.KeyCode.RETURN && keyshortcuts.indexOf('Enter') === -1) { - return; - } - if ( - (key === aria.KeyCode.BACKSPACE || key === aria.KeyCode.DELETE) && - keyshortcuts.indexOf('Delete') === -1 - ) { - return; - } + case 'Shift': + this.startRangeIndex = this.getElementIndex(currentItem, allOptions); + break; - evt.preventDefault(); + case ' ': + evt.preventDefault(); + this.toggleSelectItem(nextItem); + break; - var nextUnselected = nextItem.nextElementSibling; - while (nextUnselected) { - if (nextUnselected.getAttribute('aria-selected') != 'true') { - break; + case 'Backspace': + case 'Delete': + case 'Enter': + if (!this.moveButton) { + return; } - nextUnselected = nextUnselected.nextElementSibling; - } - if (!nextUnselected) { - nextUnselected = nextItem.previousElementSibling; + + var keyshortcuts = this.moveButton.getAttribute('aria-keyshortcuts'); + if (evt.key === 'Enter' && keyshortcuts.indexOf('Enter') === -1) { + return; + } + if ( + (evt.key === 'Backspace' || evt.key === 'Delete') && + keyshortcuts.indexOf('Delete') === -1 + ) { + return; + } + + evt.preventDefault(); + + var nextUnselected = nextItem.nextElementSibling; while (nextUnselected) { if (nextUnselected.getAttribute('aria-selected') != 'true') { break; } - nextUnselected = nextUnselected.previousElementSibling; + nextUnselected = nextUnselected.nextElementSibling; + } + if (!nextUnselected) { + nextUnselected = nextItem.previousElementSibling; + while (nextUnselected) { + if (nextUnselected.getAttribute('aria-selected') != 'true') { + break; + } + nextUnselected = nextUnselected.previousElementSibling; + } } - } - this.moveItems(); + this.moveItems(); - if (!this.activeDescendant && nextUnselected) { - this.focusItem(nextUnselected); - } - break; - case 65: - // handle control + A - if (this.multiselectable && (evt.ctrlKey || evt.metaKey)) { - evt.preventDefault(); - this.selectRange(0, allOptions.length - 1); + if (!this.activeDescendant && nextUnselected) { + this.focusItem(nextUnselected); + } break; - } - // fall through - default: - var itemToFocus = this.findItemToFocus(key); - if (itemToFocus) { - this.focusItem(itemToFocus); - } - break; - } - - if (this.activeDescendant !== lastActiveId) { - this.updateScroll(); - } -}; -aria.Listbox.prototype.findItemToFocus = function (key) { - var itemList = this.listboxNode.querySelectorAll('[role="option"]'); - var character = String.fromCharCode(key); - var searchIndex = 0; + case 'A': + case 'a': + // handle control + A + if (evt.ctrlKey || evt.metaKey) { + if (this.multiselectable) { + this.selectRange(0, allOptions.length - 1); + } + evt.preventDefault(); + break; + } + // fall through + default: + if (evt.key.length === 1) { + const itemToFocus = this.findItemToFocus(evt.key.toLowerCase()); + if (itemToFocus) { + this.focusItem(itemToFocus); + } + } + break; + } - if (!this.keysSoFar) { - for (var i = 0; i < itemList.length; i++) { - if (itemList[i].getAttribute('id') == this.activeDescendant) { - searchIndex = i; - } + if (this.activeDescendant !== lastActiveId) { + this.updateScroll(); } } - this.keysSoFar += character; - this.clearKeysSoFarAfterDelay(); - var nextMatch = this.findMatchInRange( - itemList, - searchIndex + 1, - itemList.length - ); - if (!nextMatch) { - nextMatch = this.findMatchInRange(itemList, 0, searchIndex); - } - return nextMatch; -}; + findItemToFocus(character) { + const itemList = this.listboxNode.querySelectorAll('[role="option"]'); + let searchIndex = 0; -/* Return the index of the passed element within the passed array, or null if not found */ -aria.Listbox.prototype.getElementIndex = function (option, options) { - var allOptions = Array.prototype.slice.call(options); // convert to array - var optionIndex = allOptions.indexOf(option); + if (!this.keysSoFar) { + for (let i = 0; i < itemList.length; i++) { + if (itemList[i].getAttribute('id') == this.activeDescendant) { + searchIndex = i; + } + } + } - return typeof optionIndex === 'number' ? optionIndex : null; -}; + this.keysSoFar += character; + this.clearKeysSoFarAfterDelay(); -/* Return the next listbox option, if it exists; otherwise, returns null */ -aria.Listbox.prototype.findNextOption = function (currentOption) { - var allOptions = Array.prototype.slice.call( - this.listboxNode.querySelectorAll('[role="option"]') - ); // get options array - var currentOptionIndex = allOptions.indexOf(currentOption); - var nextOption = null; + let nextMatch = this.findMatchInRange( + itemList, + searchIndex + 1, + itemList.length + ); - if (currentOptionIndex > -1 && currentOptionIndex < allOptions.length - 1) { - nextOption = allOptions[currentOptionIndex + 1]; + if (!nextMatch) { + nextMatch = this.findMatchInRange(itemList, 0, searchIndex); + } + return nextMatch; } - return nextOption; -}; + /* Return the index of the passed element within the passed array, or null if not found */ + getElementIndex(option, options) { + const allOptions = Array.prototype.slice.call(options); // convert to array + const optionIndex = allOptions.indexOf(option); -/* Return the previous listbox option, if it exists; otherwise, returns null */ -aria.Listbox.prototype.findPreviousOption = function (currentOption) { - var allOptions = Array.prototype.slice.call( - this.listboxNode.querySelectorAll('[role="option"]') - ); // get options array - var currentOptionIndex = allOptions.indexOf(currentOption); - var previousOption = null; - - if (currentOptionIndex > -1 && currentOptionIndex > 0) { - previousOption = allOptions[currentOptionIndex - 1]; + return typeof optionIndex === 'number' ? optionIndex : null; } - return previousOption; -}; + /* Return the next listbox option, if it exists; otherwise, returns null */ + findNextOption(currentOption) { + const allOptions = Array.prototype.slice.call( + this.listboxNode.querySelectorAll('[role="option"]') + ); // get options array + const currentOptionIndex = allOptions.indexOf(currentOption); + let nextOption = null; -aria.Listbox.prototype.clearKeysSoFarAfterDelay = function () { - if (this.keyClear) { - clearTimeout(this.keyClear); - this.keyClear = null; - } - this.keyClear = setTimeout( - function () { - this.keysSoFar = ''; - this.keyClear = null; - }.bind(this), - 500 - ); -}; - -aria.Listbox.prototype.findMatchInRange = function ( - list, - startIndex, - endIndex -) { - // Find the first item starting with the keysSoFar substring, searching in - // the specified range of items - for (var n = startIndex; n < endIndex; n++) { - var label = list[n].innerText; - if (label && label.toUpperCase().indexOf(this.keysSoFar) === 0) { - return list[n]; - } - } - return null; -}; + if (currentOptionIndex > -1 && currentOptionIndex < allOptions.length - 1) { + nextOption = allOptions[currentOptionIndex + 1]; + } -/** - * @description - * Check if an item is clicked on. If so, focus on it and select it. - * @param evt - * The click event object - */ -aria.Listbox.prototype.checkClickItem = function (evt) { - if (evt.target.getAttribute('role') !== 'option') { - return; + return nextOption; } - this.focusItem(evt.target); - this.toggleSelectItem(evt.target); - this.updateScroll(); + /* Return the previous listbox option, if it exists; otherwise, returns null */ + findPreviousOption(currentOption) { + const allOptions = Array.prototype.slice.call( + this.listboxNode.querySelectorAll('[role="option"]') + ); // get options array + const currentOptionIndex = allOptions.indexOf(currentOption); + let previousOption = null; - if (this.multiselectable && evt.shiftKey) { - this.selectRange(this.startRangeIndex, evt.target); - } -}; + if (currentOptionIndex > -1 && currentOptionIndex > 0) { + previousOption = allOptions[currentOptionIndex - 1]; + } -/** - * Prevent text selection on shift + click for multi-select listboxes - * - * @param evt - */ -aria.Listbox.prototype.checkMouseDown = function (evt) { - if ( - this.multiselectable && - evt.shiftKey && - evt.target.getAttribute('role') === 'option' - ) { - evt.preventDefault(); + return previousOption; } -}; -/** - * @description - * Toggle the aria-selected value - * @param element - * The element to select - */ -aria.Listbox.prototype.toggleSelectItem = function (element) { - if (this.multiselectable) { - element.setAttribute( - 'aria-selected', - element.getAttribute('aria-selected') === 'true' ? 'false' : 'true' + clearKeysSoFarAfterDelay() { + if (this.keyClear) { + clearTimeout(this.keyClear); + this.keyClear = null; + } + this.keyClear = setTimeout( + function () { + this.keysSoFar = ''; + this.keyClear = null; + }.bind(this), + 500 ); - - this.updateMoveButton(); } -}; -/** - * @description - * Defocus the specified item - * @param element - * The element to defocus - */ -aria.Listbox.prototype.defocusItem = function (element) { - if (!element) { - return; - } - if (!this.multiselectable) { - element.removeAttribute('aria-selected'); - } - element.classList.remove('focused'); -}; - -/** - * @description - * Focus on the specified item - * @param element - * The element to focus - */ -aria.Listbox.prototype.focusItem = function (element) { - this.defocusItem(document.getElementById(this.activeDescendant)); - if (!this.multiselectable) { - element.setAttribute('aria-selected', 'true'); - } - element.classList.add('focused'); - this.listboxNode.setAttribute('aria-activedescendant', element.id); - this.activeDescendant = element.id; - - if (!this.multiselectable) { - this.updateMoveButton(); + findMatchInRange(list, startIndex, endIndex) { + // Find the first item starting with the keysSoFar substring, searching in + // the specified range of items + for (let n = startIndex; n < endIndex; n++) { + const label = list[n].innerText; + if (label && label.toLowerCase().indexOf(this.keysSoFar) === 0) { + return list[n]; + } + } + return null; } - this.checkUpDownButtons(); - this.handleFocusChange(element); -}; - -/** - * Helper function to check if a number is within a range; no side effects. - * - * @param index - * @param start - * @param end - * @returns {boolean} - */ -aria.Listbox.prototype.checkInRange = function (index, start, end) { - var rangeStart = start < end ? start : end; - var rangeEnd = start < end ? end : start; - - return index >= rangeStart && index <= rangeEnd; -}; + checkClickItem(evt) { + if (evt.target.getAttribute('role') !== 'option') { + return; + } -/** - * Select a range of options - * - * @param start - * @param end - */ -aria.Listbox.prototype.selectRange = function (start, end) { - // get start/end indices - var allOptions = this.listboxNode.querySelectorAll('[role="option"]'); - var startIndex = - typeof start === 'number' ? start : this.getElementIndex(start, allOptions); - var endIndex = - typeof end === 'number' ? end : this.getElementIndex(end, allOptions); + this.focusItem(evt.target); + this.toggleSelectItem(evt.target); + this.updateScroll(); - for (var index = 0; index < allOptions.length; index++) { - var selected = this.checkInRange(index, startIndex, endIndex); - allOptions[index].setAttribute('aria-selected', selected + ''); + if (this.multiselectable && evt.shiftKey) { + this.selectRange(this.startRangeIndex, evt.target); + } } - this.updateMoveButton(); -}; - -/** - * Check for selected options and update moveButton, if applicable - */ -aria.Listbox.prototype.updateMoveButton = function () { - if (!this.moveButton) { - return; + /** + * Prevent text selection on shift + click for multi-select listboxes + * + * @param evt + */ + checkMouseDown(evt) { + if ( + this.multiselectable && + evt.shiftKey && + evt.target.getAttribute('role') === 'option' + ) { + evt.preventDefault(); + } } - if (this.listboxNode.querySelector('[aria-selected="true"]')) { - this.moveButton.setAttribute('aria-disabled', 'false'); - } else { - this.moveButton.setAttribute('aria-disabled', 'true'); + /** + * @description + * Toggle the aria-selected value + * @param element + * The element to select + */ + toggleSelectItem(element) { + if (this.multiselectable) { + element.setAttribute( + 'aria-selected', + element.getAttribute('aria-selected') === 'true' ? 'false' : 'true' + ); + + this.updateMoveButton(); + } } -}; -/** - * Check if the selected option is in view, and scroll if not - */ -aria.Listbox.prototype.updateScroll = function () { - var selectedOption = document.getElementById(this.activeDescendant); - if ( - selectedOption && - this.listboxNode.scrollHeight > this.listboxNode.clientHeight - ) { - var scrollBottom = - this.listboxNode.clientHeight + this.listboxNode.scrollTop; - var elementBottom = selectedOption.offsetTop + selectedOption.offsetHeight; - if (elementBottom > scrollBottom) { - this.listboxNode.scrollTop = - elementBottom - this.listboxNode.clientHeight; - } else if (selectedOption.offsetTop < this.listboxNode.scrollTop) { - this.listboxNode.scrollTop = selectedOption.offsetTop; - } - } -}; + /** + * @description + * Defocus the specified item + * @param element + * The element to defocus + */ + defocusItem(element) { + if (!element) { + return; + } + if (!this.multiselectable) { + element.removeAttribute('aria-selected'); + } + element.classList.remove('focused'); + } + + /** + * @description + * Focus on the specified item + * @param element + * The element to focus + */ + focusItem(element) { + this.defocusItem(document.getElementById(this.activeDescendant)); + if (!this.multiselectable) { + element.setAttribute('aria-selected', 'true'); + } + element.classList.add('focused'); + this.listboxNode.setAttribute('aria-activedescendant', element.id); + this.activeDescendant = element.id; -/** - * @description - * Enable/disable the up/down arrows based on the activeDescendant. - */ -aria.Listbox.prototype.checkUpDownButtons = function () { - var activeElement = document.getElementById(this.activeDescendant); + if (!this.multiselectable) { + this.updateMoveButton(); + } - if (!this.moveUpDownEnabled) { - return; - } + this.checkUpDownButtons(); + this.handleFocusChange(element); + } + + /** + * Helper function to check if a number is within a range; no side effects. + * + * @param index + * @param start + * @param end + * @returns {boolean} + */ + checkInRange(index, start, end) { + const rangeStart = start < end ? start : end; + const rangeEnd = start < end ? end : start; + + return index >= rangeStart && index <= rangeEnd; + } + + /** + * Select a range of options + * + * @param start + * @param end + */ + selectRange(start, end) { + // get start/end indices + const allOptions = this.listboxNode.querySelectorAll('[role="option"]'); + const startIndex = + typeof start === 'number' + ? start + : this.getElementIndex(start, allOptions); + const endIndex = + typeof end === 'number' ? end : this.getElementIndex(end, allOptions); + + for (let index = 0; index < allOptions.length; index++) { + const selected = this.checkInRange(index, startIndex, endIndex); + allOptions[index].setAttribute('aria-selected', selected + ''); + } - if (!activeElement) { - this.upButton.setAttribute('aria-disabled', 'true'); - this.downButton.setAttribute('aria-disabled', 'true'); - return; + this.updateMoveButton(); } - if (this.upButton) { - if (activeElement.previousElementSibling) { - this.upButton.setAttribute('aria-disabled', false); - } else { - this.upButton.setAttribute('aria-disabled', 'true'); + /** + * Check for selected options and update moveButton, if applicable + */ + updateMoveButton() { + if (!this.moveButton) { + return; } - } - if (this.downButton) { - if (activeElement.nextElementSibling) { - this.downButton.setAttribute('aria-disabled', false); + if (this.listboxNode.querySelector('[aria-selected="true"]')) { + this.moveButton.setAttribute('aria-disabled', 'false'); } else { - this.downButton.setAttribute('aria-disabled', 'true'); + this.moveButton.setAttribute('aria-disabled', 'true'); } } -}; -/** - * @description - * Add the specified items to the listbox. Assumes items are valid options. - * @param items - * An array of items to add to the listbox - */ -aria.Listbox.prototype.addItems = function (items) { - if (!items || !items.length) { - return; + /** + * Check if the selected option is in view, and scroll if not + */ + updateScroll() { + const selectedOption = document.getElementById(this.activeDescendant); + if (selectedOption) { + const scrollBottom = + this.listboxNode.clientHeight + this.listboxNode.scrollTop; + const elementBottom = + selectedOption.offsetTop + selectedOption.offsetHeight; + if (elementBottom > scrollBottom) { + this.listboxNode.scrollTop = + elementBottom - this.listboxNode.clientHeight; + } else if (selectedOption.offsetTop < this.listboxNode.scrollTop) { + this.listboxNode.scrollTop = selectedOption.offsetTop; + } + selectedOption.scrollIntoView({ block: 'nearest', inline: 'nearest' }); + } } - items.forEach( - function (item) { - this.defocusItem(item); - this.toggleSelectItem(item); - this.listboxNode.append(item); - }.bind(this) - ); + /** + * @description + * Enable/disable the up/down arrows based on the activeDescendant. + */ + checkUpDownButtons() { + const activeElement = document.getElementById(this.activeDescendant); - if (!this.activeDescendant) { - this.focusItem(items[0]); - } + if (!this.moveUpDownEnabled) { + return; + } - this.handleItemChange('added', items); -}; + if (!activeElement) { + this.upButton.setAttribute('aria-disabled', 'true'); + this.downButton.setAttribute('aria-disabled', 'true'); + return; + } -/** - * @description - * Remove all of the selected items from the listbox; Removes the focused items - * in a single select listbox and the items with aria-selected in a multi - * select listbox. - * @returns {Array} - * An array of items that were removed from the listbox - */ -aria.Listbox.prototype.deleteItems = function () { - var itemsToDelete; + if (this.upButton) { + if (activeElement.previousElementSibling) { + this.upButton.setAttribute('aria-disabled', false); + } else { + this.upButton.setAttribute('aria-disabled', 'true'); + } + } - if (this.multiselectable) { - itemsToDelete = this.listboxNode.querySelectorAll('[aria-selected="true"]'); - } else if (this.activeDescendant) { - itemsToDelete = [document.getElementById(this.activeDescendant)]; + if (this.downButton) { + if (activeElement.nextElementSibling) { + this.downButton.setAttribute('aria-disabled', false); + } else { + this.downButton.setAttribute('aria-disabled', 'true'); + } + } } - if (!itemsToDelete || !itemsToDelete.length) { - return []; - } + /** + * @description + * Add the specified items to the listbox. Assumes items are valid options. + * @param items + * An array of items to add to the listbox + */ + addItems(items) { + if (!items || !items.length) { + return; + } - itemsToDelete.forEach( - function (item) { - item.remove(); + items.forEach( + function (item) { + this.defocusItem(item); + this.toggleSelectItem(item); + this.listboxNode.append(item); + }.bind(this) + ); - if (item.id === this.activeDescendant) { - this.clearActiveDescendant(); - } - }.bind(this) - ); + if (!this.activeDescendant) { + this.focusItem(items[0]); + } - this.handleItemChange('removed', itemsToDelete); + this.handleItemChange('added', items); + } + + /** + * @description + * Remove all of the selected items from the listbox; Removes the focused items + * in a single select listbox and the items with aria-selected in a multi + * select listbox. + * @returns {Array} + * An array of items that were removed from the listbox + */ + deleteItems() { + let itemsToDelete; + + if (this.multiselectable) { + itemsToDelete = this.listboxNode.querySelectorAll( + '[aria-selected="true"]' + ); + } else if (this.activeDescendant) { + itemsToDelete = [document.getElementById(this.activeDescendant)]; + } + + if (!itemsToDelete || !itemsToDelete.length) { + return []; + } - return itemsToDelete; -}; + itemsToDelete.forEach( + function (item) { + item.remove(); -aria.Listbox.prototype.clearActiveDescendant = function () { - this.activeDescendant = null; - this.listboxNode.setAttribute('aria-activedescendant', null); + if (item.id === this.activeDescendant) { + this.clearActiveDescendant(); + } + }.bind(this) + ); - this.updateMoveButton(); - this.checkUpDownButtons(); -}; + this.handleItemChange('removed', itemsToDelete); -/** - * @description - * Shifts the currently focused item up on the list. No shifting occurs if the - * item is already at the top of the list. - */ -aria.Listbox.prototype.moveUpItems = function () { - if (!this.activeDescendant) { - return; + return itemsToDelete; } - var currentItem = document.getElementById(this.activeDescendant); - var previousItem = currentItem.previousElementSibling; + clearActiveDescendant() { + this.activeDescendant = null; + this.listboxNode.setAttribute('aria-activedescendant', null); - if (previousItem) { - this.listboxNode.insertBefore(currentItem, previousItem); - this.handleItemChange('moved_up', [currentItem]); + this.updateMoveButton(); + this.checkUpDownButtons(); } - this.checkUpDownButtons(); -}; + /** + * @description + * Shifts the currently focused item up on the list. No shifting occurs if the + * item is already at the top of the list. + */ + moveUpItems() { + if (!this.activeDescendant) { + return; + } -/** - * @description - * Shifts the currently focused item down on the list. No shifting occurs if - * the item is already at the end of the list. - */ -aria.Listbox.prototype.moveDownItems = function () { - if (!this.activeDescendant) { - return; - } + const currentItem = document.getElementById(this.activeDescendant); + const previousItem = currentItem.previousElementSibling; - var currentItem = document.getElementById(this.activeDescendant); - var nextItem = currentItem.nextElementSibling; + if (previousItem) { + this.listboxNode.insertBefore(currentItem, previousItem); + this.handleItemChange('moved_up', [currentItem]); + } - if (nextItem) { - this.listboxNode.insertBefore(nextItem, currentItem); - this.handleItemChange('moved_down', [currentItem]); + this.checkUpDownButtons(); } - this.checkUpDownButtons(); -}; + /** + * @description + * Shifts the currently focused item down on the list. No shifting occurs if + * the item is already at the end of the list. + */ + moveDownItems() { + if (!this.activeDescendant) { + return; + } -/** - * @description - * Delete the currently selected items and add them to the sibling list. - */ -aria.Listbox.prototype.moveItems = function () { - if (!this.siblingList) { - return; - } + var currentItem = document.getElementById(this.activeDescendant); + var nextItem = currentItem.nextElementSibling; - var itemsToMove = this.deleteItems(); - this.siblingList.addItems(itemsToMove); -}; + if (nextItem) { + this.listboxNode.insertBefore(nextItem, currentItem); + this.handleItemChange('moved_down', [currentItem]); + } -/** - * @description - * Enable Up/Down controls to shift items up and down. - * @param upButton - * Up button to trigger up shift - * @param downButton - * Down button to trigger down shift - */ -aria.Listbox.prototype.enableMoveUpDown = function (upButton, downButton) { - this.moveUpDownEnabled = true; - this.upButton = upButton; - this.downButton = downButton; - upButton.addEventListener('click', this.moveUpItems.bind(this)); - downButton.addEventListener('click', this.moveDownItems.bind(this)); -}; + this.checkUpDownButtons(); + } -/** - * @description - * Enable Move controls. Moving removes selected items from the current - * list and adds them to the sibling list. - * @param button - * Move button to trigger delete - * @param siblingList - * Listbox to move items to - */ -aria.Listbox.prototype.setupMove = function (button, siblingList) { - this.siblingList = siblingList; - this.moveButton = button; - button.addEventListener('click', this.moveItems.bind(this)); -}; - -aria.Listbox.prototype.setHandleItemChange = function (handlerFn) { - this.handleItemChange = handlerFn; -}; - -aria.Listbox.prototype.setHandleFocusChange = function (focusChangeHandler) { - this.handleFocusChange = focusChangeHandler; -}; + /** + * @description + * Delete the currently selected items and add them to the sibling list. + */ + moveItems() { + if (!this.siblingList) { + return; + } + + var itemsToMove = this.deleteItems(); + this.siblingList.addItems(itemsToMove); + } + + /** + * @description + * Enable Up/Down controls to shift items up and down. + * @param upButton + * Up button to trigger up shift + * @param downButton + * Down button to trigger down shift + */ + enableMoveUpDown(upButton, downButton) { + this.moveUpDownEnabled = true; + this.upButton = upButton; + this.downButton = downButton; + upButton.addEventListener('click', this.moveUpItems.bind(this)); + downButton.addEventListener('click', this.moveDownItems.bind(this)); + } + + /** + * @description + * Enable Move controls. Moving removes selected items from the current + * list and adds them to the sibling list. + * @param button + * Move button to trigger delete + * @param siblingList + * Listbox to move items to + */ + setupMove(button, siblingList) { + this.siblingList = siblingList; + this.moveButton = button; + button.addEventListener('click', this.moveItems.bind(this)); + } + + setHandleItemChange(handlerFn) { + this.handleItemChange = handlerFn; + } + + setHandleFocusChange(focusChangeHandler) { + this.handleFocusChange = focusChangeHandler; + } +} + +(function () { + new Listbox(); +})(); diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index 6def976c0e..60bf83fcea 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -5,11 +5,6 @@ 'use strict'; -/** - * @namespace aria - */ -var aria = aria || {}; - /** * @class * @description @@ -17,101 +12,108 @@ var aria = aria || {}; * @param toolbarNode * The DOM node pointing to the toolbar */ -aria.Toolbar = function (toolbarNode) { - this.toolbarNode = toolbarNode; - this.items = this.toolbarNode.querySelectorAll('.toolbar-item'); - this.selectedItem = this.toolbarNode.querySelector('.selected'); - this.registerEvents(); -}; +class Toolbar { + constructor(toolbarNode) { + if (!toolbarNode) return; + this.toolbarNode = toolbarNode; + this.items = this.toolbarNode.querySelectorAll('.toolbar-item'); + this.selectedItem = this.toolbarNode.querySelector('.selected'); + this.registerEvents(); + } -/** - * @description - * Register events for the toolbar interactions - */ -aria.Toolbar.prototype.registerEvents = function () { - this.toolbarNode.addEventListener( - 'keydown', - this.checkFocusChange.bind(this) - ); - this.toolbarNode.addEventListener('click', this.checkClickItem.bind(this)); -}; + /** + * @description + * Register events for the toolbar interactions + */ + registerEvents() { + this.toolbarNode.addEventListener( + 'keydown', + this.checkFocusChange.bind(this) + ); + this.toolbarNode.addEventListener('click', this.checkClickItem.bind(this)); + } -/** - * @description - * Handle various keyboard controls; LEFT/RIGHT will shift focus; DOWN - * activates a menu button if it is the focused item. - * @param evt - * The keydown event object - */ -aria.Toolbar.prototype.checkFocusChange = function (evt) { - var key = evt.which || evt.keyCode; - var nextIndex, nextItem; + /** + * @description + * Handle various keyboard controls; LEFT/RIGHT will shift focus; DOWN + * activates a menu button if it is the focused item. + * @param evt + * The keydown event object + */ + checkFocusChange(evt) { + let nextIndex, nextItem; - switch (key) { - case aria.KeyCode.LEFT: - case aria.KeyCode.RIGHT: - nextIndex = Array.prototype.indexOf.call(this.items, this.selectedItem); - nextIndex = key === aria.KeyCode.LEFT ? nextIndex - 1 : nextIndex + 1; - nextIndex = Math.max(Math.min(nextIndex, this.items.length - 1), 0); + switch (evt.key) { + case 'ArrowLeft': + case 'ArrowRight': + nextIndex = Array.prototype.indexOf.call(this.items, this.selectedItem); + nextIndex = evt.key === 'ArrowLeft' ? nextIndex - 1 : nextIndex + 1; + nextIndex = Math.max(Math.min(nextIndex, this.items.length - 1), 0); - nextItem = this.items[nextIndex]; - this.selectItem(nextItem); - this.focusItem(nextItem); - break; - case aria.KeyCode.DOWN: - // if selected item is menu button, pressing DOWN should act like a click - if (aria.Utils.hasClass(this.selectedItem, 'menu-button')) { - evt.preventDefault(); - this.selectedItem.click(); - } - break; + nextItem = this.items[nextIndex]; + this.selectItem(nextItem); + this.focusItem(nextItem); + break; + + case 'Down': + // if selected item is menu button, pressing DOWN should act like a click + if (this.selectedItem.classList.contains('menu-button')) { + evt.preventDefault(); + this.selectedItem.click(); + } + break; + } } -}; -/** - * @description - * Selects a toolbar item if it is clicked - * @param evt - * The click event object - */ -aria.Toolbar.prototype.checkClickItem = function (evt) { - if (aria.Utils.hasClass(evt.target, 'toolbar-item')) { - this.selectItem(evt.target); + /** + * @description + * Selects a toolbar item if it is clicked + * @param evt + * The click event object + */ + checkClickItem(evt) { + if (evt.target.classList.contains('toolbar-item')) { + this.selectItem(evt.target); + } } -}; -/** - * @description - * Deselect the specified item - * @param element - * The item to deselect - */ -aria.Toolbar.prototype.deselectItem = function (element) { - aria.Utils.removeClass(element, 'selected'); - element.setAttribute('aria-selected', 'false'); - element.setAttribute('tabindex', '-1'); -}; + /** + * @description + * Deselect the specified item + * @param element + * The item to deselect + */ + deselectItem(element) { + element.classList.remove('selected'); + element.setAttribute('aria-selected', 'false'); + element.setAttribute('tabindex', '-1'); + } -/** - * @description - * Deselect the currently selected item and select the specified item - * @param element - * The item to select - */ -aria.Toolbar.prototype.selectItem = function (element) { - this.deselectItem(this.selectedItem); - aria.Utils.addClass(element, 'selected'); - element.setAttribute('aria-selected', 'true'); - element.setAttribute('tabindex', '0'); - this.selectedItem = element; -}; + /** + * @description + * Deselect the currently selected item and select the specified item + * @param element + * The item to select + */ + selectItem(element) { + this.deselectItem(this.selectedItem); + element.classList.add('selected'); + element.setAttribute('aria-selected', 'true'); + element.setAttribute('tabindex', '0'); + this.selectedItem = element; + } -/** - * @description - * Focus on the specified item - * @param element - * The item to focus on - */ -aria.Toolbar.prototype.focusItem = function (element) { - element.focus(); -}; + /** + * @description + * Focus on the specified item + * @param element + * The item to focus on + */ + focusItem(element) { + element.focus(); + } +} + +(function () { + new Toolbar(); +})(); diff --git a/content/patterns/listbox/examples/listbox-collapsible.html b/content/patterns/listbox/examples/listbox-collapsible.html index 15973a2245..b0d0c8cd74 100644 --- a/content/patterns/listbox/examples/listbox-collapsible.html +++ b/content/patterns/listbox/examples/listbox-collapsible.html @@ -15,7 +15,6 @@ - @@ -118,6 +117,33 @@

Notes

+
+

Accessibility Features

+
    +
  1. + The listbox receives accessibility focus via aria-activedescendant. + This enables users to perceive the presence of the options, and enables assistive technology users to comprehend the size of the list of options.
  2. +
  3. + Navigating the list of options does not set the selection of an option. + This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. + The value is set when users press Space or Enter. + Selected options have a check preceeding the text label for the option. +
  4. +
  5. + Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. + When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. + Managing aria-activedescendant visibility is essential to accessibility for people who use a browser's zoom feature to increase the size of content. +
  6. +
  7. + To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes: +
      +
    • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the combobox or list.
    • +
    • To make it easier to distinguish the selected listbox option from other options, selection creates a 2 pixel border above and below the option.
    • +
    +
  8. +
+
+

Keyboard Support

diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index 43eddec948..086785daf0 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -15,7 +15,6 @@ - @@ -101,6 +100,34 @@

Notes

+ +
+

Accessibility Features

+
    +
  1. + The listbox receives accessibility focus via aria-activedescendant. + This enables users to perceive the presence of the options, and enables assistive technology users to comprehend the size of the list of options.
  2. +
  3. + Navigating the list of options does not set the selection of an option. + This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. + The value is set when users press Space or Enter. + Selected options have a check preceeding the text label for the option. +
  4. +
  5. + Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. + When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. + Managing aria-activedescendant visibility is essential to accessibility for people who use a browser's zoom feature to increase the size of content. +
  6. +
  7. + To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes: +
      +
    • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the combobox or list.
    • +
    • To make it easier to distinguish the selected listbox option from other options, selection creates a 2 pixel border above and below the option.
    • +
    +
  8. +
+
+

Keyboard Support

diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index b8b980b78c..1f068bc005 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -15,7 +15,6 @@ - @@ -205,6 +204,27 @@

Accessibility Features

  • In example 1, since there are four action buttons, a toolbar widget is used to group all the action buttons into a single tab stop.
  • Live regions provide confirmation of completed actions.
  • +
  • + The listbox receives accessibility focus via aria-activedescendant. + This enables users to perceive the presence of the options, and enables assistive technology users to comprehend the size of the list of options.
  • +
  • + Navigating the list of options does not set the selection of an option. + This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. + The value is set when users press Space or Enter. + Selected options have a check preceeding the text label for the option. +
  • +
  • + Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. + When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. + Managing aria-activedescendant visibility is essential to accessibility for people who use a browser's zoom feature to increase the size of content. +
  • +
  • + To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes: +
      +
    • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the combobox or list.
    • +
    • To make it easier to distinguish the selected listbox option from other options, selection creates a 2 pixel border above and below the option.
    • +
    +
  • diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 67fae5b3c2..4efe1e86e0 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -15,7 +15,6 @@ - @@ -108,6 +107,33 @@

    Notes

    +
    +

    Accessibility Features

    +
      +
    1. + The listbox receives accessibility focus via aria-activedescendant. + This enables users to perceive the presence of the options, and enables assistive technology users to comprehend the size of the list of options.
    2. +
    3. + Navigating the list of options does not set the selection of an option. + This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. + The value is set when users press Space or Enter. + Selected options have a check preceeding the text label for the option. +
    4. +
    5. + Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. + When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. + Managing aria-activedescendant visibility is essential to accessibility for people who use a browser's zoom feature to increase the size of content. +
    6. +
    7. + To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes: +
        +
      • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the combobox or list.
      • +
      • To make it easier to distinguish the selected listbox option from other options, selection creates a 2 pixel border above and below the option.
      • +
      +
    8. +
    +
    +

    Keyboard Support

    From f0b72f49e742940f8036b1941894bbfb03544738 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Thu, 16 Feb 2023 20:51:07 -0600 Subject: [PATCH 02/52] fixed spelling errors --- content/patterns/listbox/examples/listbox-collapsible.html | 2 +- content/patterns/listbox/examples/listbox-grouped.html | 2 +- content/patterns/listbox/examples/listbox-rearrangeable.html | 2 +- content/patterns/listbox/examples/listbox-scrollable.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-collapsible.html b/content/patterns/listbox/examples/listbox-collapsible.html index b0d0c8cd74..420beb4917 100644 --- a/content/patterns/listbox/examples/listbox-collapsible.html +++ b/content/patterns/listbox/examples/listbox-collapsible.html @@ -127,7 +127,7 @@

    Accessibility Features

    Navigating the list of options does not set the selection of an option. This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. The value is set when users press Space or Enter. - Selected options have a check preceeding the text label for the option. + Selected options have a check preceding the text label for the option.
  • Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index 086785daf0..d024254218 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -111,7 +111,7 @@

    Accessibility Features

    Navigating the list of options does not set the selection of an option. This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. The value is set when users press Space or Enter. - Selected options have a check preceeding the text label for the option. + Selected options have a check preceding the text label for the option.
  • Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 1f068bc005..91585422a0 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -211,7 +211,7 @@

    Accessibility Features

    Navigating the list of options does not set the selection of an option. This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. The value is set when users press Space or Enter. - Selected options have a check preceeding the text label for the option. + Selected options have a check preceding the text label for the option.
  • Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 4efe1e86e0..05af27e53f 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -117,7 +117,7 @@

    Accessibility Features

    Navigating the list of options does not set the selection of an option. This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. The value is set when users press Space or Enter. - Selected options have a check preceeding the text label for the option. + Selected options have a check preceding the text label for the option.
  • Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. From 0c60ea74188b7270caaa5ad183fc3b25083ccd16 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Fri, 17 Feb 2023 10:38:37 -0600 Subject: [PATCH 03/52] udpate --- content/patterns/listbox/examples/js/listbox-scrollable.js | 2 -- content/patterns/listbox/examples/js/listbox.js | 5 ----- content/patterns/listbox/examples/js/toolbar.js | 4 ---- 3 files changed, 11 deletions(-) diff --git a/content/patterns/listbox/examples/js/listbox-scrollable.js b/content/patterns/listbox/examples/js/listbox-scrollable.js index 84210439bd..68194d8ea0 100644 --- a/content/patterns/listbox/examples/js/listbox-scrollable.js +++ b/content/patterns/listbox/examples/js/listbox-scrollable.js @@ -5,8 +5,6 @@ 'use strict'; -var Listbox = Listbox || {}; - /** * ARIA Scrollable Listbox Example * diff --git a/content/patterns/listbox/examples/js/listbox.js b/content/patterns/listbox/examples/js/listbox.js index 757fe52dfc..f1f3a69520 100644 --- a/content/patterns/listbox/examples/js/listbox.js +++ b/content/patterns/listbox/examples/js/listbox.js @@ -14,7 +14,6 @@ */ class Listbox { constructor(listboxNode) { - if (!listboxNode) return; this.listboxNode = listboxNode; this.activeDescendant = this.listboxNode.getAttribute( 'aria-activedescendant' @@ -676,7 +675,3 @@ class Listbox { this.handleFocusChange = focusChangeHandler; } } - -(function () { - new Listbox(); -})(); diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index 60bf83fcea..eea96c2778 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -14,7 +14,6 @@ */ class Toolbar { constructor(toolbarNode) { - if (!toolbarNode) return; this.toolbarNode = toolbarNode; this.items = this.toolbarNode.querySelectorAll('.toolbar-item'); this.selectedItem = this.toolbarNode.querySelector('.selected'); @@ -114,6 +113,3 @@ class Toolbar { } } -(function () { - new Toolbar(); -})(); From ee6be160d8d767eeb1e9d9c3233e9d01b5d66ee3 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 21 Feb 2023 15:20:03 -0600 Subject: [PATCH 04/52] updated documentation of keyup and keydown commands --- .../combobox/examples/combobox-datepicker.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content/patterns/combobox/examples/combobox-datepicker.html b/content/patterns/combobox/examples/combobox-datepicker.html index 7a6f47d3d5..6bbc23becb 100644 --- a/content/patterns/combobox/examples/combobox-datepicker.html +++ b/content/patterns/combobox/examples/combobox-datepicker.html @@ -353,7 +353,7 @@

    Date Picker Dialog: Date Grid

  • Changes the grid of dates to the previous month.
  • Moves focus to the same day of the same week. - If that day does not exist, moves focus to the same day of the previous or next week. + If that day does not exist, moves focus to the last day of the month.
  • @@ -367,8 +367,8 @@

    Date Picker Dialog: Date Grid

    @@ -380,7 +380,7 @@

    Date Picker Dialog: Date Grid

  • Changes the grid of dates to the next month.
  • Moves focus to the same day of the same week. - If that day does not exist, moves focus to the same day of previous or next week. + If that day does not exist, moves focus to the last day of the month.
  • @@ -395,7 +395,7 @@

    Date Picker Dialog: Date Grid

  • Changes the grid of dates to the next year.
  • Moves focus to the same day of the same week in the next year. - If that day does not exist, moves focus to the same day of previous or next week. + If that day does not exist, moves focus to the last day of the month.
  • From 5b2831d413adfb1689a119aca8e090d9a98f5649 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 28 Feb 2023 19:14:36 -0600 Subject: [PATCH 05/52] fixed bug in rearrangable listbox --- content/patterns/listbox/examples/js/listbox-rearrangeable.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/content/patterns/listbox/examples/js/listbox-rearrangeable.js b/content/patterns/listbox/examples/js/listbox-rearrangeable.js index 0a496b47c4..9784b70242 100644 --- a/content/patterns/listbox/examples/js/listbox-rearrangeable.js +++ b/content/patterns/listbox/examples/js/listbox-rearrangeable.js @@ -5,9 +5,6 @@ 'use strict'; -var Listbox = Listbox || {}; -var Toolbar = Toolbar || {}; - /** * ARIA Listbox Examples * From fb4bfa8635ad0ae307e474cf3b7090dc6b0a4457 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 28 Feb 2023 19:26:30 -0600 Subject: [PATCH 06/52] restored fiel that should not have been changed --- .../combobox/examples/combobox-datepicker.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content/patterns/combobox/examples/combobox-datepicker.html b/content/patterns/combobox/examples/combobox-datepicker.html index 6bbc23becb..7a6f47d3d5 100644 --- a/content/patterns/combobox/examples/combobox-datepicker.html +++ b/content/patterns/combobox/examples/combobox-datepicker.html @@ -353,7 +353,7 @@

    Date Picker Dialog: Date Grid

  • Changes the grid of dates to the previous month.
  • Moves focus to the same day of the same week. - If that day does not exist, moves focus to the last day of the month. + If that day does not exist, moves focus to the same day of the previous or next week.
  • @@ -367,8 +367,8 @@

    Date Picker Dialog: Date Grid

    • Changes the grid of dates to the previous year.
    • - Moves focus to the same day of the same week. - If that day does not exist, moves focus to the last day of the month. + Moves focus to the same day of the same week in the previous year. + If that day does not exist, moves focus to the same day of the previous or next week.
    @@ -380,7 +380,7 @@

    Date Picker Dialog: Date Grid

  • Changes the grid of dates to the next month.
  • Moves focus to the same day of the same week. - If that day does not exist, moves focus to the last day of the month. + If that day does not exist, moves focus to the same day of previous or next week.
  • @@ -395,7 +395,7 @@

    Date Picker Dialog: Date Grid

  • Changes the grid of dates to the next year.
  • Moves focus to the same day of the same week in the next year. - If that day does not exist, moves focus to the last day of the month. + If that day does not exist, moves focus to the same day of previous or next week.
  • From 45dcfb6ca8d0ffa0bac6af6e24b4b91f4c17c1c1 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Thu, 2 Mar 2023 11:09:50 -0600 Subject: [PATCH 07/52] restored file that should not have been changed --- .eslintrc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index 138df8550a..02e73bdf48 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -61,7 +61,7 @@ }, "rules": { "strict": 0, - "no-unused-vars": ["error", { "varsIgnorePattern": "SkipTo" }] + "no-unused-vars": ["error", { "varsIgnorePattern": "SkipToConfig" }] } }, { From ba970a31633296d0338b198be7475635e262519c Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Thu, 2 Mar 2023 15:32:03 -0600 Subject: [PATCH 08/52] updated code to remove uneeded code --- .../patterns/listbox/examples/js/listbox-collapsible.js | 8 ++++++-- content/patterns/listbox/examples/js/listbox.js | 1 + content/patterns/listbox/examples/js/toolbar.js | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/content/patterns/listbox/examples/js/listbox-collapsible.js b/content/patterns/listbox/examples/js/listbox-collapsible.js index f908d1c55a..da95ab7e2f 100644 --- a/content/patterns/listbox/examples/js/listbox-collapsible.js +++ b/content/patterns/listbox/examples/js/listbox-collapsible.js @@ -1,4 +1,10 @@ +/* + * This content is licensed according to the W3C Software License at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document + */ + 'use strict'; + /** * ARIA Collapsible Dropdown Listbox Example * @@ -6,8 +12,6 @@ * @description Initialize the listbox example once the page has loaded */ -var Listbox = Listbox || {}; - window.addEventListener('load', function () { const button = document.getElementById('exp_button'); const exListbox = new Listbox(document.getElementById('exp_elem_list')); diff --git a/content/patterns/listbox/examples/js/listbox.js b/content/patterns/listbox/examples/js/listbox.js index f1f3a69520..314e12bc99 100644 --- a/content/patterns/listbox/examples/js/listbox.js +++ b/content/patterns/listbox/examples/js/listbox.js @@ -12,6 +12,7 @@ * @param listboxNode * The DOM node pointing to the listbox */ + class Listbox { constructor(listboxNode) { this.listboxNode = listboxNode; diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index eea96c2778..8e4f0d4f4f 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -12,6 +12,7 @@ * @param toolbarNode * The DOM node pointing to the toolbar */ + class Toolbar { constructor(toolbarNode) { this.toolbarNode = toolbarNode; From de0a3d0818f481843b12f21f40a487f36f96ab18 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 7 Mar 2023 13:58:58 -0600 Subject: [PATCH 09/52] added global listbox to eslint config --- .eslintrc.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.json b/.eslintrc.json index 02e73bdf48..46e1ffdcb7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -57,6 +57,7 @@ "files": ["**/*.html"], "plugins": ["html"], "globals": { + "Listbox": true, "sourceCode": true }, "rules": { From ec4dd4eede1736cff20c2b979e6703ecd2646188 Mon Sep 17 00:00:00 2001 From: Mike Pennisi Date: Tue, 7 Mar 2023 18:18:42 -0500 Subject: [PATCH 10/52] Satisfy linter --- .eslintrc.json | 1 - .../listbox/examples/js/listbox-collapsible.js | 4 +++- .../listbox/examples/js/listbox-rearrangeable.js | 12 +++++++----- .../listbox/examples/js/listbox-scrollable.js | 4 +++- content/patterns/listbox/examples/js/listbox.js | 9 +++++++-- content/patterns/listbox/examples/js/toolbar.js | 10 +++++++--- 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 46e1ffdcb7..02e73bdf48 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -57,7 +57,6 @@ "files": ["**/*.html"], "plugins": ["html"], "globals": { - "Listbox": true, "sourceCode": true }, "rules": { diff --git a/content/patterns/listbox/examples/js/listbox-collapsible.js b/content/patterns/listbox/examples/js/listbox-collapsible.js index da95ab7e2f..96475b10d8 100644 --- a/content/patterns/listbox/examples/js/listbox-collapsible.js +++ b/content/patterns/listbox/examples/js/listbox-collapsible.js @@ -3,6 +3,8 @@ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ +/* global aria */ + 'use strict'; /** @@ -14,7 +16,7 @@ window.addEventListener('load', function () { const button = document.getElementById('exp_button'); - const exListbox = new Listbox(document.getElementById('exp_elem_list')); + const exListbox = new aria.Listbox(document.getElementById('exp_elem_list')); new ListboxButton(button, exListbox); }); diff --git a/content/patterns/listbox/examples/js/listbox-rearrangeable.js b/content/patterns/listbox/examples/js/listbox-rearrangeable.js index 9784b70242..561b5d519a 100644 --- a/content/patterns/listbox/examples/js/listbox-rearrangeable.js +++ b/content/patterns/listbox/examples/js/listbox-rearrangeable.js @@ -3,6 +3,8 @@ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ +/* global aria */ + 'use strict'; /** @@ -16,13 +18,13 @@ window.addEventListener('load', function () { // This onload handle initializes two examples. Only initialize example if the example // can be found in the dom. if (document.getElementById('ss_imp_list')) { - var ex1ImportantListbox = new Listbox( + var ex1ImportantListbox = new aria.Listbox( document.getElementById('ss_imp_list') ); - var ex1UnimportantListbox = new Listbox( + var ex1UnimportantListbox = new aria.Listbox( document.getElementById('ss_unimp_list') ); - new Toolbar(document.querySelector('[role="toolbar"]')); + new aria.Toolbar(document.querySelector('[role="toolbar"]')); ex1ImportantListbox.enableMoveUpDown( document.getElementById('ex1-up'), @@ -69,10 +71,10 @@ window.addEventListener('load', function () { // This onload handle initializes two examples. Only initialize example if the example // can be found in the dom. if (document.getElementById('ms_imp_list')) { - var ex2ImportantListbox = new Listbox( + var ex2ImportantListbox = new aria.Listbox( document.getElementById('ms_imp_list') ); - var ex2UnimportantListbox = new Listbox( + var ex2UnimportantListbox = new aria.Listbox( document.getElementById('ms_unimp_list') ); diff --git a/content/patterns/listbox/examples/js/listbox-scrollable.js b/content/patterns/listbox/examples/js/listbox-scrollable.js index 68194d8ea0..4020406040 100644 --- a/content/patterns/listbox/examples/js/listbox-scrollable.js +++ b/content/patterns/listbox/examples/js/listbox-scrollable.js @@ -3,6 +3,8 @@ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ +/* global aria */ + 'use strict'; /** @@ -13,5 +15,5 @@ */ window.addEventListener('load', function () { - new Listbox(document.getElementById('ss_elem_list')); + new aria.Listbox(document.getElementById('ss_elem_list')); }); diff --git a/content/patterns/listbox/examples/js/listbox.js b/content/patterns/listbox/examples/js/listbox.js index 314e12bc99..96ba215f41 100644 --- a/content/patterns/listbox/examples/js/listbox.js +++ b/content/patterns/listbox/examples/js/listbox.js @@ -5,6 +5,11 @@ 'use strict'; +/** + * @namespace aria + */ +var aria = aria || {}; + /** * @class * @description @@ -13,7 +18,7 @@ * The DOM node pointing to the listbox */ -class Listbox { +aria.Listbox = class Listbox { constructor(listboxNode) { this.listboxNode = listboxNode; this.activeDescendant = this.listboxNode.getAttribute( @@ -675,4 +680,4 @@ class Listbox { setHandleFocusChange(focusChangeHandler) { this.handleFocusChange = focusChangeHandler; } -} +}; diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index 8e4f0d4f4f..9f537abac3 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -5,6 +5,11 @@ 'use strict'; +/** + * @namespace aria + */ +var aria = aria || {}; + /** * @class * @description @@ -13,7 +18,7 @@ * The DOM node pointing to the toolbar */ -class Toolbar { +aria.Toolbar = class Toolbar { constructor(toolbarNode) { this.toolbarNode = toolbarNode; this.items = this.toolbarNode.querySelectorAll('.toolbar-item'); @@ -112,5 +117,4 @@ class Toolbar { focusItem(element) { element.focus(); } -} - +}; From ab6c11068ed932e74ba984281ce435cf95b1bb09 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 7 Mar 2023 20:53:58 -0600 Subject: [PATCH 11/52] added ignore use before define --- content/patterns/listbox/examples/js/listbox-collapsible.js | 2 +- content/patterns/listbox/examples/js/listbox-rearrangeable.js | 3 ++- content/patterns/listbox/examples/js/listbox-scrollable.js | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/content/patterns/listbox/examples/js/listbox-collapsible.js b/content/patterns/listbox/examples/js/listbox-collapsible.js index da95ab7e2f..75f77cb79f 100644 --- a/content/patterns/listbox/examples/js/listbox-collapsible.js +++ b/content/patterns/listbox/examples/js/listbox-collapsible.js @@ -15,7 +15,7 @@ window.addEventListener('load', function () { const button = document.getElementById('exp_button'); const exListbox = new Listbox(document.getElementById('exp_elem_list')); - new ListboxButton(button, exListbox); + new ListboxButton(button, exListbox); /* eslint no-undef: off */ }); class ListboxButton { diff --git a/content/patterns/listbox/examples/js/listbox-rearrangeable.js b/content/patterns/listbox/examples/js/listbox-rearrangeable.js index 9784b70242..35ebb4872f 100644 --- a/content/patterns/listbox/examples/js/listbox-rearrangeable.js +++ b/content/patterns/listbox/examples/js/listbox-rearrangeable.js @@ -1,3 +1,4 @@ +/* eslint no-use-before-define: 0 */ // --> OFF /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document @@ -17,7 +18,7 @@ window.addEventListener('load', function () { // can be found in the dom. if (document.getElementById('ss_imp_list')) { var ex1ImportantListbox = new Listbox( - document.getElementById('ss_imp_list') + /* eslint no-undef: off */ document.getElementById('ss_imp_list') ); var ex1UnimportantListbox = new Listbox( document.getElementById('ss_unimp_list') diff --git a/content/patterns/listbox/examples/js/listbox-scrollable.js b/content/patterns/listbox/examples/js/listbox-scrollable.js index 68194d8ea0..b135ba097e 100644 --- a/content/patterns/listbox/examples/js/listbox-scrollable.js +++ b/content/patterns/listbox/examples/js/listbox-scrollable.js @@ -13,5 +13,7 @@ */ window.addEventListener('load', function () { - new Listbox(document.getElementById('ss_elem_list')); + new Listbox( + document.getElementById('ss_elem_list') + ); /* eslint no-undef: off */ }); From e97a6b9a719b9f076efa62da838b4529ea4a47af Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 7 Mar 2023 20:56:47 -0600 Subject: [PATCH 12/52] added ignore use before define --- content/patterns/listbox/examples/js/listbox.js | 1 + content/patterns/listbox/examples/js/toolbar.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/content/patterns/listbox/examples/js/listbox.js b/content/patterns/listbox/examples/js/listbox.js index 314e12bc99..ef9285ef2a 100644 --- a/content/patterns/listbox/examples/js/listbox.js +++ b/content/patterns/listbox/examples/js/listbox.js @@ -14,6 +14,7 @@ */ class Listbox { + /* eslint no-unused-vars : off */ constructor(listboxNode) { this.listboxNode = listboxNode; this.activeDescendant = this.listboxNode.getAttribute( diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index 8e4f0d4f4f..94e7c1b107 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -14,6 +14,7 @@ */ class Toolbar { + /* eslint no-unused-vars : off */ constructor(toolbarNode) { this.toolbarNode = toolbarNode; this.items = this.toolbarNode.querySelectorAll('.toolbar-item'); @@ -113,4 +114,3 @@ class Toolbar { element.focus(); } } - From 7da18fd2ccf8b576facf501563b95cbc4425f3ef Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 8 Mar 2023 09:18:27 -0600 Subject: [PATCH 13/52] updated eslint disable comments --- .eslintrc.json | 1 - content/patterns/listbox/examples/js/listbox-collapsible.js | 3 ++- .../patterns/listbox/examples/js/listbox-rearrangeable.js | 4 ++-- content/patterns/listbox/examples/js/listbox-scrollable.js | 5 ++--- content/patterns/listbox/examples/js/listbox.js | 2 +- content/patterns/listbox/examples/js/toolbar.js | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 46e1ffdcb7..02e73bdf48 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -57,7 +57,6 @@ "files": ["**/*.html"], "plugins": ["html"], "globals": { - "Listbox": true, "sourceCode": true }, "rules": { diff --git a/content/patterns/listbox/examples/js/listbox-collapsible.js b/content/patterns/listbox/examples/js/listbox-collapsible.js index 75f77cb79f..7d0e8866fc 100644 --- a/content/patterns/listbox/examples/js/listbox-collapsible.js +++ b/content/patterns/listbox/examples/js/listbox-collapsible.js @@ -1,3 +1,4 @@ +/* eslint no-undef: off */ /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document @@ -15,7 +16,7 @@ window.addEventListener('load', function () { const button = document.getElementById('exp_button'); const exListbox = new Listbox(document.getElementById('exp_elem_list')); - new ListboxButton(button, exListbox); /* eslint no-undef: off */ + new ListboxButton(button, exListbox); }); class ListboxButton { diff --git a/content/patterns/listbox/examples/js/listbox-rearrangeable.js b/content/patterns/listbox/examples/js/listbox-rearrangeable.js index 35ebb4872f..6f84726306 100644 --- a/content/patterns/listbox/examples/js/listbox-rearrangeable.js +++ b/content/patterns/listbox/examples/js/listbox-rearrangeable.js @@ -1,4 +1,4 @@ -/* eslint no-use-before-define: 0 */ // --> OFF +/* eslint no-undef: off */ /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document @@ -18,7 +18,7 @@ window.addEventListener('load', function () { // can be found in the dom. if (document.getElementById('ss_imp_list')) { var ex1ImportantListbox = new Listbox( - /* eslint no-undef: off */ document.getElementById('ss_imp_list') + document.getElementById('ss_imp_list') ); var ex1UnimportantListbox = new Listbox( document.getElementById('ss_unimp_list') diff --git a/content/patterns/listbox/examples/js/listbox-scrollable.js b/content/patterns/listbox/examples/js/listbox-scrollable.js index b135ba097e..e90e80aa07 100644 --- a/content/patterns/listbox/examples/js/listbox-scrollable.js +++ b/content/patterns/listbox/examples/js/listbox-scrollable.js @@ -1,3 +1,4 @@ +/* eslint no-undef: off */ /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document @@ -13,7 +14,5 @@ */ window.addEventListener('load', function () { - new Listbox( - document.getElementById('ss_elem_list') - ); /* eslint no-undef: off */ + new Listbox(document.getElementById('ss_elem_list')); }); diff --git a/content/patterns/listbox/examples/js/listbox.js b/content/patterns/listbox/examples/js/listbox.js index ef9285ef2a..b21bbb9f1e 100644 --- a/content/patterns/listbox/examples/js/listbox.js +++ b/content/patterns/listbox/examples/js/listbox.js @@ -1,3 +1,4 @@ +/* eslint no-unused-vars : off */ /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document @@ -14,7 +15,6 @@ */ class Listbox { - /* eslint no-unused-vars : off */ constructor(listboxNode) { this.listboxNode = listboxNode; this.activeDescendant = this.listboxNode.getAttribute( diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index 94e7c1b107..046ae6e514 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -1,3 +1,4 @@ +/* eslint no-unused-vars : off */ /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document @@ -14,7 +15,6 @@ */ class Toolbar { - /* eslint no-unused-vars : off */ constructor(toolbarNode) { this.toolbarNode = toolbarNode; this.items = this.toolbarNode.querySelectorAll('.toolbar-item'); From a9fb0f4a855ab5c6e721d0b39d44bf1af385224b Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 14 Mar 2023 13:48:21 -0500 Subject: [PATCH 14/52] removed eslint ignore comments --- content/patterns/listbox/examples/js/listbox-collapsible.js | 1 - content/patterns/listbox/examples/js/listbox-rearrangeable.js | 1 - content/patterns/listbox/examples/js/listbox-scrollable.js | 1 - content/patterns/listbox/examples/js/listbox.js | 1 - content/patterns/listbox/examples/js/toolbar.js | 1 - 5 files changed, 5 deletions(-) diff --git a/content/patterns/listbox/examples/js/listbox-collapsible.js b/content/patterns/listbox/examples/js/listbox-collapsible.js index 8127141729..96475b10d8 100644 --- a/content/patterns/listbox/examples/js/listbox-collapsible.js +++ b/content/patterns/listbox/examples/js/listbox-collapsible.js @@ -1,4 +1,3 @@ -/* eslint no-undef: off */ /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document diff --git a/content/patterns/listbox/examples/js/listbox-rearrangeable.js b/content/patterns/listbox/examples/js/listbox-rearrangeable.js index b6363f6ed5..561b5d519a 100644 --- a/content/patterns/listbox/examples/js/listbox-rearrangeable.js +++ b/content/patterns/listbox/examples/js/listbox-rearrangeable.js @@ -1,4 +1,3 @@ -/* eslint no-undef: off */ /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document diff --git a/content/patterns/listbox/examples/js/listbox-scrollable.js b/content/patterns/listbox/examples/js/listbox-scrollable.js index 5537d34f2a..4020406040 100644 --- a/content/patterns/listbox/examples/js/listbox-scrollable.js +++ b/content/patterns/listbox/examples/js/listbox-scrollable.js @@ -1,4 +1,3 @@ -/* eslint no-undef: off */ /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document diff --git a/content/patterns/listbox/examples/js/listbox.js b/content/patterns/listbox/examples/js/listbox.js index bcd960b2b0..96ba215f41 100644 --- a/content/patterns/listbox/examples/js/listbox.js +++ b/content/patterns/listbox/examples/js/listbox.js @@ -1,4 +1,3 @@ -/* eslint no-unused-vars : off */ /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index 891658108f..9f537abac3 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -1,4 +1,3 @@ -/* eslint no-unused-vars : off */ /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document From b47e116974fde980ea5f2f062cb610c1f6432113 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 14 Mar 2023 13:58:10 -0500 Subject: [PATCH 15/52] update strict definition --- .../listbox/examples/js/listbox-collapsible.js | 10 ++++++++-- .../listbox/examples/js/listbox-rearrangeable.js | 10 ++++++++-- .../patterns/listbox/examples/js/listbox-scrollable.js | 10 ++++++++-- content/patterns/listbox/examples/js/listbox.js | 3 +++ content/patterns/listbox/examples/js/toolbar.js | 3 +++ 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/content/patterns/listbox/examples/js/listbox-collapsible.js b/content/patterns/listbox/examples/js/listbox-collapsible.js index 96475b10d8..4beaae9f5c 100644 --- a/content/patterns/listbox/examples/js/listbox-collapsible.js +++ b/content/patterns/listbox/examples/js/listbox-collapsible.js @@ -3,10 +3,16 @@ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ -/* global aria */ - 'use strict'; +/** + * @namespace aria + * @description + * The aria namespace is used to support sharing class definitions between example files + * without causing eslint errors for undefined classes + */ +var aria = aria || {}; + /** * ARIA Collapsible Dropdown Listbox Example * diff --git a/content/patterns/listbox/examples/js/listbox-rearrangeable.js b/content/patterns/listbox/examples/js/listbox-rearrangeable.js index 561b5d519a..b5ba8ee809 100644 --- a/content/patterns/listbox/examples/js/listbox-rearrangeable.js +++ b/content/patterns/listbox/examples/js/listbox-rearrangeable.js @@ -3,10 +3,16 @@ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ -/* global aria */ - 'use strict'; +/** + * @namespace aria + * @description + * The aria namespace is used to support sharing class definitions between example files + * without causing eslint errors for undefined classes + */ +var aria = aria || {}; + /** * ARIA Listbox Examples * diff --git a/content/patterns/listbox/examples/js/listbox-scrollable.js b/content/patterns/listbox/examples/js/listbox-scrollable.js index 4020406040..4d874624bf 100644 --- a/content/patterns/listbox/examples/js/listbox-scrollable.js +++ b/content/patterns/listbox/examples/js/listbox-scrollable.js @@ -3,10 +3,16 @@ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ -/* global aria */ - 'use strict'; +/** + * @namespace aria + * @description + * The aria namespace is used to support sharing classe definitions between example files + * without causing eslint errors for undefined classes + */ +var aria = aria || {}; + /** * ARIA Scrollable Listbox Example * diff --git a/content/patterns/listbox/examples/js/listbox.js b/content/patterns/listbox/examples/js/listbox.js index 96ba215f41..8c2c389af2 100644 --- a/content/patterns/listbox/examples/js/listbox.js +++ b/content/patterns/listbox/examples/js/listbox.js @@ -7,6 +7,9 @@ /** * @namespace aria + * @description + * The aria namespace is used to support sharing class definitions between example files + * without causing eslint errors for undefined classes */ var aria = aria || {}; diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index 9f537abac3..caf43df894 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -7,6 +7,9 @@ /** * @namespace aria + * @description + * The aria namespace is used to support sharing class definitions between example files + * without causing eslint errors for undefined classes */ var aria = aria || {}; From 87ddc11ee6fd645bc5c89ac48f82b840677f3c64 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 14 Mar 2023 14:27:01 -0500 Subject: [PATCH 16/52] fixed spelling error --- content/patterns/listbox/examples/js/listbox-scrollable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/patterns/listbox/examples/js/listbox-scrollable.js b/content/patterns/listbox/examples/js/listbox-scrollable.js index 4d874624bf..fe7b2658f7 100644 --- a/content/patterns/listbox/examples/js/listbox-scrollable.js +++ b/content/patterns/listbox/examples/js/listbox-scrollable.js @@ -8,7 +8,7 @@ /** * @namespace aria * @description - * The aria namespace is used to support sharing classe definitions between example files + * The aria namespace is used to support sharing class definitions between example files * without causing eslint errors for undefined classes */ var aria = aria || {}; From 49f8bca3e2fc34d8fa0204977c280c588f330211 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 11 Apr 2023 15:14:58 -0500 Subject: [PATCH 17/52] fixed bug with keyboard shortcut not scrolling focused item into view --- content/patterns/listbox/examples/js/listbox.js | 1 + 1 file changed, 1 insertion(+) diff --git a/content/patterns/listbox/examples/js/listbox.js b/content/patterns/listbox/examples/js/listbox.js index 8c2c389af2..632063f25c 100644 --- a/content/patterns/listbox/examples/js/listbox.js +++ b/content/patterns/listbox/examples/js/listbox.js @@ -118,6 +118,7 @@ aria.Listbox = class Listbox { } else { this.moveDownItems(); } + this.updateScroll(); return; } From 224e6355727ae580f94fd9f3d9661a895aba7ab9 Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 10:00:14 -0700 Subject: [PATCH 18/52] match section id conventions, change ol to ul --- content/patterns/listbox/examples/listbox-scrollable.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 05af27e53f..258a78a720 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -108,8 +108,8 @@

    Notes

    -

    Accessibility Features

    -
      +

      Accessibility Features

      +
      • The listbox receives accessibility focus via aria-activedescendant. This enables users to perceive the presence of the options, and enables assistive technology users to comprehend the size of the list of options.
      • @@ -131,7 +131,7 @@

        Accessibility Features

      • To make it easier to distinguish the selected listbox option from other options, selection creates a 2 pixel border above and below the option.
      -
    +
    From 319d14b36161cf1bab5ea415e8b7de3eea2ed6ac Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 10:47:12 -0700 Subject: [PATCH 19/52] remove redundant and inaccurate information from accessibility features --- .../patterns/listbox/examples/listbox-scrollable.html | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 258a78a720..4952cf2b97 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -110,15 +110,6 @@

    Notes

    Accessibility Features

      -
    • - The listbox receives accessibility focus via aria-activedescendant. - This enables users to perceive the presence of the options, and enables assistive technology users to comprehend the size of the list of options.
    • -
    • - Navigating the list of options does not set the selection of an option. - This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. - The value is set when users press Space or Enter. - Selected options have a check preceding the text label for the option. -
    • Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. @@ -127,7 +118,7 @@

      Accessibility Features

    • To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes:
        -
      • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the combobox or list.
      • +
      • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the list.
      • To make it easier to distinguish the selected listbox option from other options, selection creates a 2 pixel border above and below the option.
    • From 6394905a8797877b198bd649bb9ec3058ae61966 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 25 Apr 2023 16:14:49 -0500 Subject: [PATCH 20/52] added aria-hidden to checkmark --- content/index/index.html | 1 + .../patterns/listbox/examples/css/listbox.css | 2 +- .../examples/listbox-rearrangeable.html | 99 +++++++++++++++---- test/tests/listbox_rearrangeable.js | 12 +++ 4 files changed, 93 insertions(+), 21 deletions(-) diff --git a/content/index/index.html b/content/index/index.html index e9f3c4d46c..813694ccbf 100644 --- a/content/index/index.html +++ b/content/index/index.html @@ -620,6 +620,7 @@

      Examples By Properties and States

      • Button (IDL Version)
      • +
      • Listboxes with Rearrangeable Options
      • Editor Menubar (HC)
      • Horizontal Multi-Thumb Slider (HC)
      • Color Viewer Slider (HC)
      • diff --git a/content/patterns/listbox/examples/css/listbox.css b/content/patterns/listbox/examples/css/listbox.css index 30b5fc6645..a410be2891 100644 --- a/content/patterns/listbox/examples/css/listbox.css +++ b/content/patterns/listbox/examples/css/listbox.css @@ -59,7 +59,7 @@ outline: 2px solid currentcolor; } -[role="option"][aria-selected="true"]::before { +[role="option"][aria-selected="true"] span.checkmark::before { position: absolute; left: 0.5em; content: "✓"; diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 91585422a0..04689364ac 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -59,16 +59,37 @@

        Example 1: Single-Select Listbox

        Important Features:
          -
        • Proximity of public K-12 schools
        • -
        • Proximity of child-friendly parks
        • -
        • Proximity of grocery shopping
        • -
        • Proximity of fast food
        • -
        • Proximity of fine dining
        • -
        • Neighborhood walkability
        • -
        • Availability of public transit
        • -
        • Proximity of hospital and medical services
        • -
        • Level of traffic noise
        • -
        • Access to major highways
        • +
        • + + Proximity of public K-12 schools +
        • +
        • + + Proximity of child-friendly parks
        • +
        • + + Proximity of grocery shopping
        • +
        • + + Proximity of fast food
        • +
        • + + Proximity of fine dining
        • +
        • + + Neighborhood walkability
        • +
        • + + Availability of public transit
        • +
        • + + Proximity of hospital and medical services
        • +
        • + + Level of traffic noise
        • +
        • + + Access to major highways
      + + + aria-hidden="true" + span + + Removes the character entity used for the check mark icon from the accessibility tree to prevent it from being included in the accessible name of the option. + +
    diff --git a/test/tests/listbox_rearrangeable.js b/test/tests/listbox_rearrangeable.js index 0010fbb5d8..083448cb15 100644 --- a/test/tests/listbox_rearrangeable.js +++ b/test/tests/listbox_rearrangeable.js @@ -15,6 +15,7 @@ const ex = { listboxSelector: '#ex1 [role="listbox"]', importantSelector: '#ex1 [role="listbox"]#ss_imp_list', optionSelector: '#ex1 [role="option"]', + spanSelector: '#ex1 [role="option"] span.checkmark', numOptions: 10, firstOptionSelector: '#ex1 #ss_opt1', lastOptionSelector: '#ex1 #ss_opt10', @@ -23,6 +24,7 @@ const ex = { listboxSelector: '#ex2 [role="listbox"]', availableSelector: '#ex2 [role="listbox"]#ms_imp_list', optionSelector: '#ex2 [role="option"]', + spanSelector: '#ex2 [role="option"] span.checkmark', numOptions: 10, firstOptionSelector: '#ex2 #ms_opt1', lastOptionSelector: '#ex2 #ms_opt10', @@ -126,6 +128,16 @@ ariaTest( 'role="option" on li elements', exampleFile, 'option-role', + async (t) => { + await assertAttributeValues(t, ex[1].spanSelector, 'aria-hidden', 'true'); + await assertAttributeValues(t, ex[2].spanSelector, 'aria-hidden', 'true'); + } +); + +ariaTest( + 'aria-hidden="true" on li > span elements', + exampleFile, + 'span-aria-hidden', async (t) => { await assertAriaRoles(t, 'ex1', 'option', 10, 'li'); await assertAriaRoles(t, 'ex2', 'option', 10, 'li'); From 8afc8447276ebdb1cc51b38d3118e404c04be08c Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 25 Apr 2023 16:35:39 -0500 Subject: [PATCH 21/52] updated scrollable and group listboxes --- content/index/index.html | 2 + .../listbox/examples/listbox-grouped.html | 68 +++++++-- .../listbox/examples/listbox-scrollable.html | 138 ++++++++++++++---- test/tests/listbox_grouped.js | 10 ++ test/tests/listbox_rearrangeable.js | 8 +- test/tests/listbox_scrollable.js | 10 ++ 6 files changed, 194 insertions(+), 42 deletions(-) diff --git a/content/index/index.html b/content/index/index.html index 813694ccbf..08a437bcbc 100644 --- a/content/index/index.html +++ b/content/index/index.html @@ -620,7 +620,9 @@

    Examples By Properties and States

    • Button (IDL Version)
    • +
    • Listbox with Grouped Options
    • Listboxes with Rearrangeable Options
    • +
    • Scrollable Listbox
    • Editor Menubar (HC)
    • Horizontal Multi-Thumb Slider (HC)
    • Color Viewer Slider (HC)
    • diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index d024254218..90ebcb098d 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -53,23 +53,59 @@

      Example

        -
      • Cat
      • -
      • Dog
      • -
      • Tiger
      • -
      • Reindeer
      • -
      • Raccoon
      • +
      • + + Cat +
      • +
      • + + Dog +
      • +
      • + + Tiger +
      • +
      • + + Reindeer +
      • +
      • + + Raccoon +
        - -
      • Dolphin
      • -
      • Flounder
      • -
      • Eel
      • + +
      • + + Dolphin +
      • +
      • + + Flounder +
      • +
      • + + Eel +
        -
      • Falcon
      • -
      • Winged Horse
      • -
      • Owl
      • +
      • + + Falcon +
      • +
      • + + Winged Horse +
      • +
      • + + Owl +
      @@ -238,6 +274,14 @@

      Role, Property, State, and Tabindex Attributes

    + + + aria-hidden="true" + span + + Removes the character entity used for the check mark icon from the accessibility tree to prevent it from being included in the accessible name of the option. + +
    diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 4952cf2b97..3c450f9a48 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -52,32 +52,110 @@

    Example

    Transuranium elements:
    @@ -223,6 +301,14 @@

    Role, Property, State, and Tabindex Attributes

    + + + aria-hidden="true" + span + + Removes the character entity used for the check mark icon from the accessibility tree to prevent it from being included in the accessible name of the option. + + diff --git a/test/tests/listbox_grouped.js b/test/tests/listbox_grouped.js index 8b4846e1c3..9b5761c15a 100644 --- a/test/tests/listbox_grouped.js +++ b/test/tests/listbox_grouped.js @@ -11,6 +11,7 @@ const exampleFile = 'content/patterns/listbox/examples/listbox-grouped.html'; const ex = { listboxSelector: '#ex [role="listbox"]', optionSelector: '#ex [role="option"]', + spanSelector: '#ex [role="option"] span.checkmark', optionIdBase: '#ss_elem_', }; @@ -96,6 +97,15 @@ ariaTest( } ); +ariaTest( + 'aria-hidden="true" on li > span elements', + exampleFile, + 'span-aria-hidden', + async (t) => { + await assertAttributeValues(t, ex.spanSelector, 'aria-hidden', 'true'); + } +); + ariaTest( '"aria-selected" on option elements', exampleFile, diff --git a/test/tests/listbox_rearrangeable.js b/test/tests/listbox_rearrangeable.js index 083448cb15..892881ea76 100644 --- a/test/tests/listbox_rearrangeable.js +++ b/test/tests/listbox_rearrangeable.js @@ -125,9 +125,9 @@ ariaTest( ); ariaTest( - 'role="option" on li elements', + 'aria-hidden="true" on li > span elements', exampleFile, - 'option-role', + 'span-aria-hidden', async (t) => { await assertAttributeValues(t, ex[1].spanSelector, 'aria-hidden', 'true'); await assertAttributeValues(t, ex[2].spanSelector, 'aria-hidden', 'true'); @@ -135,9 +135,9 @@ ariaTest( ); ariaTest( - 'aria-hidden="true" on li > span elements', + 'role="option" on li elements', exampleFile, - 'span-aria-hidden', + 'option-role', async (t) => { await assertAriaRoles(t, 'ex1', 'option', 10, 'li'); await assertAriaRoles(t, 'ex2', 'option', 10, 'li'); diff --git a/test/tests/listbox_scrollable.js b/test/tests/listbox_scrollable.js index cba46a1abf..d33b474880 100644 --- a/test/tests/listbox_scrollable.js +++ b/test/tests/listbox_scrollable.js @@ -11,6 +11,7 @@ const exampleFile = 'content/patterns/listbox/examples/listbox-scrollable.html'; const ex = { listboxSelector: '#ex [role="listbox"]', optionSelector: '#ex [role="option"]', + spanSelector: '#ex [role="option"] span.checkmark', numOptions: 26, firstOptionSelector: '#ex #ss_elem_Np', }; @@ -78,6 +79,15 @@ ariaTest( } ); +ariaTest( + 'aria-hidden="true" on li > span elements', + exampleFile, + 'span-aria-hidden', + async (t) => { + await assertAttributeValues(t, ex.spanSelector, 'aria-hidden', 'true'); + } +); + ariaTest( '"aria-selected" on option elements', exampleFile, From b89d17572615016e352f09a8fa568bf9e2d0bad5 Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 17:37:26 -0700 Subject: [PATCH 22/52] Move notes into a section and fix grammar errors. --- .../listbox/examples/listbox-scrollable.html | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 3c450f9a48..117636667a 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -161,32 +161,35 @@

    Example

    -

    Notes

    + + +
    +

    Notes

    This listbox is scrollable; it has more options than its height can accommodate.

    -
      +
      • Scrolling only works as expected if the listbox is the options' offsetParent. The example uses position: relative on the listbox to that effect.
      • When an option is focused that isn't (fully) visible, the listbox's scroll position is updated: -
          +
          • If Up Arrow or Down Arrow is pressed, the previous or next option is scrolled into view.
          • If Home or End is pressed, the listbox scrolls all the way to the top or to the bottom.
          • If focusItem is called, the focused option will be scrolled to the top of the view if it was located above it or to the bottom if it was below it.
          • If the mouse is clicked on a partially visible option, it will be scrolled fully into view.
          • -
        +
    1. When a fully visible option is focused in any way, no scrolling occurs.
    2. Normal scrolling through any scrolling mechanism (including Page Up and Page Down) works as expected. The scroll position will jump as described for focusItem if a means other than a mouse click is used to change focus after scrolling.
    3. -
    +
    -
    -

    Accessibility Features

    +
    +

    Accessibility Features

    • Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. @@ -206,7 +209,7 @@

      Accessibility Features

      Keyboard Support

      - The example listboxes on this page implement the following keyboard interface. + The example listbox 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 Listbox Pattern.

      @@ -240,7 +243,7 @@

      Keyboard Support

      Role, Property, State, and Tabindex Attributes

      - The example listboxes on this page implement the following ARIA roles, states, and properties. + The example listbox 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 Listbox Pattern.

      From 3273f9316344375315fa36f9212770e11d22fa98 Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 17:43:41 -0700 Subject: [PATCH 23/52] Remove unnecessary link to utils.js from source code section. --- content/patterns/listbox/examples/listbox-scrollable.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 117636667a..7d73dbb4e1 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -325,7 +325,7 @@

      Javascript and CSS Source Code

    • Javascript: - listbox.js, listbox-scrollable.js, utils.js + listbox.js, listbox-scrollable.js
    • From 8638fca667a7855a76462094109b2fb798408b8c Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 18:07:32 -0700 Subject: [PATCH 24/52] Remove unnecessary link to utils.js in source code documentation section --- content/patterns/listbox/examples/listbox-rearrangeable.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 04689364ac..908076e1a6 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -491,7 +491,7 @@

      Javascript and CSS Source Code

    • Javascript: - listbox.js, toolbar.js, listbox-rearrangeable.js, utils.js + listbox.js, toolbar.js, listbox-rearrangeable.js
    • From 0d3c334060406b85d111833248146bb788a2d529 Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 18:37:13 -0700 Subject: [PATCH 25/52] Change quote char entities to quote characters --- .../examples/listbox-rearrangeable.html | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 908076e1a6..94236760b4 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -123,10 +123,10 @@

      Notes

    • If an option is selected, the selected option receives focus.
    • -
    • Only one option may be selected at a time (have aria-selected="true").
    • +
    • Only one option may be selected at a time (have aria-selected="true").
    • As the user moves focus in the list, selection also moves. - That is, both the value of aria-activedescendant and the element that has aria-selected="true" change. + That is, both the value of aria-activedescendant and the element that has aria-selected="true" change.
    • @@ -212,9 +212,9 @@

      Notes

    • - Unlike example 1, more than one option may be selected at a time (have aria-selected="true"). + Unlike example 1, more than one option may be selected at a time (have aria-selected="true").
        -
      1. The multi-select capability is communicated to assistive technologies by setting aria-multiselectable="true" on the listbox element.
      2. +
      3. The multi-select capability is communicated to assistive technologies by setting aria-multiselectable="true" on the listbox element.
      4. All option elements have a value set for aria-selected.
      5. Selected options have aria-selected set to true and all others have it set to false.
      6. Keys that move focus do not change the selected state of an option.
      7. @@ -234,10 +234,10 @@

        Accessibility Features

      8. Action buttons have the following shortcuts:
          -
        • "Up": Alt + Up Arrow
        • -
        • "Down": Alt + Down Arrow
        • -
        • "Add": Enter
        • -
        • "Not Important", "Important", and "Remove": Delete
        • +
        • "Up": Alt + Up Arrow
        • +
        • "Down": Alt + Down Arrow
        • +
        • "Add": Enter
        • +
        • "Not Important", "Important", and "Remove": Delete
      9. Availability of the shortcuts is communicated to assistive technologies via the aria-keyshortcuts property on the button elements.
      10. @@ -247,7 +247,7 @@

        Accessibility Features

      11. Using a shortcut key intentionally places focus to optimize both screen reader and keyboard usability. - For example, pressing Alt + Up Arrow in the "Important Features" list keeps focus on the option that is moved up, enabling all keyboard users to easily perform consecutive move operations for an option and screen reader users to hear the position of an option after it is moved. + For example, pressing Alt + Up Arrow in the "Important Features" list keeps focus on the option that is moved up, enabling all keyboard users to easily perform consecutive move operations for an option and screen reader users to hear the position of an option after it is moved. Similarly, pressing Enter in the available options list leaves focus in the available options list. If the option that had focus before the add operation is no longer present in the list, focus lands on the first of the subsequent options that is still present.
      12. @@ -404,19 +404,19 @@

        Role, Property, State, and Tabindex Attributes

    • - + - + - + - + @@ -447,7 +447,7 @@

      Role, Property, State, and Tabindex Attributes

      - + - + - + + + + +
      aria-labelledby="ID_REF"aria-labelledby="ID_REF" ul Applied to the element with the listbox role, it refers to the span containing its label.
      tabindex="0"tabindex="0" ul Applied to the element with the listbox role, it puts the listbox in the tab sequence.
      aria-multiselectable="true"aria-multiselectable="true" ul
        @@ -428,14 +428,14 @@

        Role, Property, State, and Tabindex Attributes

      aria-activedescendant="ID_REF"aria-activedescendant="ID_REF" ul
      • Applied to the element with the listbox role, tells assistive technologies which of the options, if any, is visually indicated as having keyboard focus.
      • DOM focus remains on the ul element and the idref specified for aria-activedescendant refers to the li element that is visually styled as focused.
      • When navigation keys, such as Down Arrow, are pressed, the JavaScript changes the value.
      • -
      • When the listbox is empty, aria-activedescendant="".
      • +
      • When the listbox is empty, aria-activedescendant="".
      aria-selected="true"aria-selected="true" li
        @@ -460,7 +460,7 @@

        Role, Property, State, and Tabindex Attributes

      aria-selected="false"aria-selected="false" li
        @@ -472,7 +472,7 @@

        Role, Property, State, and Tabindex Attributes

      aria-hidden="true"aria-hidden="true" span Removes the character entity used for the check mark icon from the accessibility tree to prevent it from being included in the accessible name of the option. From 439f31a4aa80b0e4616c9dceed3d1b588c9a73d0 Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 18:46:52 -0700 Subject: [PATCH 26/52] change ol to ul where appropriate --- .../examples/listbox-rearrangeable.html | 47 ++++++++----------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 94236760b4..4b40a93c5f 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -107,28 +107,28 @@

      Example 1: Single-Select Listbox

      Notes

      -
        +
        • Assistive technologies are told which option in the list is visually focused by the value of aria-activedescendant: -
            +
            • DOM focus remains on the listbox element.
            • When a key that moves focus is pressed or an option is clicked, JavaScript changes the value of aria-activedescendant on the listbox element.
            • If the listbox element does not contain any options, aria-activedescendant does not have a value.
            • -
          +
      1. When Tab moves focus into either listbox: -
          +
          • If none of the options are selected, the first option receives focus.
          • If an option is selected, the selected option receives focus.
          • -
        +
      2. Only one option may be selected at a time (have aria-selected="true").
      3. As the user moves focus in the list, selection also moves. That is, both the value of aria-activedescendant and the element that has aria-selected="true" change.
      4. -
      +
      @@ -195,42 +195,42 @@

      Example 2: Multi-Select Listbox

      Notes

      -
        +
        • Like in example 1, assistive technologies are told which option in the list is visually focused by the value of aria-activedescendant: -
            +
            • DOM focus remains on the listbox element.
            • When a key that moves focus is pressed or an option is clicked, JavaScript changes the value of aria-activedescendant on the listbox element.
            • If the listbox element does not contain any options, aria-activedescendant does not have a value.
            • -
          +
      1. When Tab moves focus into either listbox: -
          +
          • If none of the options are selected, focus is set on the first option.
          • If one or more options are selected, focus is set on the first selected option.
          • -
        +
      2. Unlike example 1, more than one option may be selected at a time (have aria-selected="true"). -
          +
          • The multi-select capability is communicated to assistive technologies by setting aria-multiselectable="true" on the listbox element.
          • All option elements have a value set for aria-selected.
          • Selected options have aria-selected set to true and all others have it set to false.
          • Keys that move focus do not change the selected state of an option.
          • -
        +
      3. Users can toggle the selected state of the focused option with Space or click.
      4. -
      +

      Accessibility Features

      -
        +
        • Keyboard shortcuts for action buttons: -
            +
            • Action buttons have the following shortcuts:
                @@ -251,19 +251,10 @@

                Accessibility Features

                Similarly, pressing Enter in the available options list leaves focus in the available options list. If the option that had focus before the add operation is no longer present in the list, focus lands on the first of the subsequent options that is still present. -
          +
      1. In example 1, since there are four action buttons, a toolbar widget is used to group all the action buttons into a single tab stop.
      2. Live regions provide confirmation of completed actions.
      3. -
      4. - The listbox receives accessibility focus via aria-activedescendant. - This enables users to perceive the presence of the options, and enables assistive technology users to comprehend the size of the list of options.
      5. -
      6. - Navigating the list of options does not set the selection of an option. - This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. - The value is set when users press Space or Enter. - Selected options have a check preceding the text label for the option. -
      7. Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. @@ -272,11 +263,11 @@

        Accessibility Features

      8. To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes:
          -
        • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the combobox or list.
        • +
        • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the list.
        • To make it easier to distinguish the selected listbox option from other options, selection creates a 2 pixel border above and below the option.
      9. -
      +
      From 5a9b73445f84d3644f024844c7e4cce53b191837 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 25 Apr 2023 21:03:16 -0500 Subject: [PATCH 27/52] fixed aria-hidden bug --- .../listbox/examples/listbox-grouped.html | 24 ++++----- .../listbox/examples/listbox-scrollable.html | 52 +++++++++---------- test/tests/listbox_grouped.js | 10 ++-- 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index 90ebcb098d..00a890f0ee 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -54,56 +54,56 @@

      Example

      • - + Cat
      • - + Dog
      • - + Tiger
      • - + Reindeer
      • - + Raccoon
      • - + Dolphin
      • - + Flounder
      • - + Eel
      • - + Falcon
      • - + Winged Horse
      • - + Owl
      diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 3c450f9a48..61bd106acd 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -53,107 +53,107 @@

      Example

      Transuranium elements:
      • - + Neptunium
      • - + Plutonium
      • - + Americium
      • - + Curium
      • - + Berkelium
      • - + Californium
      • - + Einsteinium
      • - + Fermium
      • - + Mendelevium
      • - + Nobelium
      • - + Lawrencium
      • - + Rutherfordium
      • - + Dubnium
      • - + Seaborgium
      • - + Bohrium
      • - + Hassium
      • - + Meitnerium
      • - + Darmstadtium
      • - + Roentgenium
      • - + Copernicium
      • - + Nihonium
      • - + Flerovium
      • - + Moscovium
      • - + Livermorium
      • - + Tennessine
      • - + Oganesson
      diff --git a/test/tests/listbox_grouped.js b/test/tests/listbox_grouped.js index 9b5761c15a..32ef9927f1 100644 --- a/test/tests/listbox_grouped.js +++ b/test/tests/listbox_grouped.js @@ -11,7 +11,6 @@ const exampleFile = 'content/patterns/listbox/examples/listbox-grouped.html'; const ex = { listboxSelector: '#ex [role="listbox"]', optionSelector: '#ex [role="option"]', - spanSelector: '#ex [role="option"] span.checkmark', optionIdBase: '#ss_elem_', }; @@ -98,11 +97,16 @@ ariaTest( ); ariaTest( - 'aria-hidden="true" on li > span elements', + 'aria-hidden="true" on li > span.checkmark elements', exampleFile, 'span-aria-hidden', async (t) => { - await assertAttributeValues(t, ex.spanSelector, 'aria-hidden', 'true'); + await assertAttributeValues( + t, + `${ex.optionSelector} span.checkmark`, + 'aria-hidden', + 'true' + ); } ); From 5b7eeb75aafd60375b367050538d1178de1aca25 Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 19:01:14 -0700 Subject: [PATCH 28/52] Add activedescendant note above keyboard documentation table --- .../patterns/listbox/examples/listbox-rearrangeable.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 4b40a93c5f..064f4f484f 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -276,6 +276,12 @@

      Keyboard Support

      The example listboxes 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 Listbox Pattern.

      +

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

      From 46db1acbc9cccd5514a8c0ad217cb9cff745f19c Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 19:04:56 -0700 Subject: [PATCH 29/52] Add activedescendant note above keyboard documentation table --- content/patterns/listbox/examples/listbox-scrollable.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index dc299a5f8f..839f5b777f 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -212,6 +212,12 @@

      Keyboard Support

      The example listbox 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 Listbox Pattern.

      +

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

      From ec3540b39e67c78bb8ed433534da3e4cc0f195ac Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 19:10:12 -0700 Subject: [PATCH 30/52] Add activedescendant note above keyboard documentation table --- .../patterns/listbox/examples/listbox-collapsible.html | 6 ++++++ content/patterns/listbox/examples/listbox-grouped.html | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/content/patterns/listbox/examples/listbox-collapsible.html b/content/patterns/listbox/examples/listbox-collapsible.html index 420beb4917..34a25d7efd 100644 --- a/content/patterns/listbox/examples/listbox-collapsible.html +++ b/content/patterns/listbox/examples/listbox-collapsible.html @@ -150,6 +150,12 @@

      Keyboard Support

      The example listbox 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 Listbox Pattern.

      +

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

      diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index 00a890f0ee..646db5669f 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -167,9 +167,15 @@

      Accessibility Features

      Keyboard Support

      - The example listboxes on this page implement the following keyboard interface. + The example listbox 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 Listbox Pattern.

      +

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

      From 9374333018f0b6dfec1e4bc34fd6ea55bb715409 Mon Sep 17 00:00:00 2001 From: Matt King Date: Tue, 25 Apr 2023 19:32:14 -0700 Subject: [PATCH 31/52] Make activedescendant documentation in states and props table consistent with other examples --- .../patterns/listbox/examples/listbox-collapsible.html | 5 ++--- content/patterns/listbox/examples/listbox-grouped.html | 8 ++++++-- .../listbox/examples/listbox-rearrangeable.html | 10 +++++++--- .../patterns/listbox/examples/listbox-scrollable.html | 8 ++++++-- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-collapsible.html b/content/patterns/listbox/examples/listbox-collapsible.html index 34a25d7efd..4a479087b8 100644 --- a/content/patterns/listbox/examples/listbox-collapsible.html +++ b/content/patterns/listbox/examples/listbox-collapsible.html @@ -290,10 +290,9 @@

      Role, Property, State, and Tabindex Attributes

      diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 064f4f484f..970c51d052 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -277,7 +277,7 @@

      Keyboard Support

      Other variations and options for the keyboard interface are described in the Keyboard Interaction section of the Listbox Pattern.

      - NOTE: When visual focus is on an option in a listbox, DOM focus remains on the listbox element and the value of aria-activedescendant on the listbox refers to the descendant option that is visually indicated as focused. + NOTE: When visual focus is on an option in these implementations of listbox, DOM focus remains on the listbox element and the value of aria-activedescendant on the listbox refers to the descendant option that is visually indicated as focused. Where the following descriptions of keyboard commands mention focus, they are referring to the visual focus indicator, not DOM focus. For more information about this focus management technique, see Managing Focus in Composites Using aria-activedescendant. @@ -429,10 +429,14 @@

      Role, Property, State, and Tabindex Attributes

      diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 839f5b777f..69c19683f0 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -286,9 +286,13 @@

      Role, Property, State, and Tabindex Attributes

      From ac078da4adc49081fba8174af03729aecbeae367 Mon Sep 17 00:00:00 2001 From: Matt King Date: Wed, 26 Apr 2023 09:42:02 -0700 Subject: [PATCH 32/52] Move documentation from notes sections into APG template format --- .../examples/listbox-rearrangeable.html | 72 +++++-------------- 1 file changed, 16 insertions(+), 56 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 970c51d052..45f51cd4a1 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -106,29 +106,6 @@

      Example 1: Single-Select Listbox

      -

      Notes

      -
        -
      • - Assistive technologies are told which option in the list is visually focused by the value of aria-activedescendant: -
          -
        • DOM focus remains on the listbox element.
        • -
        • When a key that moves focus is pressed or an option is clicked, JavaScript changes the value of aria-activedescendant on the listbox element.
        • -
        • If the listbox element does not contain any options, aria-activedescendant does not have a value.
        • -
        -
      • -
      • - When Tab moves focus into either listbox: -
          -
        • If none of the options are selected, the first option receives focus.
        • -
        • If an option is selected, the selected option receives focus.
        • -
        -
      • -
      • Only one option may be selected at a time (have aria-selected="true").
      • -
      • - As the user moves focus in the list, selection also moves. - That is, both the value of aria-activedescendant and the element that has aria-selected="true" change. -
      • -
      @@ -194,34 +171,6 @@

      Example 2: Multi-Select Listbox

      -

      Notes

      -
        -
      • - Like in example 1, assistive technologies are told which option in the list is visually focused by the value of aria-activedescendant: -
          -
        • DOM focus remains on the listbox element.
        • -
        • When a key that moves focus is pressed or an option is clicked, JavaScript changes the value of aria-activedescendant on the listbox element.
        • -
        • If the listbox element does not contain any options, aria-activedescendant does not have a value.
        • -
        -
      • -
      • - When Tab moves focus into either listbox: -
          -
        • If none of the options are selected, focus is set on the first option.
        • -
        • If one or more options are selected, focus is set on the first selected option.
        • -
        -
      • -
      • - Unlike example 1, more than one option may be selected at a time (have aria-selected="true"). -
          -
        • The multi-select capability is communicated to assistive technologies by setting aria-multiselectable="true" on the listbox element.
        • -
        • All option elements have a value set for aria-selected.
        • -
        • Selected options have aria-selected set to true and all others have it set to false.
        • -
        • Keys that move focus do not change the selected state of an option.
        • -
        -
      • -
      • Users can toggle the selected state of the focused option with Space or click.
      • -
      @@ -290,6 +239,18 @@

      Keyboard Support

      + + + +
      ul
        -
      • Set by the JavaScript when it displays and sets focus on the listbox; otherwise is not present.
      • -
      • Refers to the option in the listbox that is visually indicated as having keyboard focus.
      • +
      • When an option in the listbox is visually indicated as having keyboard focus, refers to that option.
      • +
      • Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the listbox element.
      • 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 ul element.
      • For more information about this focus management technique, see Managing Focus in Composites Using aria-activedescendant. diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index 646db5669f..4f792bee7b 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -244,9 +244,13 @@

        Role, Property, State, and Tabindex Attributes

      div
        -
      • Tells assistive technologies which of the options, if any, is visually indicated as having keyboard focus.
      • -
      • DOM focus remains on the ul element and the idref specified for aria-activedescendant refers to the li element that is visually styled as focused.
      • +
      • When an option in the listbox is visually indicated as having keyboard focus, refers to that option.
      • +
      • Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the listbox element.
      • When navigation keys, such as Down Arrow, are pressed, the JavaScript changes the value.
      • +
      • + For more information about this focus management technique, see + Managing Focus in Composites Using aria-activedescendant. +
      ul
        -
      • Applied to the element with the listbox role, tells assistive technologies which of the options, if any, is visually indicated as having keyboard focus.
      • -
      • DOM focus remains on the ul element and the idref specified for aria-activedescendant refers to the li element that is visually styled as focused.
      • +
      • When an option in the listbox is visually indicated as having keyboard focus, refers to that option.
      • +
      • Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the listbox element.
      • When navigation keys, such as Down Arrow, are pressed, the JavaScript changes the value.
      • When the listbox is empty, aria-activedescendant="".
      • +
      • + For more information about this focus management technique, see + Managing Focus in Composites Using aria-activedescendant. +
      ul
        -
      • Tells assistive technologies which of the options, if any, is visually indicated as having keyboard focus.
      • -
      • DOM focus remains on the ul element and the idref specified for aria-activedescendant refers to the li element that is visually styled as focused.
      • +
      • When an option in the listbox is visually indicated as having keyboard focus, refers to that option.
      • +
      • Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the listbox element.
      • When navigation keys, such as Down Arrow, are pressed, the JavaScript changes the value.
      • +
      • + For more information about this focus management technique, see + Managing Focus in Composites Using aria-activedescendant. +
      Tab +
        +
      • Moves focus into and out of the listbox.
      • +
      • + When the listbox receives focus, if none of the options are selected, the first option receives focus. + Otherwise, the first selected option receives focus. +
      • +
      +
      Down Arrow @@ -329,12 +290,11 @@

      Keyboard Support

      Multiple selection keys supported in example 2

      -
      -

      Note

      -

      The selection behavior demonstrated differs from the behavior provided by browsers for native HTML <select multiple> elements. +

      + NOTE: The selection behavior demonstrated differs from the behavior provided by browsers for native HTML <select multiple> elements. The HTML select element behavior is to alter selection with unmodified up/down arrow keys, requiring the use of modifier keys to select multiple options. - This example demonstrates the multiple selection interaction model recommended in the Keyboard Interaction section of the Listbox Pattern, which does not require the use of modifier keys.

      -
      + This example demonstrates the multiple selection interaction model recommended in the Keyboard Interaction section of the Listbox Pattern, which does not require the use of modifier keys. +

      From 2ded795033ad579317ed051a472b79390f4ba0a0 Mon Sep 17 00:00:00 2001 From: Matt King Date: Wed, 26 Apr 2023 10:42:37 -0700 Subject: [PATCH 33/52] Remove redundant notes section content --- .../listbox/examples/listbox-scrollable.html | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 69c19683f0..9d95b28513 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -163,38 +163,12 @@

      Example

      -
      -

      Notes

      -

      This listbox is scrollable; it has more options than its height can accommodate.

      -
        -
      • - Scrolling only works as expected if the listbox is the options' offsetParent. - The example uses position: relative on the listbox to that effect. -
      • -
      • - When an option is focused that isn't (fully) visible, the listbox's scroll position is updated: -
          -
        • If Up Arrow or Down Arrow is pressed, the previous or next option is scrolled into view.
        • -
        • If Home or End is pressed, the listbox scrolls all the way to the top or to the bottom.
        • -
        • If focusItem is called, the focused option will be scrolled to the top of the view if it was located above it or to the bottom if it was below it.
        • -
        • If the mouse is clicked on a partially visible option, it will be scrolled fully into view.
        • -
        -
      • -
      • When a fully visible option is focused in any way, no scrolling occurs.
      • -
      • - Normal scrolling through any scrolling mechanism (including Page Up and Page Down) works as expected. - The scroll position will jump as described for focusItem if a means other than a mouse click is used to change focus after scrolling. -
      • -
      -
      -

      Accessibility Features

      • - Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. - When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. - Managing aria-activedescendant visibility is essential to accessibility for people who use a browser's zoom feature to increase the size of content. + Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. + So, when a keyboard or pointer event changes which option is referenced by aria-activedescendant, the JavaScript scrolls that option so that it is fully within view.
      • To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes: From 80c37fdac3c4fc5943d7c693ff5c7f2b474a5487 Mon Sep 17 00:00:00 2001 From: Matt King Date: Wed, 26 Apr 2023 11:31:30 -0700 Subject: [PATCH 34/52] Editorial revision to accessibility features documentation of aria-activedescendant --- content/patterns/listbox/examples/listbox-collapsible.html | 5 ++--- content/patterns/listbox/examples/listbox-grouped.html | 5 ++--- content/patterns/listbox/examples/listbox-rearrangeable.html | 5 ++--- content/patterns/listbox/examples/listbox-scrollable.html | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-collapsible.html b/content/patterns/listbox/examples/listbox-collapsible.html index 4a479087b8..0c9a29c67b 100644 --- a/content/patterns/listbox/examples/listbox-collapsible.html +++ b/content/patterns/listbox/examples/listbox-collapsible.html @@ -130,9 +130,8 @@

        Accessibility Features

        Selected options have a check preceding the text label for the option.
      • - Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. - When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. - Managing aria-activedescendant visibility is essential to accessibility for people who use a browser's zoom feature to increase the size of content. + Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. + So, when a keyboard or pointer event changes the option referenced by aria-activedescendant, if the referenced option is not fully visible, the JavaScript scrolls the listbox to position the option in view.
      • To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes: diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index 4f792bee7b..001d403f6e 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -150,9 +150,8 @@

        Accessibility Features

        Selected options have a check preceding the text label for the option.
      • - Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. - When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. - Managing aria-activedescendant visibility is essential to accessibility for people who use a browser's zoom feature to increase the size of content. + Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. + So, when a keyboard or pointer event changes the option referenced by aria-activedescendant, if the referenced option is not fully visible, the JavaScript scrolls the listbox to position the option in view.
      • To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes: diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 45f51cd4a1..98c6ba5295 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -205,9 +205,8 @@

        Accessibility Features

      • In example 1, since there are four action buttons, a toolbar widget is used to group all the action buttons into a single tab stop.
      • Live regions provide confirmation of completed actions.
      • - Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. - When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. - Managing aria-activedescendant visibility is essential to accessibility for people who use a browser's zoom feature to increase the size of content. + Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. + So, when a keyboard or pointer event changes the option referenced by aria-activedescendant, if the referenced option is not fully visible, the JavaScript scrolls the listbox to position the option in view.
      • To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes: diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 9d95b28513..88c275f36e 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -168,7 +168,7 @@

        Accessibility Features

        • Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. - So, when a keyboard or pointer event changes which option is referenced by aria-activedescendant, the JavaScript scrolls that option so that it is fully within view. + So, when a keyboard or pointer event changes the option referenced by aria-activedescendant, if the referenced option is not fully visible, the JavaScript scrolls the listbox to position the option in view.
        • To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes: From 6d4508a5dd5d19750f46bd992fbb291165179967 Mon Sep 17 00:00:00 2001 From: Matt King Date: Wed, 26 Apr 2023 11:36:33 -0700 Subject: [PATCH 35/52] Change quote entities to quot characters --- .../patterns/listbox/examples/listbox-scrollable.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 88c275f36e..cedad55d74 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -244,19 +244,19 @@

          Role, Property, State, and Tabindex Attributes

      - + - + - + - + - + + + + + + + + + diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index cedad55d74..d9fc39c35c 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -200,6 +200,10 @@

      Keyboard Support

      + + + + From 5e69a63d8a443b036f0ddc600319ba6f3648e4a2 Mon Sep 17 00:00:00 2001 From: Matt King Date: Wed, 26 Apr 2023 12:48:51 -0700 Subject: [PATCH 37/52] Update accessibility features docs, remove utils.js link --- .../listbox/examples/listbox-grouped.html | 43 +++---------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index eaf79386d4..3154bb7c11 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -112,43 +112,12 @@

      Example

      -

      Notes

      -

      This listbox is scrollable; it has more options than its height can accommodate.

      -
        -
      1. - Scrolling only works as expected if the listbox is the options' offsetParent. - The example uses position: relative on the listbox to that effect. -
      2. -
      3. - When an option is focused that isn't (fully) visible, the listbox's scroll position is updated: -
          -
        1. If Up Arrow or Down Arrow is pressed, the previous or next option is scrolled into view.
        2. -
        3. If Home or End is pressed, the listbox scrolls all the way to the top or to the bottom.
        4. -
        5. If focusItem is called, the focused option will be scrolled to the top of the view if it was located above it or to the bottom if it was below it.
        6. -
        7. If the mouse is clicked on a partially visible option, it will be scrolled fully into view.
        8. -
        -
      4. -
      5. When a fully visible option is focused in any way, no scrolling occurs.
      6. -
      7. - Normal scrolling through any scrolling mechanism (including Page Up and Page Down) works as expected. - The scroll position will jump as described for focusItem if a means other than a mouse click is used to change focus after scrolling. -
      8. -
      -
      -

      Accessibility Features

      -
        -
      1. - The listbox receives accessibility focus via aria-activedescendant. - This enables users to perceive the presence of the options, and enables assistive technology users to comprehend the size of the list of options.
      2. -
      3. - Navigating the list of options does not set the selection of an option. - This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. - The value is set when users press Space or Enter. - Selected options have a check preceding the text label for the option. -
      4. +
        +

        Accessibility Features

        +
        • Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. So, when a keyboard or pointer event changes the option referenced by aria-activedescendant, if the referenced option is not fully visible, the JavaScript scrolls the listbox to position the option in view. @@ -156,11 +125,11 @@

          Accessibility Features

        • To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes:
            -
          • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the combobox or list.
          • +
          • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the list.
          • To make it easier to distinguish the selected listbox option from other options, selection creates a 2 pixel border above and below the option.
        • -
      +
      @@ -308,7 +277,7 @@

      Javascript and CSS Source Code

    • Javascript: - listbox.js, listbox-scrollable.js, utils.js + listbox.js, listbox-scrollable.js
    • From b6e314143c40d1189af1783aa969154a59417a1a Mon Sep 17 00:00:00 2001 From: Matt King Date: Wed, 26 Apr 2023 13:05:10 -0700 Subject: [PATCH 38/52] Change quote entities to quote characters --- .../patterns/listbox/examples/listbox-grouped.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index 3154bb7c11..aaa8f134c7 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -200,19 +200,19 @@

      Role, Property, State, and Tabindex Attributes

      - + - + - + - + @@ -246,7 +246,7 @@

      Role, Property, State, and Tabindex Attributes

      - + - + - + - + - + - + - + - + - + @@ -342,7 +312,7 @@

      Javascript and CSS Source Code

    • Javascript: - listbox.js, listbox-collapsible.js, utils.js + listbox.js, listbox-collapsible.js
    • From 6983e65401858658f6db47545171dbd006f5f3a3 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 26 Apr 2023 16:28:33 -0500 Subject: [PATCH 41/52] added aria-hidden to button character entities --- .../patterns/listbox/examples/css/listbox.css | 18 +-- .../examples/listbox-rearrangeable.html | 119 +++++++++++++----- 2 files changed, 98 insertions(+), 39 deletions(-) diff --git a/content/patterns/listbox/examples/css/listbox.css b/content/patterns/listbox/examples/css/listbox.css index a410be2891..5b4ed86bd0 100644 --- a/content/patterns/listbox/examples/css/listbox.css +++ b/content/patterns/listbox/examples/css/listbox.css @@ -59,7 +59,15 @@ outline: 2px solid currentcolor; } -[role="option"][aria-selected="true"] span.checkmark::before { +.move-right-btn span.icon::after { + content: " →"; +} + +.move-left-btn span.icon::before { + content: "← "; +} + +[role="option"][aria-selected="true"] span.icon::before { position: absolute; left: 0.5em; content: "✓"; @@ -127,14 +135,6 @@ button[aria-disabled="true"] { opacity: 0.5; } -.move-right-btn::after { - content: " →"; -} - -.move-left-btn::before { - content: "← "; -} - .annotate { color: #366ed4; font-style: italic; diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 970c51d052..50cd36274e 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -58,49 +58,84 @@

      Example 1: Single-Select Listbox

      Important Features: -
        +
        • - + Proximity of public K-12 schools
        • - + Proximity of child-friendly parks
        • - + Proximity of grocery shopping
        • - + Proximity of fast food
        • - + Proximity of fine dining
        • - + Neighborhood walkability
        • - + Availability of public transit
        • - + Proximity of hospital and medical services
        • - + Level of traffic noise
        • - + Access to major highways
      Unimportant Features: -
        - +
          +
          Last change:
          @@ -141,54 +176,78 @@

          Example 2: Multi-Select Listbox

          Available upgrades: -
            +
            • Leather seats
            • - + Front seat warmers
            • - + Rear bucket seats
            • - + Rear seat warmers
            • - + Front sun roof
            • - + Rear sun roof
            • - + Cloaking capability
            • - + Food synthesizer
            • - + Advanced waste recycling system
            • - + Turbo vertical take-off capability
            - +
          Upgrades you have chosen: -
            - +
              +
            +
            Last change:
            @@ -476,7 +535,7 @@

            Role, Property, State, and Tabindex Attributes

            From a2a94606a32027446cdc0b7504e7b95a5b430404 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 26 Apr 2023 16:34:02 -0500 Subject: [PATCH 42/52] fixed regresion test: --- test/tests/listbox_rearrangeable.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tests/listbox_rearrangeable.js b/test/tests/listbox_rearrangeable.js index 892881ea76..aaa22ce858 100644 --- a/test/tests/listbox_rearrangeable.js +++ b/test/tests/listbox_rearrangeable.js @@ -15,7 +15,7 @@ const ex = { listboxSelector: '#ex1 [role="listbox"]', importantSelector: '#ex1 [role="listbox"]#ss_imp_list', optionSelector: '#ex1 [role="option"]', - spanSelector: '#ex1 [role="option"] span.checkmark', + spanSelector: '#ex1 span.icon', numOptions: 10, firstOptionSelector: '#ex1 #ss_opt1', lastOptionSelector: '#ex1 #ss_opt10', @@ -24,7 +24,7 @@ const ex = { listboxSelector: '#ex2 [role="listbox"]', availableSelector: '#ex2 [role="listbox"]#ms_imp_list', optionSelector: '#ex2 [role="option"]', - spanSelector: '#ex2 [role="option"] span.checkmark', + spanSelector: '#ex2 span.icon', numOptions: 10, firstOptionSelector: '#ex2 #ms_opt1', lastOptionSelector: '#ex2 #ms_opt10', From 4f4c176731d128d563914b8e85be56101ec2d943 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 26 Apr 2023 16:40:11 -0500 Subject: [PATCH 43/52] fixed bug --- content/patterns/listbox/examples/listbox-rearrangeable.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 69200149ef..6285198cfc 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -159,7 +159,7 @@

            Example 2: Multi-Select Listbox

            aria-labelledby="ms_av_l" aria-multiselectable="true">
          • - + Leather seats
          • From 35d6a04e8bd4b934a4cb2f532ecd51b5c979d16a Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 26 Apr 2023 16:41:23 -0500 Subject: [PATCH 44/52] updated documentation --- test/tests/listbox_rearrangeable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests/listbox_rearrangeable.js b/test/tests/listbox_rearrangeable.js index aaa22ce858..35ffebc165 100644 --- a/test/tests/listbox_rearrangeable.js +++ b/test/tests/listbox_rearrangeable.js @@ -125,7 +125,7 @@ ariaTest( ); ariaTest( - 'aria-hidden="true" on li > span elements', + 'aria-hidden="true" on span[class=icon] elements', exampleFile, 'span-aria-hidden', async (t) => { From f4d2e5cd7100fa1cc25055a16cdffee0929dbcf2 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Thu, 27 Apr 2023 10:41:40 -0500 Subject: [PATCH 45/52] fixed keyboard bugs in toolbar --- .../patterns/listbox/examples/js/toolbar.js | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index caf43df894..1c21a7480c 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -43,33 +43,50 @@ aria.Toolbar = class Toolbar { /** * @description - * Handle various keyboard controls; LEFT/RIGHT will shift focus; DOWN - * activates a menu button if it is the focused item. + * Handle various keyboard commands to move focus: + * LEFT: Previous button + * RIGHT: Next button + * HOME: First button + * END: Last button * @param evt * The keydown event object */ checkFocusChange(evt) { let nextIndex, nextItem; - switch (evt.key) { - case 'ArrowLeft': - case 'ArrowRight': - nextIndex = Array.prototype.indexOf.call(this.items, this.selectedItem); - nextIndex = evt.key === 'ArrowLeft' ? nextIndex - 1 : nextIndex + 1; - nextIndex = Math.max(Math.min(nextIndex, this.items.length - 1), 0); + // Do not move focus if any modifier keys pressed + if (!evt.shiftKey && !evt.metaKey && !evt.altKey && !evt.ctrlKey) { + switch (evt.key) { + case 'ArrowLeft': + case 'ArrowRight': + nextIndex = Array.prototype.indexOf.call( + this.items, + this.selectedItem + ); + nextIndex = evt.key === 'ArrowLeft' ? nextIndex - 1 : nextIndex + 1; + nextIndex = Math.max(Math.min(nextIndex, this.items.length - 1), 0); - nextItem = this.items[nextIndex]; + nextItem = this.items[nextIndex]; + break; + + case 'End': + nextItem = this.items[this.items.length - 1]; + break; + + case 'Home': + nextItem = this.items[0]; + break; + + default: + break; + } + + if (nextItem) { this.selectItem(nextItem); this.focusItem(nextItem); - break; - - case 'Down': - // if selected item is menu button, pressing DOWN should act like a click - if (this.selectedItem.classList.contains('menu-button')) { - evt.preventDefault(); - this.selectedItem.click(); - } - break; + evt.stopPropagation(); + evt.preventDefault(); + } } } From accf5e2fc663887c40b8ff8a71090a6c7be149e4 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Thu, 27 Apr 2023 14:02:17 -0500 Subject: [PATCH 46/52] Update content/patterns/listbox/examples/listbox-rearrangeable.html Looks good to me. Co-authored-by: Howard Edwards --- content/patterns/listbox/examples/listbox-rearrangeable.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index 6285198cfc..be00d10df2 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -264,7 +264,7 @@

            Accessibility Features

          • In example 1, since there are four action buttons, a toolbar widget is used to group all the action buttons into a single tab stop.
          • Live regions provide confirmation of completed actions.
          • - Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. + Because this listbox implementation is scrollable and manages which option is focused by using aria-activedescendant, the JavaScript must ensure the focused option is visible. So, when a keyboard or pointer event changes the option referenced by aria-activedescendant, if the referenced option is not fully visible, the JavaScript scrolls the listbox to position the option in view.
          • From 4e7a6814d49ee01df0f9f5cae3076e79c0d36700 Mon Sep 17 00:00:00 2001 From: Matt King Date: Thu, 27 Apr 2023 14:44:40 -0700 Subject: [PATCH 47/52] Fix tense of 'focus' as suggested by Howard --- content/patterns/listbox/examples/listbox-collapsible.html | 2 +- content/patterns/listbox/examples/listbox-grouped.html | 2 +- content/patterns/listbox/examples/listbox-scrollable.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-collapsible.html b/content/patterns/listbox/examples/listbox-collapsible.html index cd033302e9..dbf236f252 100644 --- a/content/patterns/listbox/examples/listbox-collapsible.html +++ b/content/patterns/listbox/examples/listbox-collapsible.html @@ -103,7 +103,7 @@

            Example

            Accessibility Features

            • - Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. + Because this listbox implementation is scrollable and manages which option is focused by using aria-activedescendant, the JavaScript must ensure the focused option is visible. So, when a keyboard or pointer event changes the option referenced by aria-activedescendant, if the referenced option is not fully visible, the JavaScript scrolls the listbox to position the option in view.
            • diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index aaa8f134c7..3651b167e9 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -119,7 +119,7 @@

              Example

              Accessibility Features

              • - Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. + Because this listbox implementation is scrollable and manages which option is focused by using aria-activedescendant, the JavaScript must ensure the focused option is visible. So, when a keyboard or pointer event changes the option referenced by aria-activedescendant, if the referenced option is not fully visible, the JavaScript scrolls the listbox to position the option in view.
              • diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index d9fc39c35c..49a4cd6620 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -167,7 +167,7 @@

                Example

                Accessibility Features

                • - Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. + Because this listbox implementation is scrollable and manages which option is focused by using aria-activedescendant, the JavaScript must ensure the focused option is visible. So, when a keyboard or pointer event changes the option referenced by aria-activedescendant, if the referenced option is not fully visible, the JavaScript scrolls the listbox to position the option in view.
                • From da7a5e4c99635f0060479b846a9ea7e008880704 Mon Sep 17 00:00:00 2001 From: Matt King Date: Thu, 27 Apr 2023 18:07:24 -0700 Subject: [PATCH 48/52] =?UTF-8?q?Add=C2=A0keyboard=C2=A0documentation?= =?UTF-8?q?=C2=A0for=C2=A0printable=C2=A0characters=C2=A0that=C2=A0perform?= =?UTF-8?q?=C2=A0type-ahead?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/patterns/listbox/examples/listbox-grouped.html | 9 +++++++++ .../patterns/listbox/examples/listbox-rearrangeable.html | 9 +++++++++ .../patterns/listbox/examples/listbox-scrollable.html | 9 +++++++++ 3 files changed, 27 insertions(+) diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index 3651b167e9..b6d45a9cbe 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -172,6 +172,15 @@

                  Keyboard Support

          • + + + +
            aria-labelledby="ID_REF"aria-labelledby="ID_REF" ul Refers to the element containing the listbox label.
            tabindex="0"tabindex="0" ul Includes the listbox in the page tab sequence.
            aria-activedescendant="ID_REF"aria-activedescendant="ID_REF" ul
              @@ -278,7 +278,7 @@

              Role, Property, State, and Tabindex Attributes

            aria-selected="true"aria-selected="true" li
              @@ -290,7 +290,7 @@

              Role, Property, State, and Tabindex Attributes

            aria-hidden="true"aria-hidden="true" span Removes the character entity used for the check mark icon from the accessibility tree to prevent it from being included in the accessible name of the option. From d6dd9906586b0c3f937e7d5d28d41b2d8c5250fb Mon Sep 17 00:00:00 2001 From: Matt King Date: Wed, 26 Apr 2023 11:53:12 -0700 Subject: [PATCH 36/52] Add tab key documentation --- .../listbox/examples/listbox-collapsible.html | 12 ++++++++++++ .../patterns/listbox/examples/listbox-grouped.html | 4 ++++ .../listbox/examples/listbox-scrollable.html | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/content/patterns/listbox/examples/listbox-collapsible.html b/content/patterns/listbox/examples/listbox-collapsible.html index 0c9a29c67b..139cf05189 100644 --- a/content/patterns/listbox/examples/listbox-collapsible.html +++ b/content/patterns/listbox/examples/listbox-collapsible.html @@ -163,6 +163,18 @@

            Keyboard Support

            Tab +
              +
            • Moves focus into and out of the listbox.
            • +
            • + When the listbox receives focus, if none of the options are selected, the first option receives focus. + Otherwise, the selected option receives focus. +
            • +
            +
            Enter diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index 001d403f6e..eaf79386d4 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -183,6 +183,10 @@

            Keyboard Support

            TabMoves focus into and out of the listbox.
            Down Arrow Moves focus to and selects the next option.
            TabMoves focus into and out of the listbox.
            Down Arrow Moves focus to and selects the next option.
            aria-labelledby="ID_REF"aria-labelledby="ID_REF" div Refers to the element containing the listbox label.
            tabindex="0"tabindex="0" div Includes the listbox in the page tab sequence.
            aria-activedescendant="ID_REF"aria-activedescendant="ID_REF" div
              @@ -234,7 +234,7 @@

              Role, Property, State, and Tabindex Attributes

            aria-labelledby="ID_REF"aria-labelledby="ID_REF" ul Refers to the element containing the option group label.
            aria-selected="true"aria-selected="true" li
              @@ -258,7 +258,7 @@

              Role, Property, State, and Tabindex Attributes

            aria-hidden="true"aria-hidden="true" span Removes the character entity used for the check mark icon from the accessibility tree to prevent it from being included in the accessible name of the option. From 6ac7780beedae6a7845c3180e2c36465a54aafd3 Mon Sep 17 00:00:00 2001 From: Matt King Date: Wed, 26 Apr 2023 13:09:41 -0700 Subject: [PATCH 39/52] Change quote entities to quote characters --- .../listbox/examples/listbox-collapsible.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-collapsible.html b/content/patterns/listbox/examples/listbox-collapsible.html index 139cf05189..75a962d1c1 100644 --- a/content/patterns/listbox/examples/listbox-collapsible.html +++ b/content/patterns/listbox/examples/listbox-collapsible.html @@ -38,7 +38,7 @@

            Deprecation Warning

            - The following example implementation of the Listbox Pattern demonstrates a collapsible single-select listbox widget that is functionally similar to an HTML select input with the attribute size="1". + The following example implementation of the Listbox Pattern demonstrates a collapsible single-select listbox widget that is functionally similar to an HTML select input with the attribute size="1". The widget consists of a button that triggers the display of a listbox. In its default state, the widget is collapsed (the listbox is not visible) and the button label shows the currently selected option from the listbox. When the button is activated, the listbox is displayed and the current option is focused and selected. @@ -245,7 +245,7 @@

            Role, Property, State, and Tabindex Attributes

            aria-labelledby="ID_REF1 ID_REF2"aria-labelledby="ID_REF1 ID_REF2" button
              @@ -257,13 +257,13 @@

              Role, Property, State, and Tabindex Attributes

            aria-haspopup="listbox"aria-haspopup="listbox" button Indicates that activating the button displays a listbox.
            aria-expanded="true"aria-expanded="true" button
              @@ -280,13 +280,13 @@

              Role, Property, State, and Tabindex Attributes

            aria-labelledby="ID_REF"aria-labelledby="ID_REF" ul Refers to the element containing the listbox label.
            tabindex="-1"tabindex="-1" ul
              @@ -297,7 +297,7 @@

              Role, Property, State, and Tabindex Attributes

            aria-activedescendant="ID_REF"aria-activedescendant="ID_REF" ul
              @@ -319,7 +319,7 @@

              Role, Property, State, and Tabindex Attributes

            aria-selected="true"aria-selected="true" li
              From ff7ae0bda683f7f7e793df4cca8611b7f96b6027 Mon Sep 17 00:00:00 2001 From: Matt King Date: Wed, 26 Apr 2023 13:20:03 -0700 Subject: [PATCH 40/52] Update accessibility features documentation and remove link to utils.js --- .../listbox/examples/listbox-collapsible.html | 44 +++---------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/content/patterns/listbox/examples/listbox-collapsible.html b/content/patterns/listbox/examples/listbox-collapsible.html index 75a962d1c1..cd033302e9 100644 --- a/content/patterns/listbox/examples/listbox-collapsible.html +++ b/content/patterns/listbox/examples/listbox-collapsible.html @@ -97,38 +97,11 @@

              Example

              -

              Notes

              -

              This listbox is scrollable; it has more options than its height can accommodate.

              -
                -
              1. - Scrolling only works as expected if the listbox is the options' offsetParent. - The example uses position: relative on the listbox to that effect. -
              2. -
              3. - When an option is focused that isn't (fully) visible, the listbox's scroll position is updated: -
                  -
                1. If Up Arrow or Down Arrow is pressed, the previous or next option is scrolled into view.
                2. -
                3. If Home or End is pressed, the listbox scrolls all the way to the top or to the bottom.
                4. -
                5. If focusItem is called, the focused option will be scrolled to the top of the view if it was located above it or to the bottom if it was below it.
                6. -
                7. If the mouse is clicked on a partially visible option, it will be scrolled fully into view.
                8. -
                -
              4. -
              5. When a fully visible option is focused in any way, no scrolling occurs.
              6. -
              -
              -

              Accessibility Features

              -
                -
              1. - The listbox receives accessibility focus via aria-activedescendant. - This enables users to perceive the presence of the options, and enables assistive technology users to comprehend the size of the list of options.
              2. -
              3. - Navigating the list of options does not set the selection of an option. - This gives screen reader users, who need to navigate among the options to perceive them, the ability to explore options without changing the currently selected options. - The value is set when users press Space or Enter. - Selected options have a check preceding the text label for the option. -
              4. +
                +

                Accessibility Features

                +
                • Because this listbox implementation is scrollable and manages which option is focus by using aria-activedescendant, the JavaScript must ensure the focused option is visible. So, when a keyboard or pointer event changes the option referenced by aria-activedescendant, if the referenced option is not fully visible, the JavaScript scrolls the listbox to position the option in view. @@ -136,11 +109,11 @@

                  Accessibility Features

                • To enhance perceivability when operating the listbox, visual keyboard focus and hover are styled using the CSS :hover and :focus pseudo-classes:
                    -
                  • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the combobox or list.
                  • +
                  • To help people with visual impairments identify the listbox as an interactive element, the cursor is changed to a pointer when hovering over the list.
                  • To make it easier to distinguish the selected listbox option from other options, selection creates a 2 pixel border above and below the option.
                • -
              +
            @@ -168,10 +141,7 @@

            Keyboard Support

            • Moves focus into and out of the listbox.
            • -
            • - When the listbox receives focus, if none of the options are selected, the first option receives focus. - Otherwise, the selected option receives focus. -
            • +
            • If the listbox is expanded, selects the focused option, collapses the listbox, and moves focus out of the listbox.
            aria-hidden="true" span - Removes the character entity used for the check mark icon from the accessibility tree to prevent it from being included in the accessible name of the option. + Removes the character entities used for the check mark, left arrow and right arrow from the accessibility tree to prevent them from being included in the accessible name of an option or button.
            End Moves focus to and selects the last option.
            Printable Characters +
              +
            • Type a character: focus moves to the next item with a name that starts with the typed character.
            • +
            • Type multiple characters in rapid succession: focus moves to the next item with a name that starts with the string of characters typed.
            • +
            +
            diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index be00d10df2..e8c02d0ed0 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -345,6 +345,15 @@

            Keyboard Support

            Printable Characters +
              +
            • Type a character: focus moves to the next item with a name that starts with the typed character.
            • +
            • Type multiple characters in rapid succession: focus moves to the next item with a name that starts with the string of characters typed.
            • +
            +

            Multiple selection keys supported in example 2

            diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 49a4cd6620..9498a23faa 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -220,6 +220,15 @@

            Keyboard Support

            End Moves focus to and selects the last option. + + Printable Characters + +
              +
            • Type a character: focus moves to the next item with a name that starts with the typed character.
            • +
            • Type multiple characters in rapid succession: focus moves to the next item with a name that starts with the string of characters typed.
            • +
            + +
            From 38323e36812ce5b9aa88fe6a7c80c9167165d2c3 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 11 Jul 2023 13:37:52 -0500 Subject: [PATCH 49/52] fixes checkbox issue --- .../patterns/listbox/examples/css/listbox.css | 6 +-- .../examples/listbox-rearrangeable.html | 48 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/content/patterns/listbox/examples/css/listbox.css b/content/patterns/listbox/examples/css/listbox.css index 5b4ed86bd0..5ecb8d129d 100644 --- a/content/patterns/listbox/examples/css/listbox.css +++ b/content/patterns/listbox/examples/css/listbox.css @@ -59,15 +59,15 @@ outline: 2px solid currentcolor; } -.move-right-btn span.icon::after { +.move-right-btn span.checkmark::after { content: " →"; } -.move-left-btn span.icon::before { +.move-left-btn span.checkmark::before { content: "← "; } -[role="option"][aria-selected="true"] span.icon::before { +[role="option"][aria-selected="true"] span.checkmark::before { position: absolute; left: 0.5em; content: "✓"; diff --git a/content/patterns/listbox/examples/listbox-rearrangeable.html b/content/patterns/listbox/examples/listbox-rearrangeable.html index e8c02d0ed0..0da76caa4b 100644 --- a/content/patterns/listbox/examples/listbox-rearrangeable.html +++ b/content/patterns/listbox/examples/listbox-rearrangeable.html @@ -63,35 +63,35 @@

            Example 1: Single-Select Listbox

            role="listbox" aria-labelledby="ss_imp_l">
          • - + Proximity of public K-12 schools
          • - + Proximity of child-friendly parks
          • - + Proximity of grocery shopping
          • - + Proximity of fast food
          • - + Proximity of fine dining
          • - + Neighborhood walkability
          • - + Availability of public transit
          • - + Proximity of hospital and medical services
          • - + Level of traffic noise
          • - + Access to major highways
          @@ -133,7 +133,7 @@

          Example 1: Single-Select Listbox

          class="move-left-btn" aria-keyshortcuts="Alt+ArrowLeft Enter" aria-disabled="true"> - + Important @@ -159,43 +159,43 @@

          Example 2: Multi-Select Listbox

          aria-labelledby="ms_av_l" aria-multiselectable="true">
        • - + Leather seats
        • - + Front seat warmers
        • - + Rear bucket seats
        • - + Rear seat warmers
        • - + Front sun roof
        • - + Rear sun roof
        • - + Cloaking capability
        • - + Food synthesizer
        • - + Advanced waste recycling system
        • - + Turbo vertical take-off capability
        • @@ -204,7 +204,7 @@

          Example 2: Multi-Select Listbox

          class="move-right-btn" aria-keyshortcuts="Alt+ArrowRight Enter" aria-disabled="true"> - + Add @@ -222,7 +222,7 @@

          Example 2: Multi-Select Listbox

          class="move-left-btn" aria-keyshortcuts="Alt+ArrowLeft Delete" aria-disabled="true"> - + Remove From 945222842caffa46c7079b5d2aadcfd03b61baa6 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 26 Jul 2023 16:01:04 -0500 Subject: [PATCH 50/52] fixed regression bug --- test/tests/listbox_rearrangeable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests/listbox_rearrangeable.js b/test/tests/listbox_rearrangeable.js index 35ffebc165..c5f4fb03ef 100644 --- a/test/tests/listbox_rearrangeable.js +++ b/test/tests/listbox_rearrangeable.js @@ -24,7 +24,7 @@ const ex = { listboxSelector: '#ex2 [role="listbox"]', availableSelector: '#ex2 [role="listbox"]#ms_imp_list', optionSelector: '#ex2 [role="option"]', - spanSelector: '#ex2 span.icon', + spanSelector: '#ex2 span.checkmark', numOptions: 10, firstOptionSelector: '#ex2 #ms_opt1', lastOptionSelector: '#ex2 #ms_opt10', From d113845ac7e9976b58230427344e4e5b79376540 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 26 Jul 2023 16:03:58 -0500 Subject: [PATCH 51/52] fixed regression bug --- test/tests/listbox_rearrangeable.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tests/listbox_rearrangeable.js b/test/tests/listbox_rearrangeable.js index c5f4fb03ef..4f46856fd1 100644 --- a/test/tests/listbox_rearrangeable.js +++ b/test/tests/listbox_rearrangeable.js @@ -15,7 +15,7 @@ const ex = { listboxSelector: '#ex1 [role="listbox"]', importantSelector: '#ex1 [role="listbox"]#ss_imp_list', optionSelector: '#ex1 [role="option"]', - spanSelector: '#ex1 span.icon', + spanSelector: '#ex1 span.checkmark', numOptions: 10, firstOptionSelector: '#ex1 #ss_opt1', lastOptionSelector: '#ex1 #ss_opt10', @@ -125,7 +125,7 @@ ariaTest( ); ariaTest( - 'aria-hidden="true" on span[class=icon] elements', + 'aria-hidden="true" on span[class=checkmark] elements', exampleFile, 'span-aria-hidden', async (t) => { From 31759c24fe719de323191c51225f56caaa844e2e Mon Sep 17 00:00:00 2001 From: Matt King Date: Mon, 18 Sep 2023 16:48:31 -0700 Subject: [PATCH 52/52] remove superfluous default from switch per suggestion from Mike --- content/patterns/listbox/examples/js/toolbar.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index 1c21a7480c..a1fb9f891b 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -76,9 +76,6 @@ aria.Toolbar = class Toolbar { case 'Home': nextItem = this.items[0]; break; - - default: - break; } if (nextItem) {