зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
7739643423
Коммит
ff03e00a30
|
@ -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;
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче