Commit cbc7f89b authored by John Emau's avatar John Emau Committed by Commit Bot

DevTools: Make UI.SoftDropdown follow ARIA Action Menu Button pattern

This change will make it easier for screen reader users to select the JavaScript Context
in the Console and the audio context in the Web Audio tool by moving focus to the menu on
activation and reading the selected position of the item.

Summary of changes:
- Decorated markup with appropriate ARIA properties.
- Moved focus from button to menu following active-descendant pattern [1].
- Dropdown selection is now only finalized by clicking an item, or pressing Enter, Space, or Tab.

Scope of impact:
The SoftDropDown is currently used as the JavaScript context selector in
the Console tool and audio context in the Web Audio tool.

[1] https://w3c.github.io/aria-practices/examples/menu-button/menu-button-actions-active-descendant.html
[2] https://cs.chromium.org/search/?q=UI.SoftDropDown+-f:out+f:.js+f:front_end&sq=package:chromium&type=cs

Screenshot: https://i.imgur.com/jIpsRYP.png
Gif: https://i.imgur.com/V4FoqKn.gifv

Bug: 963183
Change-Id: Iaf44f50639626fe22ef55862ae3a8b93962a4efe
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1626643
Commit-Queue: John Emau <johnemau@microsoft.com>
Reviewed-by: default avatarErik Luo <luoe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#686682}
parent e553e089
......@@ -22,6 +22,7 @@ UI.SoftDropDown = class {
this._titleElement = this.element.createChild('span', 'title');
const dropdownArrowIcon = UI.Icon.create('smallicon-triangle-down');
this.element.appendChild(dropdownArrowIcon);
UI.ARIAUtils.setExpanded(this.element, false);
this._glassPane = new UI.GlassPane();
this._glassPane.setMarginBehavior(UI.GlassPane.MarginBehavior.NoMargin);
......@@ -33,7 +34,9 @@ UI.SoftDropDown = class {
this._rowHeight = 36;
this._width = 315;
UI.createShadowRootWithCoreStyles(this._glassPane.contentElement, 'ui/softDropDown.css')
.createChild('div', 'list-container') // issue #972755
.appendChild(this._list.element);
UI.ARIAUtils.markAsMenu(this._list.element);
this._listWasShowing200msAgo = false;
this.element.addEventListener('mousedown', event => {
......@@ -42,8 +45,9 @@ UI.SoftDropDown = class {
else if (!this.element.disabled)
this._show(event);
}, false);
this.element.addEventListener('keydown', this._onKeyDown.bind(this), false);
this.element.addEventListener('focusout', this._hide.bind(this), false);
this.element.addEventListener('keydown', this._onKeyDownButton.bind(this), false);
this._list.element.addEventListener('keydown', this._onKeyDownList.bind(this), false);
this._list.element.addEventListener('focusout', this._hide.bind(this), false);
this._list.element.addEventListener('mousedown', event => event.consume(true), false);
this._list.element.addEventListener('mouseup', event => {
if (event.target === this._list.element)
......@@ -65,10 +69,11 @@ UI.SoftDropDown = class {
return;
this._glassPane.setContentAnchorBox(this.element.boxInWindow());
this._glassPane.show(/** @type {!Document} **/ (this.element.ownerDocument));
this._list.element.focus();
UI.ARIAUtils.setExpanded(this.element, true);
this._updateGlasspaneSize();
if (this._selectedItem)
this._list.selectItem(this._selectedItem);
this.element.focus();
event.consume(true);
setTimeout(() => this._listWasShowing200msAgo = true, 200);
}
......@@ -86,28 +91,51 @@ UI.SoftDropDown = class {
setTimeout(() => this._listWasShowing200msAgo = false, 200);
this._glassPane.hide();
this._list.selectItem(null);
UI.ARIAUtils.setExpanded(this.element, false);
this.element.focus();
event.consume(true);
}
/**
* @param {!Event} event
*/
_onKeyDown(event) {
_onKeyDownButton(event) {
let handled = false;
switch (event.key) {
case 'ArrowLeft':
case 'ArrowUp':
handled = this._list.selectPreviousItem(false, false);
this._show(event);
this._list.selectItemNextPage();
handled = true;
break;
case 'ArrowRight':
case 'ArrowDown':
handled = this._list.selectNextItem(false, false);
this._show(event);
this._list.selectItemPreviousPage();
handled = true;
break;
case 'PageUp':
handled = this._list.selectItemPreviousPage(false);
case 'Enter':
case ' ':
this._show(event);
handled = true;
break;
case 'PageDown':
handled = this._list.selectItemNextPage(false);
default:
break;
}
if (handled)
event.consume(true);
}
/**
* @param {!Event} event
*/
_onKeyDownList(event) {
let handled = false;
switch (event.key) {
case 'ArrowLeft':
handled = this._list.selectPreviousItem(false, false);
break;
case 'ArrowRight':
handled = this._list.selectNextItem(false, false);
break;
case 'Home':
for (let i = 0; i < this._model.length; i++) {
......@@ -129,23 +157,14 @@ UI.SoftDropDown = class {
break;
case 'Escape':
this._hide(event);
handled = true;
break;
case 'Tab':
if (!this._glassPane.isShowing())
break;
this._selectHighlightedItem();
this._hide(event);
break;
case 'Enter':
if (!this._glassPane.isShowing()) {
this._show(event);
break;
}
case ' ':
this._selectHighlightedItem();
this._hide(event);
break;
case ' ':
this._show(event);
handled = true;
break;
default:
if (event.key.length === 1) {
......@@ -163,10 +182,8 @@ UI.SoftDropDown = class {
break;
}
if (handled) {
if (handled)
event.consume(true);
this._selectHighlightedItem();
}
}
/**
......@@ -231,6 +248,7 @@ UI.SoftDropDown = class {
element.classList.toggle('disabled', !this._delegate.isItemSelectable(item));
element.classList.toggle('highlighted', this._list.selectedItem() === item);
UI.ARIAUtils.markAsMenuItem(element);
element.appendChild(this._delegate.createElementForItem(item));
return element;
......@@ -266,6 +284,8 @@ UI.SoftDropDown = class {
fromElement.classList.remove('highlighted');
if (toElement)
toElement.classList.add('highlighted');
UI.ARIAUtils.setActiveDescendant(this._list.element, toElement);
this._delegate.highlightedItemChanged(
from, to, fromElement && fromElement.firstElementChild, toElement && toElement.firstElementChild);
}
......
......@@ -20,3 +20,7 @@
color: var(--selection-fg-color);
background-color: var(--selection-bg-color);
}
.list-container {
width: 100%;
}
......@@ -3,35 +3,24 @@ Showing drop down
Item highlighted: fifth
ArrowDown
Item highlighted: sixth
Item selected: sixth
ArrowDown
Item highlighted: seventh
Item selected: seventh
ArrowDown
Item highlighted: eighth
Item selected: eighth
ArrowUp
Item highlighted: seventh
Item selected: seventh
ArrowUp
Item highlighted: sixth
Item selected: sixth
ArrowUp
Item highlighted: fifth
Item selected: fifth
ArrowDown
Item highlighted: sixth
Item selected: sixth
ArrowDown
Item highlighted: seventh
Item selected: seventh
f
Item highlighted: first
Item selected: first
f
Item highlighted: fourth
Item selected: fourth
t
Item highlighted: third
Item selected: third
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment