diff --git a/packages/docs/src/components/View/View.stories.js b/packages/docs/src/components/View/View.stories.js
index a4958f6b..74b97692 100644
--- a/packages/docs/src/components/View/View.stories.js
+++ b/packages/docs/src/components/View/View.stories.js
@@ -3,12 +3,12 @@ import PropTypes, { any, bool, func, object, oneOf, string } from 'prop-types';
const ofProps = () => {};
ofProps.propTypes = {
- /* test */
accessibilityLabel: PropTypes.string,
accessibilityLiveRegion: oneOf(['assertive', 'none', 'polite']),
accessibilityRelationship: object,
accessibilityRole: string,
accessibilityState: object,
+ accessibilityValue: object,
accessible: bool,
children: any,
forwardedRef: any,
diff --git a/packages/docs/src/guides/accessibility.stories.mdx b/packages/docs/src/guides/accessibility.stories.mdx
index b767aad4..db32c027 100644
--- a/packages/docs/src/guides/accessibility.stories.mdx
+++ b/packages/docs/src/guides/accessibility.stories.mdx
@@ -89,6 +89,10 @@ assistive technologies of a `role` value change.
...
+### accessibilityValue: ?object
+
+...
+
### accessible
When `true`, indicates that the view is an accessibility element. When a view
diff --git a/packages/react-native-web/src/exports/ActivityIndicator/index.js b/packages/react-native-web/src/exports/ActivityIndicator/index.js
index bc8bfe59..74485145 100644
--- a/packages/react-native-web/src/exports/ActivityIndicator/index.js
+++ b/packages/react-native-web/src/exports/ActivityIndicator/index.js
@@ -14,6 +14,8 @@ import StyleSheet from '../StyleSheet';
import View from '../View';
import React, { forwardRef } from 'react';
+const accessibilityValue = { max: 1, min: 0 };
+
const createSvgCircle = style => (
);
@@ -54,8 +56,7 @@ const ActivityIndicator = forwardRef((props, ref) =>
diff --git a/packages/react-native-web/src/exports/ProgressBar/index.js b/packages/react-native-web/src/exports/ProgressBar/index.js
index 96f3f4f0..9af90211 100644
--- a/packages/react-native-web/src/exports/ProgressBar/index.js
+++ b/packages/react-native-web/src/exports/ProgressBar/index.js
@@ -48,9 +48,11 @@ const ProgressBar = forwardRef((props, ref) => {
diff --git a/packages/react-native-web/src/exports/Text/index.js b/packages/react-native-web/src/exports/Text/index.js
index 6de8f66d..69f3059e 100644
--- a/packages/react-native-web/src/exports/Text/index.js
+++ b/packages/react-native-web/src/exports/Text/index.js
@@ -12,7 +12,6 @@ import type { TextProps } from './types';
import createElement from '../createElement';
import css from '../StyleSheet/css';
-import filterSupportedProps from '../View/filterSupportedProps';
import setAndForwardRef from '../../modules/setAndForwardRef';
import useElementLayout from '../../hooks/useElementLayout';
import usePlatformMethods from '../../hooks/usePlatformMethods';
@@ -21,7 +20,69 @@ import StyleSheet from '../StyleSheet';
import TextAncestorContext from './TextAncestorContext';
const Text = forwardRef((props, ref) => {
- const { dir, forwardedRef, numberOfLines, onLayout, onPress, selectable } = props;
+ const {
+ accessibilityLabel,
+ accessibilityLiveRegion,
+ accessibilityRelationship,
+ accessibilityRole,
+ accessibilityState,
+ children,
+ dir,
+ forwardedRef,
+ importantForAccessibility,
+ lang,
+ nativeID,
+ numberOfLines,
+ onBlur,
+ onContextMenu,
+ onFocus,
+ onLayout,
+ onPress,
+ onMoveShouldSetResponder,
+ onMoveShouldSetResponderCapture,
+ onResponderEnd,
+ onResponderGrant,
+ onResponderMove,
+ onResponderReject,
+ onResponderRelease,
+ onResponderStart,
+ onResponderTerminate,
+ onResponderTerminationRequest,
+ onScrollShouldSetResponder,
+ onScrollShouldSetResponderCapture,
+ onSelectionChangeShouldSetResponder,
+ onSelectionChangeShouldSetResponderCapture,
+ onStartShouldSetResponder,
+ onStartShouldSetResponderCapture,
+ selectable,
+ testID,
+ // unstable
+ onMouseDown,
+ onMouseEnter,
+ onMouseLeave,
+ onMouseMove,
+ onMouseOver,
+ onMouseOut,
+ onMouseUp,
+ onTouchCancel,
+ onTouchCancelCapture,
+ onTouchEnd,
+ onTouchEndCapture,
+ onTouchMove,
+ onTouchMoveCapture,
+ onTouchStart,
+ onTouchStartCapture,
+ href,
+ itemID,
+ itemRef,
+ itemProp,
+ itemScope,
+ itemType,
+ rel,
+ target,
+ unstable_ariaSet,
+ unstable_dataSet
+ } = props;
const hasTextAncestor = useContext(TextAncestorContext);
const hostRef = useRef(null);
@@ -63,22 +124,72 @@ const Text = forwardRef((props, ref) => {
};
}
- const supportedProps = filterSupportedProps(props);
-
- if (onPress) {
- supportedProps.accessible = true;
- supportedProps.onClick = createPressHandler(onPress);
- supportedProps.onKeyDown = createEnterHandler(onPress);
- }
-
- supportedProps.classList = classList;
- // allow browsers to automatically infer the language writing direction
- supportedProps.dir = dir !== undefined ? dir : 'auto';
- supportedProps.ref = setRef;
- supportedProps.style = style;
-
const component = hasTextAncestor ? 'span' : 'div';
- const element = createElement(component, supportedProps);
+ const element = createElement(component, {
+ accessibilityLabel,
+ accessibilityLiveRegion,
+ accessibilityRelationship,
+ accessibilityRole,
+ accessibilityState,
+ accessible: onPress != null ? true : null,
+ children,
+ classList,
+ // allow browsers to automatically infer the language writing direction
+ dir: dir !== undefined ? dir : 'auto',
+ importantForAccessibility,
+ lang,
+ nativeID,
+ onBlur,
+ onFocus,
+ onMoveShouldSetResponder,
+ onMoveShouldSetResponderCapture,
+ onResponderEnd,
+ onResponderGrant,
+ onResponderMove,
+ onResponderReject,
+ onResponderRelease,
+ onResponderStart,
+ onResponderTerminate,
+ onResponderTerminationRequest,
+ onScrollShouldSetResponder,
+ onScrollShouldSetResponderCapture,
+ onSelectionChangeShouldSetResponder,
+ onSelectionChangeShouldSetResponderCapture,
+ onStartShouldSetResponder,
+ onStartShouldSetResponderCapture,
+ ref: setRef,
+ style,
+ testID,
+ // unstable
+ onClick: onPress != null ? createPressHandler(onPress) : null,
+ onContextMenu,
+ onKeyDown: onPress != null ? createEnterHandler(onPress) : null,
+ onMouseDown,
+ onMouseEnter,
+ onMouseLeave,
+ onMouseMove,
+ onMouseOver,
+ onMouseOut,
+ onMouseUp,
+ onTouchCancel,
+ onTouchCancelCapture,
+ onTouchEnd,
+ onTouchEndCapture,
+ onTouchMove,
+ onTouchMoveCapture,
+ onTouchStart,
+ onTouchStartCapture,
+ href,
+ itemID,
+ itemRef,
+ itemProp,
+ itemScope,
+ itemType,
+ rel,
+ target,
+ unstable_ariaSet,
+ unstable_dataSet
+ });
return hasTextAncestor ? (
element
diff --git a/packages/react-native-web/src/exports/Text/types.js b/packages/react-native-web/src/exports/Text/types.js
index 0a028484..681fbd55 100644
--- a/packages/react-native-web/src/exports/Text/types.js
+++ b/packages/react-native-web/src/exports/Text/types.js
@@ -104,14 +104,53 @@ export type TextProps = {
onFocus?: (e: any) => void,
onLayout?: (e: LayoutEvent) => void,
onPress?: (e: any) => void,
+ onMoveShouldSetResponder?: (e: any) => boolean,
+ onMoveShouldSetResponderCapture?: (e: any) => boolean,
+ onResponderEnd?: (e: any) => void,
+ onResponderGrant?: (e: any) => void,
+ onResponderMove?: (e: any) => void,
+ onResponderReject?: (e: any) => void,
+ onResponderRelease?: (e: any) => void,
+ onResponderStart?: (e: any) => void,
+ onResponderTerminate?: (e: any) => void,
+ onResponderTerminationRequest?: (e: any) => void,
+ onScrollShouldSetResponder?: (e: any) => boolean,
+ onScrollShouldSetResponderCapture?: (e: any) => boolean,
+ onSelectionChangeShouldSetResponder?: (e: any) => boolean,
+ onSelectionChangeShouldSetResponderCapture?: (e: any) => boolean,
+ onStartShouldSetResponder?: (e: any) => boolean,
+ onStartShouldSetResponderCapture?: (e: any) => boolean,
selectable?: boolean,
style?: GenericStyleProp,
testID?: string,
- // web extensions
+ // 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,
+ onMouseMove?: (e: any) => void,
+ onMouseOver?: (e: any) => void,
+ onMouseOut?: (e: any) => void,
+ onMouseUp?: (e: any) => void,
+ onTouchCancel?: (e: any) => void,
+ onTouchCancelCapture?: (e: any) => void,
+ onTouchEnd?: (e: any) => void,
+ onTouchEndCapture?: (e: any) => void,
+ onTouchMove?: (e: any) => void,
+ onTouchMoveCapture?: (e: any) => void,
+ onTouchStart?: (e: any) => void,
+ onTouchStartCapture?: (e: any) => void,
+ href?: string,
itemID?: string,
itemRef?: string,
itemProp?: string,
itemScope?: string,
- itemType?: string
+ itemType?: string,
+ rel?: string,
+ target?: string,
+ unstable_ariaSet?: Object,
+ unstable_dataSet?: Object
};
diff --git a/packages/react-native-web/src/exports/TextInput/index.js b/packages/react-native-web/src/exports/TextInput/index.js
index 22dd6c76..71023db6 100644
--- a/packages/react-native-web/src/exports/TextInput/index.js
+++ b/packages/react-native-web/src/exports/TextInput/index.js
@@ -12,11 +12,11 @@ import type { TextInputProps } from './types';
import createElement from '../createElement';
import css from '../StyleSheet/css';
-import filterSupportedProps from '../View/filterSupportedProps';
import setAndForwardRef from '../../modules/setAndForwardRef';
import useElementLayout from '../../hooks/useElementLayout';
-import usePlatformMethods from '../../hooks/usePlatformMethods';
-import { forwardRef, useEffect, useRef } from 'react';
+import useLayoutEffect from '../../hooks/useLayoutEffect';
+import { usePlatformInputMethods } from '../../hooks/usePlatformMethods';
+import { forwardRef, useRef } from 'react';
import StyleSheet from '../StyleSheet';
import TextInputState from '../../modules/TextInputState';
@@ -50,6 +50,9 @@ const setSelection = (node, selection) => {
const TextInput = forwardRef((props, ref) => {
const {
+ accessibilityLabel,
+ accessibilityRelationship,
+ accessibilityState,
autoCapitalize = 'sentences',
autoComplete,
autoCompleteType,
@@ -60,27 +63,58 @@ const TextInput = forwardRef((props, ref) => {
defaultValue,
disabled,
editable = true,
+ forwardedRef,
+ importantForAccessibility,
keyboardType = 'default',
maxLength,
multiline = false,
+ nativeID,
numberOfLines = 1,
onBlur,
onChange,
onChangeText,
onContentSizeChange,
+ onContextMenu,
onFocus,
onKeyPress,
onLayout,
+ onScroll,
onSelectionChange,
onSubmitEditing,
+ onMoveShouldSetResponder,
+ onMoveShouldSetResponderCapture,
+ onResponderEnd,
+ onResponderGrant,
+ onResponderMove,
+ onResponderReject,
+ onResponderRelease,
+ onResponderStart,
+ onResponderTerminate,
+ onResponderTerminationRequest,
+ onScrollShouldSetResponder,
+ onScrollShouldSetResponderCapture,
+ onSelectionChangeShouldSetResponder,
+ onSelectionChangeShouldSetResponderCapture,
+ onStartShouldSetResponder,
+ onStartShouldSetResponderCapture,
placeholder,
placeholderTextColor,
+ pointerEvents,
returnKeyType,
secureTextEntry = false,
selection = emptyObject,
selectTextOnFocus,
spellCheck,
- value
+ testID,
+ value,
+ // unstable
+ itemID,
+ itemRef,
+ itemProp,
+ itemScope,
+ itemType,
+ unstable_ariaSet,
+ unstable_dataSet
} = props;
let type;
@@ -114,7 +148,7 @@ const TextInput = forwardRef((props, ref) => {
const hostRef = useRef(null);
const dimensions = useRef({ height: null, width: null });
const setRef = setAndForwardRef({
- getForwardedRef: () => ref,
+ getForwardedRef: () => forwardedRef,
setLocalRef: c => {
hostRef.current = c;
if (hostRef.current != null) {
@@ -124,7 +158,6 @@ const TextInput = forwardRef((props, ref) => {
});
const component = multiline ? 'textarea' : 'input';
- const supportedProps = filterSupportedProps(props);
const classList = [classes.textinput];
const style = StyleSheet.compose(
props.style,
@@ -160,7 +193,7 @@ const TextInput = forwardRef((props, ref) => {
}
function handleChange(e) {
- const { text } = e.nativeEvent;
+ const text = e.target.value;
e.nativeEvent.text = text;
handleContentSizeChange();
if (onChange) {
@@ -219,7 +252,7 @@ const TextInput = forwardRef((props, ref) => {
e.nativeEvent = { target: e.target, text: e.target.value };
onSubmitEditing(e);
}
- if (shouldBlurOnSubmit) {
+ if (shouldBlurOnSubmit && hostRef.current != null) {
// $FlowFixMe
hostRef.current.blur();
}
@@ -243,26 +276,21 @@ const TextInput = forwardRef((props, ref) => {
}
}
- useEffect(() => {
- setSelection(hostRef.current, selection);
- if (document.activeElement === hostRef.current) {
- TextInputState._currentlyFocusedNode = hostRef.current;
+ useLayoutEffect(() => {
+ const node = hostRef.current;
+ setSelection(node, selection);
+ if (document.activeElement === node) {
+ TextInputState._currentlyFocusedNode = node;
}
}, [hostRef, selection]);
useElementLayout(hostRef, onLayout);
- usePlatformMethods(hostRef, ref, classList, style, {
- clear() {
- if (hostRef.current != null) {
- hostRef.current.value = '';
- }
- },
- isFocused() {
- return hostRef.current != null && TextInputState.currentlyFocusedField() === hostRef.current;
- }
- });
+ usePlatformInputMethods(hostRef, ref, classList, style);
- Object.assign(supportedProps, {
+ return createElement(component, {
+ accessibilityLabel,
+ accessibilityRelationship,
+ accessibilityState,
autoCapitalize,
autoComplete: autoComplete || autoCompleteType || 'on',
autoCorrect: autoCorrect ? 'on' : 'off',
@@ -272,27 +300,51 @@ const TextInput = forwardRef((props, ref) => {
dir: 'auto',
disabled,
enterkeyhint: returnKeyType,
+ importantForAccessibility,
maxLength,
+ nativeID,
onBlur: handleBlur,
onChange: handleChange,
+ onContextMenu,
onFocus: handleFocus,
onKeyDown: handleKeyDown,
+ onScroll,
onSelect: handleSelectionChange,
+ onMoveShouldSetResponder,
+ onMoveShouldSetResponderCapture,
+ onResponderEnd,
+ onResponderGrant,
+ onResponderMove,
+ onResponderReject,
+ onResponderRelease,
+ onResponderStart,
+ onResponderTerminate,
+ onResponderTerminationRequest,
+ onScrollShouldSetResponder,
+ onScrollShouldSetResponderCapture,
+ onSelectionChangeShouldSetResponder,
+ onSelectionChangeShouldSetResponderCapture,
+ onStartShouldSetResponder,
+ onStartShouldSetResponderCapture,
placeholder,
+ pointerEvents,
+ testID,
readOnly: !editable,
ref: setRef,
+ rows: multiline ? numberOfLines : undefined,
spellCheck: spellCheck != null ? spellCheck : autoCorrect,
style,
- value
+ type: multiline ? undefined : type,
+ value,
+ // unstable
+ itemID,
+ itemRef,
+ itemProp,
+ itemScope,
+ itemType,
+ unstable_ariaSet,
+ unstable_dataSet
});
-
- if (multiline) {
- supportedProps.rows = numberOfLines;
- } else {
- supportedProps.type = type;
- }
-
- return createElement(component, supportedProps);
});
TextInput.displayName = 'TextInput';
diff --git a/packages/react-native-web/src/exports/TouchableHighlight/index.js b/packages/react-native-web/src/exports/TouchableHighlight/index.js
index 18c7c0f9..db3db8aa 100644
--- a/packages/react-native-web/src/exports/TouchableHighlight/index.js
+++ b/packages/react-native-web/src/exports/TouchableHighlight/index.js
@@ -291,7 +291,10 @@ const TouchableHighlight = ((createReactClass({
accessibilityHint={this.props.accessibilityHint}
accessibilityLabel={this.props.accessibilityLabel}
accessibilityRole={this.props.accessibilityRole}
- accessibilityState={this.props.accessibilityState}
+ accessibilityState={{
+ disabled: this.props.disabled,
+ ...this.props.accessibilityState
+ }}
accessible={this.props.accessible !== false}
hitSlop={this.props.hitSlop}
nativeID={this.props.nativeID}
diff --git a/packages/react-native-web/src/exports/TouchableOpacity/index.js b/packages/react-native-web/src/exports/TouchableOpacity/index.js
index 3222a303..e1833db9 100644
--- a/packages/react-native-web/src/exports/TouchableOpacity/index.js
+++ b/packages/react-native-web/src/exports/TouchableOpacity/index.js
@@ -246,7 +246,10 @@ const TouchableOpacity = ((createReactClass({
accessibilityHint={this.props.accessibilityHint}
accessibilityLabel={this.props.accessibilityLabel}
accessibilityRole={this.props.accessibilityRole}
- accessibilityState={this.props.accessibilityState}
+ accessibilityState={{
+ disabled: this.props.disabled,
+ ...this.props.accessibilityState
+ }}
accessible={this.props.accessible !== false}
hitSlop={this.props.hitSlop}
nativeID={this.props.nativeID}
diff --git a/packages/react-native-web/src/exports/TouchableWithoutFeedback/index.js b/packages/react-native-web/src/exports/TouchableWithoutFeedback/index.js
index 2091672b..e8c915cb 100644
--- a/packages/react-native-web/src/exports/TouchableWithoutFeedback/index.js
+++ b/packages/react-native-web/src/exports/TouchableWithoutFeedback/index.js
@@ -148,6 +148,11 @@ const TouchableWithoutFeedback = ((createReactClass({
}
}
+ overrides.accessibilityState = {
+ disabled: this.props.disabled,
+ ...this.props.accessibilityState
+ };
+
return (React: any).cloneElement(child, {
...overrides,
accessible: this.props.accessible !== false,
diff --git a/packages/react-native-web/src/exports/View/filterSupportedProps.js b/packages/react-native-web/src/exports/View/filterSupportedProps.js
deleted file mode 100644
index 5c23416a..00000000
--- a/packages/react-native-web/src/exports/View/filterSupportedProps.js
+++ /dev/null
@@ -1,92 +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.
- *
- * @noflow
- */
-
-const supportedProps = {
- accessibilityLabel: true,
- accessibilityLiveRegion: true,
- accessibilityRelationship: true,
- accessibilityRole: true,
- accessibilityState: true,
- accessible: true,
- children: true,
- disabled: true,
- importantForAccessibility: true,
- nativeID: true,
- onBlur: true,
- onContextMenu: true,
- onFocus: true,
- onMoveShouldSetResponder: true,
- onMoveShouldSetResponderCapture: true,
- onResponderEnd: true,
- onResponderGrant: true,
- onResponderMove: true,
- onResponderReject: true,
- onResponderRelease: true,
- onResponderStart: true,
- onResponderTerminate: true,
- onResponderTerminationRequest: true,
- onScrollShouldSetResponder: true,
- onScrollShouldSetResponderCapture: true,
- onSelectionChangeShouldSetResponder: true,
- onSelectionChangeShouldSetResponderCapture: true,
- onStartShouldSetResponder: true,
- onStartShouldSetResponderCapture: true,
- onTouchCancel: true,
- onTouchCancelCapture: true,
- onTouchEnd: true,
- onTouchEndCapture: true,
- onTouchMove: true,
- onTouchMoveCapture: true,
- onTouchStart: true,
- onTouchStartCapture: true,
- pointerEvents: true,
- style: true,
- testID: true,
- /* @platform web */
- lang: true,
- onScroll: true,
- onWheel: true,
- // keyboard events
- onKeyDown: true,
- onKeyPress: true,
- onKeyUp: true,
- // mouse events (e.g, hover effects)
- onMouseDown: true,
- onMouseEnter: true,
- onMouseLeave: true,
- onMouseMove: true,
- onMouseOver: true,
- onMouseOut: true,
- onMouseUp: true,
- // unstable escape-hatches for web
- href: true,
- itemID: true,
- itemRef: true,
- itemProp: true,
- itemScope: true,
- itemType: true,
- onClick: true,
- onClickCapture: true,
- rel: true,
- target: true
-};
-
-const filterSupportedProps = props => {
- const safeProps = {};
- for (const prop in props) {
- if (props.hasOwnProperty(prop)) {
- if (supportedProps[prop] || prop.indexOf('aria-') === 0 || prop.indexOf('data-') === 0) {
- safeProps[prop] = props[prop];
- }
- }
- }
- return safeProps;
-};
-
-export default filterSupportedProps;
diff --git a/packages/react-native-web/src/exports/View/index.js b/packages/react-native-web/src/exports/View/index.js
index f015e2e8..7abad2aa 100644
--- a/packages/react-native-web/src/exports/View/index.js
+++ b/packages/react-native-web/src/exports/View/index.js
@@ -12,7 +12,6 @@ import type { ViewProps } from './types';
import createElement from '../createElement';
import css from '../StyleSheet/css';
-import filterSupportedProps from './filterSupportedProps';
import setAndForwardRef from '../../modules/setAndForwardRef';
import useElementLayout from '../../hooks/useElementLayout';
import usePlatformMethods from '../../hooks/usePlatformMethods';
@@ -37,7 +36,73 @@ function createHitSlopElement(hitSlop) {
}
const View = forwardRef((props, ref) => {
- const { children, forwardedRef, hitSlop, onLayout } = props;
+ const {
+ accessibilityLabel,
+ accessibilityLiveRegion,
+ accessibilityRelationship,
+ accessibilityRole,
+ accessibilityState,
+ accessibilityValue,
+ forwardedRef,
+ hitSlop,
+ importantForAccessibility,
+ nativeID,
+ onBlur,
+ onContextMenu,
+ onFocus,
+ onLayout,
+ onMoveShouldSetResponder,
+ onMoveShouldSetResponderCapture,
+ onResponderEnd,
+ onResponderGrant,
+ onResponderMove,
+ onResponderReject,
+ onResponderRelease,
+ onResponderStart,
+ onResponderTerminate,
+ onResponderTerminationRequest,
+ onScrollShouldSetResponder,
+ onScrollShouldSetResponderCapture,
+ onSelectionChangeShouldSetResponder,
+ onSelectionChangeShouldSetResponderCapture,
+ onStartShouldSetResponder,
+ onStartShouldSetResponderCapture,
+ pointerEvents,
+ testID,
+ // unstable
+ onClick,
+ onClickCapture,
+ onScroll,
+ onWheel,
+ onKeyDown,
+ onKeyPress,
+ onKeyUp,
+ onMouseDown,
+ onMouseEnter,
+ onMouseLeave,
+ onMouseMove,
+ onMouseOver,
+ onMouseOut,
+ onMouseUp,
+ onTouchCancel,
+ onTouchCancelCapture,
+ onTouchEnd,
+ onTouchEndCapture,
+ onTouchMove,
+ onTouchMoveCapture,
+ onTouchStart,
+ onTouchStartCapture,
+ href,
+ itemID,
+ itemRef,
+ itemProp,
+ itemScope,
+ itemType,
+ rel,
+ target,
+ unstable_ariaSet,
+ unstable_dataSet
+ } = props;
if (process.env.NODE_ENV !== 'production') {
React.Children.toArray(props.children).forEach(item => {
@@ -56,6 +121,9 @@ const View = forwardRef((props, ref) => {
}
});
+ const children = hitSlop
+ ? React.Children.toArray([createHitSlopElement(hitSlop), props.children])
+ : props.children;
const classList = [classes.view];
const style = StyleSheet.compose(
hasTextAncestor && styles.inline,
@@ -65,15 +133,74 @@ const View = forwardRef((props, ref) => {
useElementLayout(hostRef, onLayout);
usePlatformMethods(hostRef, ref, classList, style);
- const supportedProps = filterSupportedProps(props);
- supportedProps.children = hitSlop
- ? React.Children.toArray([createHitSlopElement(hitSlop), children])
- : children;
- supportedProps.classList = classList;
- supportedProps.ref = setRef;
- supportedProps.style = style;
-
- return createElement('div', supportedProps);
+ return createElement('div', {
+ accessibilityLabel,
+ accessibilityLiveRegion,
+ accessibilityRelationship,
+ accessibilityRole,
+ accessibilityState,
+ accessibilityValue,
+ children,
+ classList,
+ importantForAccessibility,
+ nativeID,
+ onBlur,
+ onContextMenu,
+ onFocus,
+ onMoveShouldSetResponder,
+ onMoveShouldSetResponderCapture,
+ onResponderEnd,
+ onResponderGrant,
+ onResponderMove,
+ onResponderReject,
+ onResponderRelease,
+ onResponderStart,
+ onResponderTerminate,
+ onResponderTerminationRequest,
+ onScrollShouldSetResponder,
+ onScrollShouldSetResponderCapture,
+ onSelectionChangeShouldSetResponder,
+ onSelectionChangeShouldSetResponderCapture,
+ onStartShouldSetResponder,
+ onStartShouldSetResponderCapture,
+ pointerEvents,
+ ref: setRef,
+ style,
+ testID,
+ // unstable
+ onClick,
+ onClickCapture,
+ onScroll,
+ onWheel,
+ onKeyDown,
+ onKeyPress,
+ onKeyUp,
+ onMouseDown,
+ onMouseEnter,
+ onMouseLeave,
+ onMouseMove,
+ onMouseOver,
+ onMouseOut,
+ onMouseUp,
+ onTouchCancel,
+ onTouchCancelCapture,
+ onTouchEnd,
+ onTouchEndCapture,
+ onTouchMove,
+ onTouchMoveCapture,
+ onTouchStart,
+ onTouchStartCapture,
+ href,
+ itemID,
+ itemRef,
+ itemProp,
+ itemScope,
+ itemType,
+ rel,
+ target,
+ unstable_ariaSet,
+ unstable_dataSet
+ });
});
View.displayName = 'View';
diff --git a/packages/react-native-web/src/exports/View/types.js b/packages/react-native-web/src/exports/View/types.js
index 853c2520..552fbaed 100644
--- a/packages/react-native-web/src/exports/View/types.js
+++ b/packages/react-native-web/src/exports/View/types.js
@@ -83,6 +83,12 @@ export type ViewProps = {
required?: ?boolean,
selected?: ?boolean
},
+ accessibilityValue?: {
+ max?: ?number,
+ min?: ?number,
+ now?: ?number,
+ text?: ?string
+ },
accessible?: boolean,
children?: any,
forwardedRef?: any,
@@ -90,20 +96,43 @@ export type ViewProps = {
importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants',
nativeID?: string,
onBlur?: (e: any) => void,
- onClick?: (e: any) => void,
- onClickCapture?: (e: any) => void,
onFocus?: (e: any) => void,
onLayout?: (e: LayoutEvent) => void,
+ onMoveShouldSetResponder?: (e: any) => boolean,
+ onMoveShouldSetResponderCapture?: (e: any) => boolean,
+ onResponderEnd?: (e: any) => void,
onResponderGrant?: (e: any) => void,
onResponderMove?: (e: any) => void,
onResponderReject?: (e: any) => void,
onResponderRelease?: (e: any) => void,
+ onResponderStart?: (e: any) => void,
onResponderTerminate?: (e: any) => void,
onResponderTerminationRequest?: (e: any) => void,
+ onScrollShouldSetResponder?: (e: any) => boolean,
+ onScrollShouldSetResponderCapture?: (e: any) => boolean,
+ onSelectionChangeShouldSetResponder?: (e: any) => boolean,
+ onSelectionChangeShouldSetResponderCapture?: (e: any) => boolean,
onStartShouldSetResponder?: (e: any) => boolean,
onStartShouldSetResponderCapture?: (e: any) => boolean,
- onMoveShouldSetResponder?: (e: any) => boolean,
- onMoveShouldSetResponderCapture?: (e: any) => boolean,
+ pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto',
+ style?: GenericStyleProp,
+ 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,
+ onMouseLeave?: (e: any) => void,
+ onMouseMove?: (e: any) => void,
+ onMouseOver?: (e: any) => void,
+ onMouseOut?: (e: any) => void,
+ onMouseUp?: (e: any) => void,
onTouchCancel?: (e: any) => void,
onTouchCancelCapture?: (e: any) => void,
onTouchEnd?: (e: any) => void,
@@ -112,14 +141,14 @@ export type ViewProps = {
onTouchMoveCapture?: (e: any) => void,
onTouchStart?: (e: any) => void,
onTouchStartCapture?: (e: any) => void,
- pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto',
- style?: GenericStyleProp,
- testID?: string,
- // web extensions
- onContextMenu?: (e: any) => void,
+ href?: string,
itemID?: string,
itemRef?: string,
itemProp?: string,
itemScope?: string,
- itemType?: string
+ itemType?: string,
+ rel?: string,
+ target?: string,
+ unstable_ariaSet?: Object,
+ unstable_dataSet?: Object
};
diff --git a/packages/react-native-web/src/hooks/usePlatformMethods.js b/packages/react-native-web/src/hooks/usePlatformMethods.js
index 13c85eda..f5564bc4 100644
--- a/packages/react-native-web/src/hooks/usePlatformMethods.js
+++ b/packages/react-native-web/src/hooks/usePlatformMethods.js
@@ -13,6 +13,34 @@ import type { ElementRef } from 'react';
import UIManager from '../exports/UIManager';
import createDOMProps from '../modules/createDOMProps';
import { useImperativeHandle, useRef } from 'react';
+import TextInputState from '../modules/TextInputState';
+
+function setNativeProps(node, nativeProps, classList, style, previousStyle) {
+ if (node != null && nativeProps) {
+ const domProps = createDOMProps(null, {
+ ...nativeProps,
+ classList: [nativeProps.className, classList],
+ style: [style, nativeProps.style]
+ });
+
+ const nextDomStyle = domProps.style;
+
+ if (previousStyle.current != null) {
+ if (domProps.style == null) {
+ domProps.style = {};
+ }
+ for (const styleName in previousStyle.current) {
+ if (domProps.style[styleName] == null) {
+ domProps.style[styleName] = '';
+ }
+ }
+ }
+
+ previousStyle.current = nextDomStyle;
+
+ UIManager.updateView(node, domProps);
+ }
+}
export default function usePlatformMethods(
hostRef: ElementRef,
@@ -26,52 +54,74 @@ export default function usePlatformMethods(
useImperativeHandle(
ref,
() => {
+ const hostNode = hostRef.current;
return {
blur() {
- UIManager.blur(hostRef.current);
+ UIManager.blur(hostNode);
},
focus() {
- UIManager.focus(hostRef.current);
+ UIManager.focus(hostNode);
},
measure(callback) {
- UIManager.measure(hostRef.current, callback);
+ UIManager.measure(hostNode, callback);
},
measureLayout(relativeToNativeNode, onFail, onSuccess) {
- UIManager.measureLayout(hostRef.current, relativeToNativeNode, onFail, onSuccess);
+ UIManager.measureLayout(hostNode, relativeToNativeNode, onFail, onSuccess);
},
measureInWindow(callback) {
- UIManager.measureInWindow(hostRef.current, callback);
+ UIManager.measureInWindow(hostNode, callback);
},
setNativeProps(nativeProps) {
- const node = hostRef.current;
- if (node && nativeProps) {
- const domProps = createDOMProps(null, {
- ...nativeProps,
- classList: [nativeProps.className, classList],
- style: [style, nativeProps.style]
- });
-
- const nextDomStyle = domProps.style;
-
- if (previousStyle.current != null) {
- if (domProps.style == null) {
- domProps.style = {};
- }
- for (const styleName in previousStyle.current) {
- if (domProps.style[styleName] == null) {
- domProps.style[styleName] = '';
- }
- }
- }
-
- previousStyle.current = nextDomStyle;
-
- UIManager.updateView(node, domProps);
- }
- },
- ...extras
+ setNativeProps(hostNode, nativeProps, classList, style, previousStyle);
+ }
};
},
- [classList, hostRef, style, extras]
+ [classList, hostRef, style]
+ );
+}
+
+export function usePlatformInputMethods(
+ hostRef: ElementRef,
+ ref: ElementRef,
+ classList: Array,
+ style: GenericStyleProp,
+ extras: any
+) {
+ const previousStyle = useRef(null);
+
+ useImperativeHandle(
+ ref,
+ () => {
+ const hostNode = hostRef.current;
+ return {
+ blur() {
+ UIManager.blur(hostNode);
+ },
+ clear() {
+ if (hostNode != null) {
+ hostNode.value = '';
+ }
+ },
+ focus() {
+ UIManager.focus(hostNode);
+ },
+ isFocused() {
+ return hostNode != null && TextInputState.currentlyFocusedField() === hostNode;
+ },
+ measure(callback) {
+ UIManager.measure(hostNode, callback);
+ },
+ measureLayout(relativeToNativeNode, onFail, onSuccess) {
+ UIManager.measureLayout(hostNode, relativeToNativeNode, onFail, onSuccess);
+ },
+ measureInWindow(callback) {
+ UIManager.measureInWindow(hostNode, callback);
+ },
+ setNativeProps(nativeProps) {
+ setNativeProps(hostNode, nativeProps, classList, style, previousStyle);
+ }
+ };
+ },
+ [classList, hostRef, style]
);
}
diff --git a/packages/react-native-web/src/modules/AccessibilityUtil/__tests__/propsToAccessibilityComponent.js b/packages/react-native-web/src/modules/AccessibilityUtil/__tests__/propsToAccessibilityComponent-test.js
similarity index 87%
rename from packages/react-native-web/src/modules/AccessibilityUtil/__tests__/propsToAccessibilityComponent.js
rename to packages/react-native-web/src/modules/AccessibilityUtil/__tests__/propsToAccessibilityComponent-test.js
index f7386ce9..8ac3cd4e 100644
--- a/packages/react-native-web/src/modules/AccessibilityUtil/__tests__/propsToAccessibilityComponent.js
+++ b/packages/react-native-web/src/modules/AccessibilityUtil/__tests__/propsToAccessibilityComponent-test.js
@@ -17,7 +17,10 @@ describe('modules/AccessibilityUtil/propsToAccessibilityComponent', () => {
test('when "accessibilityRole" is "heading" and "aria-level" is set', () => {
expect(
- propsToAccessibilityComponent({ accessibilityRole: 'heading', 'aria-level': 3 })
+ propsToAccessibilityComponent({
+ accessibilityRole: 'heading',
+ unstable_ariaSet: { level: 3 }
+ })
).toEqual('h3');
});
diff --git a/packages/react-native-web/src/modules/AccessibilityUtil/propsToAccessibilityComponent.js b/packages/react-native-web/src/modules/AccessibilityUtil/propsToAccessibilityComponent.js
index 85a068f5..5144a039 100644
--- a/packages/react-native-web/src/modules/AccessibilityUtil/propsToAccessibilityComponent.js
+++ b/packages/react-native-web/src/modules/AccessibilityUtil/propsToAccessibilityComponent.js
@@ -34,8 +34,12 @@ const propsToAccessibilityComponent = (props: Object = emptyObject) => {
const role = propsToAriaRole(props);
if (role) {
if (role === 'heading') {
- const level = props['aria-level'] || 1;
- return `h${level}`;
+ const ariaSet = props.unstable_ariaSet;
+ if (ariaSet != null && ariaSet.level) {
+ const level = ariaSet.level;
+ return `h${level}`;
+ }
+ return 'h1';
}
return roleComponents[role];
}
diff --git a/packages/react-native-web/src/modules/createDOMProps/index.js b/packages/react-native-web/src/modules/createDOMProps/index.js
index 2e0980dd..4d2486f4 100644
--- a/packages/react-native-web/src/modules/createDOMProps/index.js
+++ b/packages/react-native-web/src/modules/createDOMProps/index.js
@@ -14,6 +14,7 @@ import styleResolver from '../../exports/StyleSheet/styleResolver';
import { STYLE_GROUPS } from '../../exports/StyleSheet/constants';
const emptyObject = {};
+const hasOwnProperty = Object.prototype.hasOwnProperty;
// Reset styles for heading, link, and list DOM elements
const classes = css.create(
@@ -65,8 +66,8 @@ const createDOMProps = (component, props, styleResolver) => {
accessibilityLiveRegion,
accessibilityRelationship,
accessibilityState,
+ accessibilityValue,
classList,
- className: deprecatedClassName,
disabled: providedDisabled,
importantForAccessibility,
nativeID,
@@ -77,6 +78,8 @@ const createDOMProps = (component, props, styleResolver) => {
accessible,
accessibilityRole,
/* eslint-enable */
+ unstable_ariaSet,
+ unstable_dataSet,
...domProps
} = props;
@@ -84,6 +87,30 @@ const createDOMProps = (component, props, styleResolver) => {
(accessibilityState != null && accessibilityState.disabled === true) || providedDisabled;
const role = AccessibilityUtil.propsToAriaRole(props);
+ // unstable_ariaSet
+ if (unstable_ariaSet != null) {
+ for (const prop in unstable_ariaSet) {
+ if (hasOwnProperty.call(unstable_ariaSet, prop)) {
+ const value = unstable_ariaSet[prop];
+ if (value != null) {
+ domProps[`aria-${prop}`] = value;
+ }
+ }
+ }
+ }
+
+ // unstable_dataSet
+ if (unstable_dataSet != null) {
+ for (const prop in unstable_dataSet) {
+ if (hasOwnProperty.call(unstable_dataSet, prop)) {
+ const value = unstable_dataSet[prop];
+ if (value != null) {
+ domProps[`data-${prop}`] = value;
+ }
+ }
+ }
+ }
+
// accessibilityLabel
if (accessibilityLabel != null) {
domProps['aria-label'] = accessibilityLabel;
@@ -126,6 +153,17 @@ const createDOMProps = (component, props, styleResolver) => {
}
}
}
+
+ // accessibilityValue
+ if (accessibilityValue != null) {
+ for (const prop in accessibilityValue) {
+ const value = accessibilityValue[prop];
+ if (value != null) {
+ domProps[`aria-value${prop}`] = value;
+ }
+ }
+ }
+
// legacy fallbacks
if (importantForAccessibility === 'no-hide-descendants') {
domProps['aria-hidden'] = true;
@@ -182,12 +220,7 @@ const createDOMProps = (component, props, styleResolver) => {
component === 'ul' ||
role === 'heading';
// Classic CSS styles
- const finalClassList = [
- deprecatedClassName,
- needsReset && classes.reset,
- needsCursor && classes.cursor,
- classList
- ];
+ const finalClassList = [needsReset && classes.reset, needsCursor && classes.cursor, classList];
// Resolve styles
const { className, style } = styleResolver(reactNativeStyle, finalClassList);
@@ -202,7 +235,7 @@ const createDOMProps = (component, props, styleResolver) => {
// OTHER
// Native element ID
- if (nativeID && nativeID.constructor === String) {
+ if (nativeID != null) {
domProps.id = nativeID;
}
@@ -214,7 +247,7 @@ const createDOMProps = (component, props, styleResolver) => {
domProps.rel = `${domProps.rel || ''} noopener noreferrer`;
}
// Automated test IDs
- if (testID && testID.constructor === String) {
+ if (testID != null) {
domProps['data-testid'] = testID;
}