diff --git a/packages/react-native-web/src/exports/Pressable/index.js b/packages/react-native-web/src/exports/Pressable/index.js index fc001788..c68eca3c 100644 --- a/packages/react-native-web/src/exports/Pressable/index.js +++ b/packages/react-native-web/src/exports/Pressable/index.js @@ -153,7 +153,6 @@ function Pressable(props: Props, forwardedRef): React.Node { const isTerminalSignal = signal => signal === RESPONDER_TERMINATED || signal === RESPONDER_RELEASE; -const isKeyPress = event => { +const isValidKeyPress = event => { + const key = event.key; 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 @@ -306,7 +308,7 @@ export default class PressResponder { }, onKeyDown: event => { - if (isKeyPress(event)) { + if (isValidKeyPress(event)) { if (this._touchState === NOT_RESPONDER) { start(event, false); } @@ -315,7 +317,7 @@ export default class PressResponder { }, onKeyUp: event => { - if (isKeyPress(event)) { + if (isValidKeyPress(event)) { end(event); event.stopPropagation(); } diff --git a/packages/react-native-web/src/modules/createDOMProps/index.js b/packages/react-native-web/src/modules/createDOMProps/index.js index cf4a312c..55174aa9 100644 --- a/packages/react-native-web/src/modules/createDOMProps/index.js +++ b/packages/react-native-web/src/modules/createDOMProps/index.js @@ -86,6 +86,14 @@ const createDOMProps = (component, props, styleResolver) => { const disabled = (accessibilityState != null && accessibilityState.disabled === true) || providedDisabled; const role = AccessibilityUtil.propsToAriaRole(props); + const isNativeInteractiveElement = + role === 'link' || + component === 'a' || + component === 'button' || + component === 'input' || + component === 'select' || + component === 'textarea' || + domProps.contentEditable != null; // unstable_ariaSet if (unstable_ariaSet != null) { @@ -176,24 +184,17 @@ const createDOMProps = (component, props, styleResolver) => { // FOCUS // Assume that 'link' is focusable by default (uses ). // Assume that 'button' is not (uses
) but must be treated as such. - const isInteractiveElement = - role === 'link' || - component === 'a' || - component === 'button' || - component === 'input' || - component === 'select' || - component === 'textarea'; const focusable = !disabled && importantForAccessibility !== 'no' && importantForAccessibility !== 'no-hide-descendants'; - if (isInteractiveElement) { + if (isNativeInteractiveElement) { if (accessible === false || !focusable) { domProps.tabIndex = '-1'; } else { domProps['data-focusable'] = true; } - } else if (AccessibilityUtil.buttonLikeRoles[role] || role === 'textbox') { + } else if (role === 'button' || role === 'menuitem' || role === 'textbox') { if (accessible !== false && focusable) { domProps['data-focusable'] = true; domProps.tabIndex = '0'; @@ -252,22 +253,30 @@ const createDOMProps = (component, props, styleResolver) => { } // 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. if (domProps['data-focusable']) { const onClick = domProps.onClick; if (onClick != null) { if (disabled) { 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; domProps.onKeyDown = function(e) { - if (!e.isDefaultPrevented() && (e.key === 'Enter' || e.key === ' ')) { - e.preventDefault(); - if (onKeyDown != null) { - onKeyDown(e); - } + const key = e.key; + const isSpacebarKey = key === ' ' || key === 'Spacebar'; + const isButtonRole = role === 'button' || role === 'menuitem'; + if (onKeyDown != null) { + onKeyDown(e); + } + if (key === 'Enter') { onClick(e); + } else if (isSpacebarKey && isButtonRole) { + onClick(e); + // Prevent spacebar scrolling the window + e.preventDefault(); } }; } diff --git a/packages/react-native-web/src/modules/isLink/index.js b/packages/react-native-web/src/modules/isLink/index.js deleted file mode 100644 index 3a8d7bb9..00000000 --- a/packages/react-native-web/src/modules/isLink/index.js +++ /dev/null @@ -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' - ); -}