Bug 977267 - [a11y] Make autocomplete suggestions accessible to screen readers. r=bgrins

This commit is contained in:
James Teh 2014-08-29 11:36:50 +10:00
Родитель f3188bd805
Коммит ee9e873e22
3 изменённых файлов: 47 добавлений и 1 удалений

Просмотреть файл

@ -65,6 +65,8 @@ function AutocompletePopup(aDocument, aOptions = {})
if (!aOptions.onKeypress) { if (!aOptions.onKeypress) {
this._panel.setAttribute("ignorekeys", "true"); this._panel.setAttribute("ignorekeys", "true");
} }
// Stop this appearing as an alert to accessibility.
this._panel.setAttribute("role", "presentation");
let mainPopupSet = this._document.getElementById("mainPopupSet"); let mainPopupSet = this._document.getElementById("mainPopupSet");
if (mainPopupSet) { if (mainPopupSet) {
@ -106,6 +108,7 @@ function AutocompletePopup(aDocument, aOptions = {})
if (this.onKeypress) { if (this.onKeypress) {
this._list.addEventListener("keypress", this.onKeypress, false); this._list.addEventListener("keypress", this.onKeypress, false);
} }
this._itemIdCounter = 0;
} }
exports.AutocompletePopup = AutocompletePopup; exports.AutocompletePopup = AutocompletePopup;
@ -148,6 +151,8 @@ AutocompletePopup.prototype = {
*/ */
hidePopup: function AP_hidePopup() hidePopup: function AP_hidePopup()
{ {
// Return accessibility focus to the input.
this._document.activeElement.removeAttribute("aria-activedescendant");
this._panel.hidePopup(); this._panel.hidePopup();
}, },
@ -295,6 +300,23 @@ AutocompletePopup.prototype = {
this._list.ensureIndexIsVisible(this._list.selectedIndex); this._list.ensureIndexIsVisible(this._list.selectedIndex);
}, },
/**
* Update accessibility appropriately when the selected item is changed.
*
* @private
*/
_updateAriaActiveDescendant: function AP__updateAriaActiveDescendant()
{
if (!this._list.selectedItem) {
// Return accessibility focus to the input.
this._document.activeElement.removeAttribute("aria-activedescendant");
return;
}
// Focus this for accessibility so users know about the selected item.
this._document.activeElement.setAttribute("aria-activedescendant",
this._list.selectedItem.id);
},
/** /**
* Clear all the items from the autocomplete list. * Clear all the items from the autocomplete list.
*/ */
@ -340,6 +362,7 @@ AutocompletePopup.prototype = {
if (this.isOpen && this._list.ensureIndexIsVisible) { if (this.isOpen && this._list.ensureIndexIsVisible) {
this._list.ensureIndexIsVisible(this._list.selectedIndex); this._list.ensureIndexIsVisible(this._list.selectedIndex);
} }
this._updateAriaActiveDescendant();
}, },
/** /**
@ -362,6 +385,7 @@ AutocompletePopup.prototype = {
if (this.isOpen) { if (this.isOpen) {
this._list.ensureIndexIsVisible(this._list.selectedIndex); this._list.ensureIndexIsVisible(this._list.selectedIndex);
} }
this._updateAriaActiveDescendant();
}, },
/** /**
@ -383,6 +407,8 @@ AutocompletePopup.prototype = {
appendItem: function AP_appendItem(aItem) appendItem: function AP_appendItem(aItem)
{ {
let listItem = this._document.createElementNS(XUL_NS, "richlistitem"); let listItem = this._document.createElementNS(XUL_NS, "richlistitem");
// Items must have an id for accessibility.
listItem.id = this._panel.id + "_item_" + this._itemIdCounter++;
if (this.direction) { if (this.direction) {
listItem.setAttribute("dir", this.direction); listItem.setAttribute("dir", this.direction);
} }

Просмотреть файл

@ -22,7 +22,14 @@ function consoleOpened(HUD) {
let popup = HUD.jsterm.autocompletePopup; let popup = HUD.jsterm.autocompletePopup;
let input = popup._document.activeElement;
function getActiveDescendant() {
return input.ownerDocument.getElementById(
input.getAttribute("aria-activedescendant"));
}
ok(!popup.isOpen, "popup is not open"); ok(!popup.isOpen, "popup is not open");
ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant");
popup._panel.addEventListener("popupshown", function() { popup._panel.addEventListener("popupshown", function() {
popup._panel.removeEventListener("popupshown", arguments.callee, false); popup._panel.removeEventListener("popupshown", arguments.callee, false);
@ -30,6 +37,8 @@ function consoleOpened(HUD) {
ok(popup.isOpen, "popup is open"); ok(popup.isOpen, "popup is open");
is(popup.itemCount, 0, "no items"); is(popup.itemCount, 0, "no items");
ok(!input.hasAttribute("aria-activedescendant"),
"no aria-activedescendant");
popup.setItems(items); popup.setItems(items);
@ -43,31 +52,37 @@ function consoleOpened(HUD) {
is(popup.selectedIndex, 2, is(popup.selectedIndex, 2,
"Index of the first item from bottom is selected."); "Index of the first item from bottom is selected.");
is(popup.selectedItem, items[2], "First item from bottom is selected"); is(popup.selectedItem, items[2], "First item from bottom is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
popup.selectedIndex = 1; popup.selectedIndex = 1;
is(popup.selectedIndex, 1, "index 1 is selected"); is(popup.selectedIndex, 1, "index 1 is selected");
is(popup.selectedItem, items[1], "item1 is selected"); is(popup.selectedItem, items[1], "item1 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
popup.selectedItem = items[2]; popup.selectedItem = items[2];
is(popup.selectedIndex, 2, "index 2 is selected"); is(popup.selectedIndex, 2, "index 2 is selected");
is(popup.selectedItem, items[2], "item2 is selected"); is(popup.selectedItem, items[2], "item2 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
is(popup.selectPreviousItem(), items[1], "selectPreviousItem() works"); is(popup.selectPreviousItem(), items[1], "selectPreviousItem() works");
is(popup.selectedIndex, 1, "index 1 is selected"); is(popup.selectedIndex, 1, "index 1 is selected");
is(popup.selectedItem, items[1], "item1 is selected"); is(popup.selectedItem, items[1], "item1 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
is(popup.selectNextItem(), items[2], "selectPreviousItem() works"); is(popup.selectNextItem(), items[2], "selectPreviousItem() works");
is(popup.selectedIndex, 2, "index 2 is selected"); is(popup.selectedIndex, 2, "index 2 is selected");
is(popup.selectedItem, items[2], "item2 is selected"); is(popup.selectedItem, items[2], "item2 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
ok(popup.selectNextItem(), "selectPreviousItem() works"); ok(popup.selectNextItem(), "selectPreviousItem() works");
is(popup.selectedIndex, 0, "index 0 is selected"); is(popup.selectedIndex, 0, "index 0 is selected");
is(popup.selectedItem, items[0], "item0 is selected"); is(popup.selectedItem, items[0], "item0 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
items.push({label: "label3", value: "value3"}); items.push({label: "label3", value: "value3"});
popup.appendItem(items[3]); popup.appendItem(items[3]);
@ -76,15 +91,19 @@ function consoleOpened(HUD) {
popup.selectedIndex = 3; popup.selectedIndex = 3;
is(popup.selectedItem, items[3], "item3 is selected"); is(popup.selectedItem, items[3], "item3 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
popup.removeItem(items[2]); popup.removeItem(items[2]);
is(popup.selectedIndex, 2, "index2 is selected"); is(popup.selectedIndex, 2, "index2 is selected");
is(popup.selectedItem, items[3], "item3 is still selected"); is(popup.selectedItem, items[3], "item3 is still selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
is(popup.itemCount, items.length - 1, "item2 removed"); is(popup.itemCount, items.length - 1, "item2 removed");
popup.clearItems(); popup.clearItems();
is(popup.itemCount, 0, "items cleared"); is(popup.itemCount, 0, "items cleared");
ok(!input.hasAttribute("aria-activedescendant"),
"no aria-activedescendant");
popup.hidePopup(); popup.hidePopup();
finishTest(); finishTest();

Просмотреть файл

@ -183,7 +183,8 @@ function goUpdateConsoleCommands() {
<textbox class="jsterm-complete-node devtools-monospace" <textbox class="jsterm-complete-node devtools-monospace"
multiline="true" rows="1" tabindex="-1"/> multiline="true" rows="1" tabindex="-1"/>
<textbox class="jsterm-input-node devtools-monospace" <textbox class="jsterm-input-node devtools-monospace"
multiline="true" rows="1" tabindex="0"/> multiline="true" rows="1" tabindex="0"
aria-autocomplete="list"/>
</stack> </stack>
</hbox> </hbox>
</notificationbox> </notificationbox>