mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-06-02 18:41:17 +00:00
[change] Text and View onClick handling
Makes `onClick` part of the stable props API. In the future this will be used to implement `onPress` in the Touchables/Pressables. Special handling of click for keyboards is performed in `createElement`. At the moment, `Text` still includes the `onPress` prop, which will only be called if `onClick` is not also being used. In the future `Text` (in React Native) should remove the Touchable props from its API.
This commit is contained in:
+9
-14
@@ -35,6 +35,7 @@ const Text = forwardRef<TextProps, *>((props, ref) => {
|
||||
nativeID,
|
||||
numberOfLines,
|
||||
onBlur,
|
||||
onClick,
|
||||
onContextMenu,
|
||||
onFocus,
|
||||
onLayout,
|
||||
@@ -128,19 +129,14 @@ const Text = forwardRef<TextProps, *>((props, ref) => {
|
||||
onStartShouldSetResponderCapture
|
||||
});
|
||||
|
||||
function createEnterHandler(fn) {
|
||||
return e => {
|
||||
if (e.keyCode === 13) {
|
||||
fn && fn(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createPressHandler(fn) {
|
||||
return e => {
|
||||
function handleClick(e) {
|
||||
if (onClick != null) {
|
||||
onClick(e);
|
||||
}
|
||||
if (onClick == null && onPress != null) {
|
||||
e.stopPropagation();
|
||||
fn && fn(e);
|
||||
};
|
||||
onPress(e);
|
||||
}
|
||||
}
|
||||
|
||||
const component = hasTextAncestor ? 'span' : 'div';
|
||||
@@ -159,14 +155,13 @@ const Text = forwardRef<TextProps, *>((props, ref) => {
|
||||
lang,
|
||||
nativeID,
|
||||
onBlur,
|
||||
onClick: handleClick,
|
||||
onContextMenu,
|
||||
onFocus,
|
||||
ref: setRef,
|
||||
style,
|
||||
testID,
|
||||
// unstable
|
||||
onClick: onPress != null ? createPressHandler(onPress) : null,
|
||||
onKeyDown: onPress != null ? createEnterHandler(onPress) : null,
|
||||
onMouseDown,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
|
||||
+2
-4
@@ -103,6 +103,8 @@ export type TextProps = {
|
||||
nativeID?: ?string,
|
||||
numberOfLines?: ?number,
|
||||
onBlur?: (e: any) => void,
|
||||
onClick?: (e: any) => void,
|
||||
onContextMenu?: (e: any) => void,
|
||||
onFocus?: (e: any) => void,
|
||||
onLayout?: (e: LayoutEvent) => void,
|
||||
onPress?: (e: any) => void,
|
||||
@@ -126,10 +128,6 @@ export type TextProps = {
|
||||
style?: GenericStyleProp<TextStyle>,
|
||||
testID?: ?string,
|
||||
// unstable
|
||||
onContextMenu?: (e: any) => void,
|
||||
onKeyDown?: (e: any) => void,
|
||||
onKeyPress?: (e: any) => void,
|
||||
onKeyUp?: (e: any) => void,
|
||||
onMouseDown?: (e: any) => void,
|
||||
onMouseEnter?: (e: any) => void,
|
||||
onMouseLeave?: (e: any) => void,
|
||||
|
||||
+8
-10
@@ -44,11 +44,13 @@ const View = forwardRef<ViewProps, *>((props, ref) => {
|
||||
accessibilityRole,
|
||||
accessibilityState,
|
||||
accessibilityValue,
|
||||
accessible,
|
||||
forwardedRef,
|
||||
hitSlop,
|
||||
importantForAccessibility,
|
||||
nativeID,
|
||||
onBlur,
|
||||
onClick,
|
||||
onContextMenu,
|
||||
onFocus,
|
||||
onLayout,
|
||||
@@ -71,12 +73,7 @@ const View = forwardRef<ViewProps, *>((props, ref) => {
|
||||
pointerEvents,
|
||||
testID,
|
||||
// unstable
|
||||
onClick,
|
||||
onClickCapture,
|
||||
onScroll,
|
||||
onWheel,
|
||||
onKeyDown,
|
||||
onKeyPress,
|
||||
onKeyUp,
|
||||
onMouseDown,
|
||||
onMouseEnter,
|
||||
@@ -85,6 +82,7 @@ const View = forwardRef<ViewProps, *>((props, ref) => {
|
||||
onMouseOver,
|
||||
onMouseOut,
|
||||
onMouseUp,
|
||||
onScroll,
|
||||
onTouchCancel,
|
||||
onTouchCancelCapture,
|
||||
onTouchEnd,
|
||||
@@ -93,6 +91,7 @@ const View = forwardRef<ViewProps, *>((props, ref) => {
|
||||
onTouchMoveCapture,
|
||||
onTouchStart,
|
||||
onTouchStartCapture,
|
||||
onWheel,
|
||||
href,
|
||||
itemID,
|
||||
itemRef,
|
||||
@@ -159,11 +158,13 @@ const View = forwardRef<ViewProps, *>((props, ref) => {
|
||||
accessibilityRole,
|
||||
accessibilityState,
|
||||
accessibilityValue,
|
||||
accessible,
|
||||
children,
|
||||
classList,
|
||||
importantForAccessibility,
|
||||
nativeID,
|
||||
onBlur,
|
||||
onClick,
|
||||
onContextMenu,
|
||||
onFocus,
|
||||
pointerEvents,
|
||||
@@ -171,12 +172,7 @@ const View = forwardRef<ViewProps, *>((props, ref) => {
|
||||
style,
|
||||
testID,
|
||||
// unstable
|
||||
onClick,
|
||||
onClickCapture,
|
||||
onScroll,
|
||||
onWheel,
|
||||
onKeyDown,
|
||||
onKeyPress,
|
||||
onKeyUp,
|
||||
onMouseDown,
|
||||
onMouseEnter,
|
||||
@@ -185,6 +181,7 @@ const View = forwardRef<ViewProps, *>((props, ref) => {
|
||||
onMouseOver,
|
||||
onMouseOut,
|
||||
onMouseUp,
|
||||
onScroll,
|
||||
onTouchCancel,
|
||||
onTouchCancelCapture,
|
||||
onTouchEnd,
|
||||
@@ -193,6 +190,7 @@ const View = forwardRef<ViewProps, *>((props, ref) => {
|
||||
onTouchMoveCapture,
|
||||
onTouchStart,
|
||||
onTouchStartCapture,
|
||||
onWheel,
|
||||
href,
|
||||
itemID,
|
||||
itemRef,
|
||||
|
||||
+4
-6
@@ -98,6 +98,8 @@ export type ViewProps = {
|
||||
importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants',
|
||||
nativeID?: ?string,
|
||||
onBlur?: (e: any) => void,
|
||||
onClick?: (e: any) => void,
|
||||
onContextMenu?: (e: any) => void,
|
||||
onFocus?: (e: any) => void,
|
||||
onLayout?: (e: LayoutEvent) => void,
|
||||
onMoveShouldSetResponder?: (e: any) => boolean,
|
||||
@@ -120,13 +122,7 @@ export type ViewProps = {
|
||||
style?: GenericStyleProp<ViewStyle>,
|
||||
testID?: ?string,
|
||||
// unstable
|
||||
onClick?: (e: any) => void,
|
||||
onClickCapture?: (e: any) => void,
|
||||
onContextMenu?: (e: any) => void,
|
||||
onScroll?: (e: any) => void,
|
||||
onWheel?: (e: any) => void,
|
||||
onKeyDown?: (e: any) => void,
|
||||
onKeyPress?: (e: any) => void,
|
||||
onKeyUp?: (e: any) => void,
|
||||
onMouseDown?: (e: any) => void,
|
||||
onMouseEnter?: (e: any) => void,
|
||||
@@ -135,6 +131,7 @@ export type ViewProps = {
|
||||
onMouseOver?: (e: any) => void,
|
||||
onMouseOut?: (e: any) => void,
|
||||
onMouseUp?: (e: any) => void,
|
||||
onScroll?: (e: any) => void,
|
||||
onTouchCancel?: (e: any) => void,
|
||||
onTouchCancelCapture?: (e: any) => void,
|
||||
onTouchEnd?: (e: any) => void,
|
||||
@@ -143,6 +140,7 @@ export type ViewProps = {
|
||||
onTouchMoveCapture?: (e: any) => void,
|
||||
onTouchStart?: (e: any) => void,
|
||||
onTouchStartCapture?: (e: any) => void,
|
||||
onWheel?: (e: any) => void,
|
||||
href?: ?string,
|
||||
itemID?: ?string,
|
||||
itemRef?: ?string,
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ describe('modules/createElement', () => {
|
||||
});
|
||||
|
||||
const testRole = ({ accessibilityRole, disabled }) => {
|
||||
[{ key: 'Enter', which: 13 }, { key: 'Space', which: 32 }].forEach(({ key, which }) => {
|
||||
[{ key: 'Enter' }, { key: ' ' }].forEach(({ key }) => {
|
||||
test(`"onClick" is ${disabled ? 'not ' : ''}called when "${key}" key is pressed`, () => {
|
||||
const onClick = jest.fn();
|
||||
const component = shallow(
|
||||
@@ -35,7 +35,7 @@ describe('modules/createElement', () => {
|
||||
isDefaultPrevented() {},
|
||||
nativeEvent: {},
|
||||
preventDefault() {},
|
||||
which
|
||||
key
|
||||
});
|
||||
expect(onClick).toHaveBeenCalledTimes(disabled ? 0 : 1);
|
||||
});
|
||||
|
||||
@@ -11,39 +11,15 @@ import AccessibilityUtil from '../../modules/AccessibilityUtil';
|
||||
import createDOMProps from '../../modules/createDOMProps';
|
||||
import React from 'react';
|
||||
|
||||
const adjustProps = domProps => {
|
||||
const { onClick, role } = domProps;
|
||||
|
||||
const isButtonLikeRole = AccessibilityUtil.buttonLikeRoles[role];
|
||||
const isDisabled = AccessibilityUtil.isDisabled(domProps);
|
||||
|
||||
// Button-like roles should not trigger 'onClick' if they are disabled.
|
||||
if (isButtonLikeRole && isDisabled && domProps.onClick != null) {
|
||||
domProps.onClick = undefined;
|
||||
}
|
||||
|
||||
// Button-like roles should trigger 'onClick' if SPACE or ENTER keys are pressed.
|
||||
if (isButtonLikeRole && !isDisabled) {
|
||||
domProps.onKeyPress = function(e) {
|
||||
if (!e.isDefaultPrevented() && (e.which === 13 || e.which === 32)) {
|
||||
e.preventDefault();
|
||||
if (onClick) {
|
||||
onClick(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const createElement = (component, props, ...children) => {
|
||||
// use equivalent platform elements where possible
|
||||
// Use equivalent platform elements where possible.
|
||||
let accessibilityComponent;
|
||||
if (component && component.constructor === String) {
|
||||
accessibilityComponent = AccessibilityUtil.propsToAccessibilityComponent(props);
|
||||
}
|
||||
const Component = accessibilityComponent || component;
|
||||
const domProps = createDOMProps(Component, props);
|
||||
adjustProps(domProps);
|
||||
|
||||
return React.createElement(Component, domProps, ...children);
|
||||
};
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ const createDOMProps = (component, props, styleResolver) => {
|
||||
accessibilityRelationship,
|
||||
accessibilityState,
|
||||
accessibilityValue,
|
||||
accessible,
|
||||
classList,
|
||||
disabled: providedDisabled,
|
||||
importantForAccessibility,
|
||||
@@ -75,7 +76,6 @@ const createDOMProps = (component, props, styleResolver) => {
|
||||
style: providedStyle,
|
||||
testID,
|
||||
/* eslint-disable */
|
||||
accessible,
|
||||
accessibilityRole,
|
||||
/* eslint-enable */
|
||||
unstable_ariaSet,
|
||||
@@ -176,18 +176,18 @@ const createDOMProps = (component, props, styleResolver) => {
|
||||
// FOCUS
|
||||
// Assume that 'link' is focusable by default (uses <a>).
|
||||
// Assume that 'button' is not (uses <div role='button'>) but must be treated as such.
|
||||
const focusable =
|
||||
!disabled &&
|
||||
importantForAccessibility !== 'no' &&
|
||||
importantForAccessibility !== 'no-hide-descendants';
|
||||
if (
|
||||
const isInteractiveElement =
|
||||
role === 'link' ||
|
||||
component === 'a' ||
|
||||
component === 'button' ||
|
||||
component === 'input' ||
|
||||
component === 'select' ||
|
||||
component === 'textarea'
|
||||
) {
|
||||
component === 'textarea';
|
||||
const focusable =
|
||||
!disabled &&
|
||||
importantForAccessibility !== 'no' &&
|
||||
importantForAccessibility !== 'no-hide-descendants';
|
||||
if (isInteractiveElement) {
|
||||
if (accessible === false || !focusable) {
|
||||
domProps.tabIndex = '-1';
|
||||
} else {
|
||||
@@ -251,6 +251,29 @@ const createDOMProps = (component, props, styleResolver) => {
|
||||
domProps['data-testid'] = testID;
|
||||
}
|
||||
|
||||
// Keyboard accessibility
|
||||
// Button-like roles should trigger 'onClick' if SPACE or ENTER keys are 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) {
|
||||
const onKeyDown = domProps.onKeyDown;
|
||||
domProps.onKeyDown = function(e) {
|
||||
if (!e.isDefaultPrevented() && (e.key === 'Enter' || e.key === ' ')) {
|
||||
e.preventDefault();
|
||||
if (onKeyDown != null) {
|
||||
onKeyDown(e);
|
||||
}
|
||||
onClick(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return domProps;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user