Adjust keyboard onClick shim

This commit is contained in:
Nicolas Gallagher
2020-04-29 15:48:02 -07:00
parent 4b4163f630
commit e9933b107a
4 changed files with 32 additions and 37 deletions
@@ -153,7 +153,6 @@ function Pressable(props: Props, forwardedRef): React.Node {
<View <View
{...rest} {...rest}
{...pressEventHandlers} {...pressEventHandlers}
accessibilityRole={props.accessibilityRole ?? 'button'}
accessibilityState={accessibilityState} accessibilityState={accessibilityState}
accessible={accessible !== false} accessible={accessible !== false}
focusable={focusable !== false} focusable={focusable !== false}
@@ -11,7 +11,6 @@
'use strict'; 'use strict';
import invariant from 'fbjs/lib/invariant'; import invariant from 'fbjs/lib/invariant';
import isLink from '../../modules/isLink';
import isSelectionValid from '../../modules/isSelectionValid'; import isSelectionValid from '../../modules/isSelectionValid';
type ClickEvent = any; type ClickEvent = any;
@@ -131,9 +130,12 @@ const isPressStartSignal = signal =>
const isTerminalSignal = signal => signal === RESPONDER_TERMINATED || signal === RESPONDER_RELEASE; const isTerminalSignal = signal => signal === RESPONDER_TERMINATED || signal === RESPONDER_RELEASE;
const isKeyPress = event => { const isValidKeyPress = event => {
const key = event.key;
const target = event.currentTarget; const target = event.currentTarget;
return (!isLink(target) && event.key === ' ') || event.key === 'Enter'; const role = target.getAttribute('role');
const isSpacebar = key === ' ' || key === 'Spacebar';
return key === 'Enter' || (isSpacebar && (role === 'button' || role === 'menuitem'));
}; };
const DEFAULT_LONG_PRESS_DELAY_MS = 450; // 500 - 50 const DEFAULT_LONG_PRESS_DELAY_MS = 450; // 500 - 50
@@ -306,7 +308,7 @@ export default class PressResponder {
}, },
onKeyDown: event => { onKeyDown: event => {
if (isKeyPress(event)) { if (isValidKeyPress(event)) {
if (this._touchState === NOT_RESPONDER) { if (this._touchState === NOT_RESPONDER) {
start(event, false); start(event, false);
} }
@@ -315,7 +317,7 @@ export default class PressResponder {
}, },
onKeyUp: event => { onKeyUp: event => {
if (isKeyPress(event)) { if (isValidKeyPress(event)) {
end(event); end(event);
event.stopPropagation(); event.stopPropagation();
} }
+25 -16
View File
@@ -86,6 +86,14 @@ const createDOMProps = (component, props, styleResolver) => {
const disabled = const disabled =
(accessibilityState != null && accessibilityState.disabled === true) || providedDisabled; (accessibilityState != null && accessibilityState.disabled === true) || providedDisabled;
const role = AccessibilityUtil.propsToAriaRole(props); const role = AccessibilityUtil.propsToAriaRole(props);
const isNativeInteractiveElement =
role === 'link' ||
component === 'a' ||
component === 'button' ||
component === 'input' ||
component === 'select' ||
component === 'textarea' ||
domProps.contentEditable != null;
// unstable_ariaSet // unstable_ariaSet
if (unstable_ariaSet != null) { if (unstable_ariaSet != null) {
@@ -176,24 +184,17 @@ const createDOMProps = (component, props, styleResolver) => {
// FOCUS // FOCUS
// Assume that 'link' is focusable by default (uses <a>). // Assume that 'link' is focusable by default (uses <a>).
// Assume that 'button' is not (uses <div role='button'>) but must be treated as such. // Assume that 'button' is not (uses <div role='button'>) but must be treated as such.
const isInteractiveElement =
role === 'link' ||
component === 'a' ||
component === 'button' ||
component === 'input' ||
component === 'select' ||
component === 'textarea';
const focusable = const focusable =
!disabled && !disabled &&
importantForAccessibility !== 'no' && importantForAccessibility !== 'no' &&
importantForAccessibility !== 'no-hide-descendants'; importantForAccessibility !== 'no-hide-descendants';
if (isInteractiveElement) { if (isNativeInteractiveElement) {
if (accessible === false || !focusable) { if (accessible === false || !focusable) {
domProps.tabIndex = '-1'; domProps.tabIndex = '-1';
} else { } else {
domProps['data-focusable'] = true; domProps['data-focusable'] = true;
} }
} else if (AccessibilityUtil.buttonLikeRoles[role] || role === 'textbox') { } else if (role === 'button' || role === 'menuitem' || role === 'textbox') {
if (accessible !== false && focusable) { if (accessible !== false && focusable) {
domProps['data-focusable'] = true; domProps['data-focusable'] = true;
domProps.tabIndex = '0'; domProps.tabIndex = '0';
@@ -252,22 +253,30 @@ const createDOMProps = (component, props, styleResolver) => {
} }
// Keyboard accessibility // Keyboard accessibility
// Button-like roles should trigger 'onClick' if SPACE or ENTER keys are pressed. // Button-like roles should trigger 'onClick' if SPACE key is pressed.
// Button-like roles should not trigger 'onClick' if they are disabled. // Button-like roles should not trigger 'onClick' if they are disabled.
if (domProps['data-focusable']) { if (domProps['data-focusable']) {
const onClick = domProps.onClick; const onClick = domProps.onClick;
if (onClick != null) { if (onClick != null) {
if (disabled) { if (disabled) {
domProps.onClick = undefined; domProps.onClick = undefined;
} else if (!isInteractiveElement) { } else if (!isNativeInteractiveElement) {
// For native elements that are focusable but don't dispatch 'click' events
// for keyboards.
const onKeyDown = domProps.onKeyDown; const onKeyDown = domProps.onKeyDown;
domProps.onKeyDown = function(e) { domProps.onKeyDown = function(e) {
if (!e.isDefaultPrevented() && (e.key === 'Enter' || e.key === ' ')) { const key = e.key;
e.preventDefault(); const isSpacebarKey = key === ' ' || key === 'Spacebar';
if (onKeyDown != null) { const isButtonRole = role === 'button' || role === 'menuitem';
onKeyDown(e); if (onKeyDown != null) {
} onKeyDown(e);
}
if (key === 'Enter') {
onClick(e); onClick(e);
} else if (isSpacebarKey && isButtonRole) {
onClick(e);
// Prevent spacebar scrolling the window
e.preventDefault();
} }
}; };
} }
-15
View File
@@ -1,15 +0,0 @@
/**
* Copyright (c) Nicolas Gallagher
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
export default function isLink(node: HTMLElement) {
return (
(node.nodeName === 'A' && node.getAttribute('href') != null) ||
node.getAttribute('role') === 'link'
);
}