Bug 1478156 - Move shareable focus methods to be used for color picker accessibility, r=yzen,gl

Differential Revision: https://phabricator.services.mozilla.com/D33885

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Maliha Islam 2019-07-15 11:01:04 +00:00
Родитель e9c7f9af44
Коммит 8cfcbe667f
5 изменённых файлов: 113 добавлений и 166 удалений

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

@ -10,8 +10,14 @@ const {
flashElementOff,
} = require("devtools/client/inspector/markup/utils");
const DRAG_DROP_MIN_INITIAL_DISTANCE = 10;
loader.lazyRequireGetter(
this,
"wrapMoveFocus",
"devtools/client/shared/focus",
true
);
const DRAG_DROP_MIN_INITIAL_DISTANCE = 10;
const TYPES = {
TEXT_CONTAINER: "textcontainer",
ELEMENT_CONTAINER: "elementcontainer",
@ -472,30 +478,6 @@ MarkupContainer.prototype = {
return false;
},
/**
* Move keyboard focus to a next/previous focusable element inside container
* that is not part of its children (only if current focus is on first or last
* element).
*
* @param {DOMNode} current currently focused element
* @param {Boolean} back direction
* @return {DOMNode} newly focused element if any
*/
_wrapMoveFocus: function(current, back) {
const elms = this.focusableElms;
let next;
if (back) {
if (elms.indexOf(current) === 0) {
next = elms[elms.length - 1];
next.focus();
}
} else if (elms.indexOf(current) === elms.length - 1) {
next = elms[0];
next.focus();
}
return next;
},
_onKeyDown: function(event) {
const { target, keyCode, shiftKey } = event;
const isInput = this.markup._isInputOrTextarea(target);
@ -511,7 +493,11 @@ MarkupContainer.prototype = {
// Only handle 'Tab' if tabbable element is on the edge (first or last).
if (isInput) {
// Corresponding tabbable element is editor's next sibling.
const next = this._wrapMoveFocus(target.nextSibling, shiftKey);
const next = wrapMoveFocus(
this.focusableElms,
target.nextSibling,
shiftKey
);
if (next) {
event.preventDefault();
// Keep the editing state if possible.
@ -522,7 +508,7 @@ MarkupContainer.prototype = {
}
}
} else {
const next = this._wrapMoveFocus(target, shiftKey);
const next = wrapMoveFocus(this.focusableElms, target, shiftKey);
if (next) {
event.preventDefault();
}

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

@ -24,7 +24,13 @@ const {
loader.lazyRequireGetter(
this,
"focusableSelector",
"wrapMoveFocus",
"devtools/client/shared/focus",
true
);
loader.lazyRequireGetter(
this,
"getFocusableElements",
"devtools/client/shared/focus",
true
);
@ -50,7 +56,6 @@ class ListItemClass extends Component {
this._setTabbableState = this._setTabbableState.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this._wrapMoveFocus = this._wrapMoveFocus.bind(this);
}
componentDidMount() {
@ -61,45 +66,6 @@ class ListItemClass extends Component {
this._setTabbableState();
}
/**
* Get a list of all elements that are focusable with a keyboard inside the list item.
*/
getFocusableElements() {
return Array.from(
this.contentRef.current.querySelectorAll(focusableSelector)
);
}
/**
* Wrap and move keyboard focus to first/last focusable element inside the list item to
* prevent the focus from escaping the list item container.
* element).
*
* @param {DOMNode} current currently focused element
* @param {Boolean} back direction
* @return {Boolean} true there is a newly focused element.
*/
_wrapMoveFocus(current, back) {
const elms = this.getFocusableElements();
let next;
if (elms.length === 0) {
return false;
}
if (back) {
if (elms.indexOf(current) === 0) {
next = elms[elms.length - 1];
next.focus();
}
} else if (elms.indexOf(current) === elms.length - 1) {
next = elms[0];
next.focus();
}
return !!next;
}
_onKeyDown(event) {
const { target, key, shiftKey } = event;
@ -107,7 +73,11 @@ class ListItemClass extends Component {
return;
}
const focusMoved = this._wrapMoveFocus(target, shiftKey);
const focusMoved = !!wrapMoveFocus(
getFocusableElements(this.contentRef.current),
target,
shiftKey
);
if (focusMoved) {
// Focus was moved to the begining/end of the list, so we need to prevent the
// default focus change that would happen here.
@ -123,7 +93,7 @@ class ListItemClass extends Component {
* outside its container, focus on the first focusable element inside.
*/
_setTabbableState() {
const elms = this.getFocusableElements();
const elms = getFocusableElements(this.contentRef.current);
if (elms.length === 0) {
return;
}

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

@ -17,7 +17,13 @@ const {
loader.lazyRequireGetter(
this,
"focusableSelector",
"wrapMoveFocus",
"devtools/client/shared/focus",
true
);
loader.lazyRequireGetter(
this,
"getFocusableElements",
"devtools/client/shared/focus",
true
);
@ -943,7 +949,7 @@ class TreeNodeClass extends Component {
// Make sure that none of the focusable elements inside the tree node container are
// tabbable if the tree node is not active. If the tree node is active and focus is
// outside its container, focus on the first focusable element inside.
const elms = this.getFocusableElements();
const elms = getFocusableElements(this.refs.treenode);
if (elms.length === 0) {
return;
}
@ -958,43 +964,6 @@ class TreeNodeClass extends Component {
}
}
/**
* Get a list of all elements that are focusable with a keyboard inside the tree node.
*/
getFocusableElements() {
return Array.from(this.refs.treenode.querySelectorAll(focusableSelector));
}
/**
* Wrap and move keyboard focus to first/last focusable element inside the tree node to
* prevent the focus from escaping the tree node boundaries.
* element).
*
* @param {DOMNode} current currently focused element
* @param {Boolean} back direction
* @return {Boolean} true there is a newly focused element.
*/
_wrapMoveFocus(current, back) {
const elms = this.getFocusableElements();
let next;
if (elms.length === 0) {
return false;
}
if (back) {
if (elms.indexOf(current) === 0) {
next = elms[elms.length - 1];
next.focus();
}
} else if (elms.indexOf(current) === elms.length - 1) {
next = elms[0];
next.focus();
}
return !!next;
}
_onKeyDown(e) {
const { target, key, shiftKey } = e;
@ -1002,7 +971,11 @@ class TreeNodeClass extends Component {
return;
}
const focusMoved = this._wrapMoveFocus(target, shiftKey);
const focusMoved = !!wrapMoveFocus(
getFocusableElements(this.refs.treenode),
target,
shiftKey
);
if (focusMoved) {
// Focus was moved to the begining/end of the list, so we need to prevent the
// default focus change that would happen here.

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

@ -21,7 +21,10 @@ define(function(require, exports, module) {
const TreeCell = createFactory(require("./TreeCell"));
const LabelCell = createFactory(require("./LabelCell"));
const { focusableSelector } = require("devtools/client/shared/focus");
const {
wrapMoveFocus,
getFocusableElements,
} = require("devtools/client/shared/focus");
const UPDATE_ON_PROPS = [
"name",
@ -133,7 +136,7 @@ define(function(require, exports, module) {
* is outside its container, focus on the first focusable element inside.
*/
_setTabbableState() {
const elms = this.getFocusableElements();
const elms = getFocusableElements(this.treeRowRef.current);
if (elms.length === 0) {
return;
}
@ -149,46 +152,6 @@ define(function(require, exports, module) {
}
}
/**
* Get a list of all elements that are focusable with a keyboard inside the
* tree node.
*/
getFocusableElements() {
return Array.from(
this.treeRowRef.current.querySelectorAll(focusableSelector)
);
}
/**
* Wrap and move keyboard focus to first/last focusable element inside the
* tree node to prevent the focus from escaping the tree node boundaries.
* element).
*
* @param {DOMNode} current currently focused element
* @param {Boolean} back direction
* @return {Boolean} true there is a newly focused element.
*/
_wrapMoveFocus(current, back) {
const elms = this.getFocusableElements();
let next;
if (elms.length === 0) {
return false;
}
if (back) {
if (elms.indexOf(current) === 0) {
next = elms[elms.length - 1];
next.focus();
}
} else if (elms.indexOf(current) === elms.length - 1) {
next = elms[0];
next.focus();
}
return !!next;
}
_onKeyDown(e) {
const { target, key, shiftKey } = e;
@ -196,7 +159,11 @@ define(function(require, exports, module) {
return;
}
const focusMoved = this._wrapMoveFocus(target, shiftKey);
const focusMoved = !!wrapMoveFocus(
getFocusableElements(this.treeRowRef.current),
target,
shiftKey
);
if (focusMoved) {
// Focus was moved to the begining/end of the list, so we need to
// prevent the default focus change that would happen here.

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

@ -4,19 +4,70 @@
"use strict";
/*
* Simplied selector targetting elements that can receive the focus, full
* version at http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus
*/
const focusableSelector = [
"a[href]:not([tabindex='-1'])",
"button:not([disabled]):not([tabindex='-1'])",
"iframe:not([tabindex='-1'])",
"input:not([disabled]):not([tabindex='-1'])",
"select:not([disabled]):not([tabindex='-1'])",
"textarea:not([disabled]):not([tabindex='-1'])",
"[tabindex]:not([tabindex='-1'])",
].join(", ");
/**
* Wrap and move keyboard focus to first/last focusable element inside a container
* element to prevent the focus from escaping the container.
*
* @param {Array} elms
* focusable elements inside a container
* @param {DOMNode} current
* currently focused element inside containter
* @param {Boolean} back
* direction
* @return {DOMNode}
* newly focused element
*/
function wrapMoveFocus(elms, current, back) {
let next;
if (elms.length === 0) {
return false;
}
if (back) {
if (elms.indexOf(current) === 0) {
next = elms[elms.length - 1];
next.focus();
}
} else if (elms.indexOf(current) === elms.length - 1) {
next = elms[0];
next.focus();
}
return next;
}
/**
* Get a list of all elements that are focusable with a keyboard inside the parent element
*
* @param {DOMNode} parentEl
* parent DOM element to be queried
* @return {Array}
* array of focusable children elements inside the parent
*/
function getFocusableElements(parentEl) {
return parentEl
? Array.from(parentEl.querySelectorAll(focusableSelector))
: [];
}
// Make this available to both AMD and CJS environments
define(function(require, exports, module) {
// Simplied selector targetting elements that can receive the focus, full
// version at
// http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus
// .
module.exports.focusableSelector = [
"a[href]:not([tabindex='-1'])",
"button:not([disabled]):not([tabindex='-1'])",
"iframe:not([tabindex='-1'])",
"input:not([disabled]):not([tabindex='-1'])",
"select:not([disabled]):not([tabindex='-1'])",
"textarea:not([disabled]):not([tabindex='-1'])",
"[tabindex]:not([tabindex='-1'])",
].join(", ");
module.exports.focusableSelector = focusableSelector;
exports.wrapMoveFocus = wrapMoveFocus;
exports.getFocusableElements = getFocusableElements;
});