mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add web compat unit tests and fixes (#35316)
Summary: Adds compat with W3C logical CSS properties. See https://github.com/facebook/react-native/issues/34425 This is a replacement for reverted https://github.com/facebook/react-native/issues/34590, which can no longer be imported internally. ## Changelog [General][Added] - Added CSS logical properties. Pull Request resolved: https://github.com/facebook/react-native/pull/35316 Test Plan: Unit test snapshots. Reviewed By: NickGerleman Differential Revision: D41230978 Pulled By: necolas fbshipit-source-id: 40e93d0d697f0cb28390480ce2b4bcbce18da70a
This commit is contained in:
committed by
Facebook GitHub Bot
parent
04c286c3c2
commit
3681df2878
@@ -8,6 +8,7 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {AccessibilityRole} from '../../Components/View/ViewAccessibility';
|
||||
import type {
|
||||
MeasureInWindowOnSuccessCallback,
|
||||
MeasureLayoutOnSuccessCallback,
|
||||
@@ -21,7 +22,6 @@ import dismissKeyboard from '../../Utilities/dismissKeyboard';
|
||||
import Platform from '../../Utilities/Platform';
|
||||
import StatusBar from '../StatusBar/StatusBar';
|
||||
import View from '../View/View';
|
||||
import type {AccessibilityRole} from '../../Components/View/ViewAccessibility';
|
||||
import AndroidDrawerLayoutNativeComponent, {
|
||||
Commands,
|
||||
} from './AndroidDrawerLayoutNativeComponent';
|
||||
|
||||
@@ -1068,6 +1068,18 @@ const emptyFunctionThatReturnsTrue = () => true;
|
||||
*
|
||||
*/
|
||||
function InternalTextInput(props: Props): React.Node {
|
||||
const {
|
||||
'aria-busy': ariaBusy,
|
||||
'aria-checked': ariaChecked,
|
||||
'aria-disabled': ariaDisabled,
|
||||
'aria-expanded': ariaExpanded,
|
||||
'aria-selected': ariaSelected,
|
||||
accessibilityState,
|
||||
id,
|
||||
tabIndex,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const inputRef = useRef<null | React.ElementRef<HostComponent<mixed>>>(null);
|
||||
|
||||
// Android sends a "onTextChanged" event followed by a "onSelectionChanged" event, for
|
||||
@@ -1388,13 +1400,25 @@ function InternalTextInput(props: Props): React.Node {
|
||||
// so omitting onBlur and onFocus pressability handlers here.
|
||||
const {onBlur, onFocus, ...eventHandlers} = usePressability(config) || {};
|
||||
|
||||
const _accessibilityState = {
|
||||
busy: props['aria-busy'] ?? props.accessibilityState?.busy,
|
||||
checked: props['aria-checked'] ?? props.accessibilityState?.checked,
|
||||
disabled: props['aria-disabled'] ?? props.accessibilityState?.disabled,
|
||||
expanded: props['aria-expanded'] ?? props.accessibilityState?.expanded,
|
||||
selected: props['aria-selected'] ?? props.accessibilityState?.selected,
|
||||
};
|
||||
let _accessibilityState;
|
||||
if (
|
||||
accessibilityState != null ||
|
||||
ariaBusy != null ||
|
||||
ariaChecked != null ||
|
||||
ariaDisabled != null ||
|
||||
ariaExpanded != null ||
|
||||
ariaSelected != null
|
||||
) {
|
||||
_accessibilityState = {
|
||||
busy: ariaBusy ?? accessibilityState?.busy,
|
||||
checked: ariaChecked ?? accessibilityState?.checked,
|
||||
disabled: ariaDisabled ?? accessibilityState?.disabled,
|
||||
expanded: ariaExpanded ?? accessibilityState?.expanded,
|
||||
selected: ariaSelected ?? accessibilityState?.selected,
|
||||
};
|
||||
}
|
||||
|
||||
let style = flattenStyle(props.style);
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
const RCTTextInputView =
|
||||
@@ -1402,10 +1426,7 @@ function InternalTextInput(props: Props): React.Node {
|
||||
? RCTMultilineTextInputView
|
||||
: RCTSinglelineTextInputView;
|
||||
|
||||
const style =
|
||||
props.multiline === true
|
||||
? StyleSheet.flatten([styles.multilineInput, props.style])
|
||||
: props.style;
|
||||
style = props.multiline === true ? [styles.multilineInput, style] : style;
|
||||
|
||||
const useOnChangeSync =
|
||||
(props.unstable_onChangeSync || props.unstable_onChangeTextSync) &&
|
||||
@@ -1415,15 +1436,16 @@ function InternalTextInput(props: Props): React.Node {
|
||||
<RCTTextInputView
|
||||
// $FlowFixMe[incompatible-type] - Figure out imperative + forward refs.
|
||||
ref={ref}
|
||||
{...props}
|
||||
{...otherProps}
|
||||
{...eventHandlers}
|
||||
accessible={accessible}
|
||||
accessibilityState={_accessibilityState}
|
||||
accessible={accessible}
|
||||
submitBehavior={submitBehavior}
|
||||
caretHidden={caretHidden}
|
||||
dataDetectorTypes={props.dataDetectorTypes}
|
||||
focusable={focusable}
|
||||
focusable={tabIndex !== undefined ? !tabIndex : focusable}
|
||||
mostRecentEventCount={mostRecentEventCount}
|
||||
nativeID={id ?? props.nativeID}
|
||||
onBlur={_onBlur}
|
||||
onKeyPressSync={props.unstable_onKeyPressSync}
|
||||
onChange={_onChange}
|
||||
@@ -1439,7 +1461,6 @@ function InternalTextInput(props: Props): React.Node {
|
||||
/>
|
||||
);
|
||||
} else if (Platform.OS === 'android') {
|
||||
const style = [props.style];
|
||||
const autoCapitalize = props.autoCapitalize || 'sentences';
|
||||
const _accessibilityLabelledBy =
|
||||
props?.['aria-labelledby'] ?? props?.accessibilityLabelledBy;
|
||||
@@ -1466,18 +1487,19 @@ function InternalTextInput(props: Props): React.Node {
|
||||
<AndroidTextInput
|
||||
// $FlowFixMe[incompatible-type] - Figure out imperative + forward refs.
|
||||
ref={ref}
|
||||
{...props}
|
||||
{...otherProps}
|
||||
{...eventHandlers}
|
||||
accessible={accessible}
|
||||
accessibilityState={_accessibilityState}
|
||||
accessibilityLabelledBy={_accessibilityLabelledBy}
|
||||
accessible={accessible}
|
||||
autoCapitalize={autoCapitalize}
|
||||
submitBehavior={submitBehavior}
|
||||
caretHidden={caretHidden}
|
||||
children={children}
|
||||
disableFullscreenUI={props.disableFullscreenUI}
|
||||
focusable={focusable}
|
||||
focusable={tabIndex !== undefined ? !tabIndex : focusable}
|
||||
mostRecentEventCount={mostRecentEventCount}
|
||||
nativeID={id ?? props.nativeID}
|
||||
numberOfLines={props.rows ?? props.numberOfLines}
|
||||
onBlur={_onBlur}
|
||||
onChange={_onChange}
|
||||
@@ -1606,11 +1628,12 @@ const ExportedForwardRef: React.AbstractComponent<
|
||||
},
|
||||
forwardedRef: ReactRefSetter<TextInputInstance>,
|
||||
) {
|
||||
const style = flattenStyle(restProps.style);
|
||||
let style = flattenStyle(restProps.style);
|
||||
|
||||
if (style?.verticalAlign != null) {
|
||||
style.textAlignVertical =
|
||||
verticalAlignToTextAlignVerticalMap[style.verticalAlign];
|
||||
delete style.verticalAlign;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -1650,6 +1673,8 @@ const ExportedForwardRef: React.AbstractComponent<
|
||||
);
|
||||
});
|
||||
|
||||
ExportedForwardRef.displayName = 'TextInput';
|
||||
|
||||
/**
|
||||
* Switch to `deprecated-react-native-prop-types` for compatibility with future
|
||||
* releases. This is deprecated and will be removed in the future.
|
||||
|
||||
@@ -179,3 +179,269 @@ describe('TextInput tests', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TextInput', () => {
|
||||
it('default render', () => {
|
||||
const instance = ReactTestRenderer.create(<TextInput />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTSinglelineTextInputView
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
focusable={true}
|
||||
forwardedRef={null}
|
||||
mostRecentEventCount={0}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onChangeSync={null}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onScroll={[Function]}
|
||||
onSelectionChange={[Function]}
|
||||
onSelectionChangeShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
rejectResponderTermination={true}
|
||||
selection={null}
|
||||
submitBehavior="blurAndSubmit"
|
||||
text=""
|
||||
underlineColorAndroid="transparent"
|
||||
/>
|
||||
`);
|
||||
});
|
||||
|
||||
it('has displayName', () => {
|
||||
expect(TextInput.displayName).toEqual('TextInput');
|
||||
});
|
||||
});
|
||||
|
||||
describe('TextInput compat with web', () => {
|
||||
it('renders core props', () => {
|
||||
const props = {
|
||||
id: 'id',
|
||||
tabIndex: 0,
|
||||
testID: 'testID',
|
||||
};
|
||||
|
||||
const instance = ReactTestRenderer.create(<TextInput {...props} />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTSinglelineTextInputView
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
focusable={true}
|
||||
forwardedRef={null}
|
||||
mostRecentEventCount={0}
|
||||
nativeID="id"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onChangeSync={null}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onScroll={[Function]}
|
||||
onSelectionChange={[Function]}
|
||||
onSelectionChangeShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
rejectResponderTermination={true}
|
||||
selection={null}
|
||||
submitBehavior="blurAndSubmit"
|
||||
testID="testID"
|
||||
text=""
|
||||
underlineColorAndroid="transparent"
|
||||
/>
|
||||
`);
|
||||
});
|
||||
|
||||
it('renders "aria-*" props', () => {
|
||||
const props = {
|
||||
'aria-activedescendant': 'activedescendant',
|
||||
'aria-atomic': true,
|
||||
'aria-autocomplete': 'list',
|
||||
'aria-busy': true,
|
||||
'aria-checked': true,
|
||||
'aria-columncount': 5,
|
||||
'aria-columnindex': 3,
|
||||
'aria-columnspan': 2,
|
||||
'aria-controls': 'controls',
|
||||
'aria-current': 'current',
|
||||
'aria-describedby': 'describedby',
|
||||
'aria-details': 'details',
|
||||
'aria-disabled': true,
|
||||
'aria-errormessage': 'errormessage',
|
||||
'aria-expanded': true,
|
||||
'aria-flowto': 'flowto',
|
||||
'aria-haspopup': true,
|
||||
'aria-hidden': true,
|
||||
'aria-invalid': true,
|
||||
'aria-keyshortcuts': 'Cmd+S',
|
||||
'aria-label': 'label',
|
||||
'aria-labelledby': 'labelledby',
|
||||
'aria-level': 3,
|
||||
'aria-live': 'polite',
|
||||
'aria-modal': true,
|
||||
'aria-multiline': true,
|
||||
'aria-multiselectable': true,
|
||||
'aria-orientation': 'portrait',
|
||||
'aria-owns': 'owns',
|
||||
'aria-placeholder': 'placeholder',
|
||||
'aria-posinset': 5,
|
||||
'aria-pressed': true,
|
||||
'aria-readonly': true,
|
||||
'aria-required': true,
|
||||
role: 'main',
|
||||
'aria-roledescription': 'roledescription',
|
||||
'aria-rowcount': 5,
|
||||
'aria-rowindex': 3,
|
||||
'aria-rowspan': 3,
|
||||
'aria-selected': true,
|
||||
'aria-setsize': 5,
|
||||
'aria-sort': 'ascending',
|
||||
'aria-valuemax': 5,
|
||||
'aria-valuemin': 0,
|
||||
'aria-valuenow': 3,
|
||||
'aria-valuetext': '3',
|
||||
};
|
||||
|
||||
const instance = ReactTestRenderer.create(<TextInput {...props} />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTSinglelineTextInputView
|
||||
accessibilityState={
|
||||
Object {
|
||||
"busy": true,
|
||||
"checked": true,
|
||||
"disabled": true,
|
||||
"expanded": true,
|
||||
"selected": true,
|
||||
}
|
||||
}
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
aria-activedescendant="activedescendant"
|
||||
aria-atomic={true}
|
||||
aria-autocomplete="list"
|
||||
aria-columncount={5}
|
||||
aria-columnindex={3}
|
||||
aria-columnspan={2}
|
||||
aria-controls="controls"
|
||||
aria-current="current"
|
||||
aria-describedby="describedby"
|
||||
aria-details="details"
|
||||
aria-errormessage="errormessage"
|
||||
aria-flowto="flowto"
|
||||
aria-haspopup={true}
|
||||
aria-hidden={true}
|
||||
aria-invalid={true}
|
||||
aria-keyshortcuts="Cmd+S"
|
||||
aria-label="label"
|
||||
aria-labelledby="labelledby"
|
||||
aria-level={3}
|
||||
aria-live="polite"
|
||||
aria-modal={true}
|
||||
aria-multiline={true}
|
||||
aria-multiselectable={true}
|
||||
aria-orientation="portrait"
|
||||
aria-owns="owns"
|
||||
aria-placeholder="placeholder"
|
||||
aria-posinset={5}
|
||||
aria-pressed={true}
|
||||
aria-readonly={true}
|
||||
aria-required={true}
|
||||
aria-roledescription="roledescription"
|
||||
aria-rowcount={5}
|
||||
aria-rowindex={3}
|
||||
aria-rowspan={3}
|
||||
aria-setsize={5}
|
||||
aria-sort="ascending"
|
||||
aria-valuemax={5}
|
||||
aria-valuemin={0}
|
||||
aria-valuenow={3}
|
||||
aria-valuetext="3"
|
||||
focusable={true}
|
||||
forwardedRef={null}
|
||||
mostRecentEventCount={0}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onChangeSync={null}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onScroll={[Function]}
|
||||
onSelectionChange={[Function]}
|
||||
onSelectionChangeShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
rejectResponderTermination={true}
|
||||
role="main"
|
||||
selection={null}
|
||||
submitBehavior="blurAndSubmit"
|
||||
text=""
|
||||
underlineColorAndroid="transparent"
|
||||
/>
|
||||
`);
|
||||
});
|
||||
|
||||
it('renders styles', () => {
|
||||
const style = {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
backgroundColor: 'white',
|
||||
marginInlineStart: 10,
|
||||
userSelect: 'none',
|
||||
verticalAlign: 'middle',
|
||||
};
|
||||
|
||||
const instance = ReactTestRenderer.create(<TextInput style={style} />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTSinglelineTextInputView
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
focusable={true}
|
||||
forwardedRef={null}
|
||||
mostRecentEventCount={0}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onChangeSync={null}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onScroll={[Function]}
|
||||
onSelectionChange={[Function]}
|
||||
onSelectionChangeShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
rejectResponderTermination={true}
|
||||
selection={null}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "white",
|
||||
"display": "flex",
|
||||
"flex": 1,
|
||||
"marginInlineStart": 10,
|
||||
"textAlignVertical": "center",
|
||||
"userSelect": "none",
|
||||
}
|
||||
}
|
||||
submitBehavior="blurAndSubmit"
|
||||
text=""
|
||||
underlineColorAndroid="transparent"
|
||||
/>
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,15 +2,6 @@
|
||||
|
||||
exports[`TextInput tests should render as expected: should deep render when mocked (please verify output manually) 1`] = `
|
||||
<RCTSinglelineTextInputView
|
||||
accessibilityState={
|
||||
Object {
|
||||
"busy": undefined,
|
||||
"checked": undefined,
|
||||
"disabled": undefined,
|
||||
"expanded": undefined,
|
||||
"selected": undefined,
|
||||
}
|
||||
}
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
focusable={true}
|
||||
@@ -40,15 +31,6 @@ exports[`TextInput tests should render as expected: should deep render when mock
|
||||
|
||||
exports[`TextInput tests should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
|
||||
<RCTSinglelineTextInputView
|
||||
accessibilityState={
|
||||
Object {
|
||||
"busy": undefined,
|
||||
"checked": undefined,
|
||||
"disabled": undefined,
|
||||
"expanded": undefined,
|
||||
"selected": undefined,
|
||||
}
|
||||
}
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
focusable={true}
|
||||
@@ -76,6 +58,6 @@ exports[`TextInput tests should render as expected: should deep render when not
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`TextInput tests should render as expected: should shallow render as <TextInput /> when mocked 1`] = `<ForwardRef(TextInput) />`;
|
||||
exports[`TextInput tests should render as expected: should shallow render as <TextInput /> when mocked 1`] = `<TextInput />`;
|
||||
|
||||
exports[`TextInput tests should render as expected: should shallow render as <TextInput /> when not mocked 1`] = `<ForwardRef(TextInput) />`;
|
||||
exports[`TextInput tests should render as expected: should shallow render as <TextInput /> when not mocked 1`] = `<TextInput />`;
|
||||
|
||||
@@ -57,7 +57,6 @@ const View: React.AbstractComponent<
|
||||
nativeID,
|
||||
pointerEvents,
|
||||
role,
|
||||
style,
|
||||
tabIndex,
|
||||
...otherProps
|
||||
}: ViewProps,
|
||||
@@ -66,23 +65,42 @@ const View: React.AbstractComponent<
|
||||
const _accessibilityLabelledBy =
|
||||
ariaLabelledBy?.split(/\s*,\s*/g) ?? accessibilityLabelledBy;
|
||||
|
||||
const _accessibilityState = {
|
||||
busy: ariaBusy ?? accessibilityState?.busy,
|
||||
checked: ariaChecked ?? accessibilityState?.checked,
|
||||
disabled: ariaDisabled ?? accessibilityState?.disabled,
|
||||
expanded: ariaExpanded ?? accessibilityState?.expanded,
|
||||
selected: ariaSelected ?? accessibilityState?.selected,
|
||||
};
|
||||
let _accessibilityState;
|
||||
if (
|
||||
accessibilityState != null ||
|
||||
ariaBusy != null ||
|
||||
ariaChecked != null ||
|
||||
ariaDisabled != null ||
|
||||
ariaExpanded != null ||
|
||||
ariaSelected != null
|
||||
) {
|
||||
_accessibilityState = {
|
||||
busy: ariaBusy ?? accessibilityState?.busy,
|
||||
checked: ariaChecked ?? accessibilityState?.checked,
|
||||
disabled: ariaDisabled ?? accessibilityState?.disabled,
|
||||
expanded: ariaExpanded ?? accessibilityState?.expanded,
|
||||
selected: ariaSelected ?? accessibilityState?.selected,
|
||||
};
|
||||
}
|
||||
let _accessibilityValue;
|
||||
if (
|
||||
accessibilityValue != null ||
|
||||
ariaValueMax != null ||
|
||||
ariaValueMin != null ||
|
||||
ariaValueNow != null ||
|
||||
ariaValueText != null
|
||||
) {
|
||||
_accessibilityValue = {
|
||||
max: ariaValueMax ?? accessibilityValue?.max,
|
||||
min: ariaValueMin ?? accessibilityValue?.min,
|
||||
now: ariaValueNow ?? accessibilityValue?.now,
|
||||
text: ariaValueText ?? accessibilityValue?.text,
|
||||
};
|
||||
}
|
||||
|
||||
const _accessibilityValue = {
|
||||
max: ariaValueMax ?? accessibilityValue?.max,
|
||||
min: ariaValueMin ?? accessibilityValue?.min,
|
||||
now: ariaValueNow ?? accessibilityValue?.now,
|
||||
text: ariaValueText ?? accessibilityValue?.text,
|
||||
};
|
||||
let style = flattenStyle(otherProps.style);
|
||||
|
||||
const flattenedStyle = flattenStyle(style);
|
||||
const newPointerEvents = flattenedStyle?.pointerEvents || pointerEvents;
|
||||
const newPointerEvents = style?.pointerEvents || pointerEvents;
|
||||
|
||||
return (
|
||||
<TextAncestor.Provider value={false}>
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const render = require('../../../../jest/renderer');
|
||||
const React = require('../React');
|
||||
const View = require('../View');
|
||||
|
||||
jest.unmock('../View');
|
||||
jest.unmock('../ViewNativeComponent');
|
||||
|
||||
describe('View', () => {
|
||||
it('default render', () => {
|
||||
const instance = render.create(<View />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`<RCTView />`);
|
||||
});
|
||||
|
||||
it('has displayName', () => {
|
||||
expect(View.displayName).toEqual('View');
|
||||
});
|
||||
});
|
||||
|
||||
describe('View compat with web', () => {
|
||||
it('renders core props', () => {
|
||||
const props = {
|
||||
id: 'id',
|
||||
tabIndex: 0,
|
||||
testID: 'testID',
|
||||
};
|
||||
|
||||
const instance = render.create(<View {...props} />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTView
|
||||
focusable={true}
|
||||
nativeID="id"
|
||||
testID="testID"
|
||||
/>
|
||||
`);
|
||||
});
|
||||
|
||||
it('renders "aria-*" props', () => {
|
||||
const props = {
|
||||
'aria-activedescendant': 'activedescendant',
|
||||
'aria-atomic': true,
|
||||
'aria-autocomplete': 'list',
|
||||
'aria-busy': true,
|
||||
'aria-checked': true,
|
||||
'aria-columncount': 5,
|
||||
'aria-columnindex': 3,
|
||||
'aria-columnspan': 2,
|
||||
'aria-controls': 'controls',
|
||||
'aria-current': 'current',
|
||||
'aria-describedby': 'describedby',
|
||||
'aria-details': 'details',
|
||||
'aria-disabled': true,
|
||||
'aria-errormessage': 'errormessage',
|
||||
'aria-expanded': true,
|
||||
'aria-flowto': 'flowto',
|
||||
'aria-haspopup': true,
|
||||
'aria-hidden': true,
|
||||
'aria-invalid': true,
|
||||
'aria-keyshortcuts': 'Cmd+S',
|
||||
'aria-label': 'label',
|
||||
'aria-labelledby': 'labelledby',
|
||||
'aria-level': 3,
|
||||
'aria-live': 'polite',
|
||||
'aria-modal': true,
|
||||
'aria-multiline': true,
|
||||
'aria-multiselectable': true,
|
||||
'aria-orientation': 'portrait',
|
||||
'aria-owns': 'owns',
|
||||
'aria-placeholder': 'placeholder',
|
||||
'aria-posinset': 5,
|
||||
'aria-pressed': true,
|
||||
'aria-readonly': true,
|
||||
'aria-required': true,
|
||||
role: 'main',
|
||||
'aria-roledescription': 'roledescription',
|
||||
'aria-rowcount': 5,
|
||||
'aria-rowindex': 3,
|
||||
'aria-rowspan': 3,
|
||||
'aria-selected': true,
|
||||
'aria-setsize': 5,
|
||||
'aria-sort': 'ascending',
|
||||
'aria-valuemax': 5,
|
||||
'aria-valuemin': 0,
|
||||
'aria-valuenow': 3,
|
||||
'aria-valuetext': '3',
|
||||
};
|
||||
|
||||
const instance = render.create(<View {...props} />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTView
|
||||
accessibilityElementsHidden={true}
|
||||
accessibilityLabel="label"
|
||||
accessibilityLabelledBy={
|
||||
Array [
|
||||
"labelledby",
|
||||
]
|
||||
}
|
||||
accessibilityLiveRegion="polite"
|
||||
accessibilityState={
|
||||
Object {
|
||||
"busy": true,
|
||||
"checked": true,
|
||||
"disabled": true,
|
||||
"expanded": true,
|
||||
"selected": true,
|
||||
}
|
||||
}
|
||||
accessibilityValue={
|
||||
Object {
|
||||
"max": 5,
|
||||
"min": 0,
|
||||
"now": 3,
|
||||
"text": "3",
|
||||
}
|
||||
}
|
||||
aria-activedescendant="activedescendant"
|
||||
aria-atomic={true}
|
||||
aria-autocomplete="list"
|
||||
aria-columncount={5}
|
||||
aria-columnindex={3}
|
||||
aria-columnspan={2}
|
||||
aria-controls="controls"
|
||||
aria-current="current"
|
||||
aria-describedby="describedby"
|
||||
aria-details="details"
|
||||
aria-errormessage="errormessage"
|
||||
aria-flowto="flowto"
|
||||
aria-haspopup={true}
|
||||
aria-invalid={true}
|
||||
aria-keyshortcuts="Cmd+S"
|
||||
aria-level={3}
|
||||
aria-modal={true}
|
||||
aria-multiline={true}
|
||||
aria-multiselectable={true}
|
||||
aria-orientation="portrait"
|
||||
aria-owns="owns"
|
||||
aria-placeholder="placeholder"
|
||||
aria-posinset={5}
|
||||
aria-pressed={true}
|
||||
aria-readonly={true}
|
||||
aria-required={true}
|
||||
aria-roledescription="roledescription"
|
||||
aria-rowcount={5}
|
||||
aria-rowindex={3}
|
||||
aria-rowspan={3}
|
||||
aria-setsize={5}
|
||||
aria-sort="ascending"
|
||||
importantForAccessibility="no-hide-descendants"
|
||||
/>
|
||||
`);
|
||||
});
|
||||
|
||||
it('renders styles', () => {
|
||||
const style = {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
backgroundColor: 'white',
|
||||
marginInlineStart: 10,
|
||||
pointerEvents: 'none',
|
||||
};
|
||||
|
||||
const instance = render.create(<View style={style} />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTView
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "white",
|
||||
"display": "flex",
|
||||
"flex": 1,
|
||||
"marginInlineStart": 10,
|
||||
"pointerEvents": "none",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -158,13 +158,13 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => {
|
||||
const {width = props.width, height = props.height, uri} = source;
|
||||
style = flattenStyle([{width, height}, styles.base, props.style]);
|
||||
sources = [source];
|
||||
|
||||
if (uri === '') {
|
||||
console.warn('source.uri should not be an empty string');
|
||||
}
|
||||
}
|
||||
|
||||
const {height, width, ...restProps} = props;
|
||||
|
||||
const {onLoadStart, onLoad, onLoadEnd, onError} = props;
|
||||
const nativeProps = {
|
||||
...restProps,
|
||||
|
||||
@@ -199,7 +199,7 @@ type ____LayoutStyle_Internal = $ReadOnly<{
|
||||
marginBlockStart?: DimensionValue,
|
||||
|
||||
/** `marginBottom` works like `margin-bottom` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-bottom
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-block-start
|
||||
* for more details.
|
||||
*/
|
||||
marginBottom?: DimensionValue,
|
||||
@@ -270,17 +270,19 @@ type ____LayoutStyle_Internal = $ReadOnly<{
|
||||
|
||||
/** Setting `paddingBlock` is like setting both of
|
||||
* `paddingTop` and `paddingBottom`.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-block
|
||||
* for more details.
|
||||
*/
|
||||
paddingBlock?: DimensionValue,
|
||||
|
||||
/** `paddingBlockEnd` works like `padding-bottom` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-bottom
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-block-end
|
||||
* for more details.
|
||||
*/
|
||||
paddingBlockEnd?: DimensionValue,
|
||||
|
||||
/** `paddingBlockStart` works like `padding-top` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-top
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-block-start
|
||||
* for more details.
|
||||
*/
|
||||
paddingBlockStart?: DimensionValue,
|
||||
|
||||
+49
-41
@@ -9,17 +9,16 @@
|
||||
*/
|
||||
|
||||
import type {PressEvent} from '../Types/CoreEventTypes';
|
||||
import type {TextProps} from './TextProps';
|
||||
|
||||
import * as PressabilityDebug from '../Pressability/PressabilityDebug';
|
||||
import usePressability from '../Pressability/usePressability';
|
||||
import flattenStyle from '../StyleSheet/flattenStyle';
|
||||
import processColor from '../StyleSheet/processColor';
|
||||
import StyleSheet from '../StyleSheet/StyleSheet';
|
||||
import {getAccessibilityRoleFromRole} from '../Utilities/AcessibilityMapping';
|
||||
import Platform from '../Utilities/Platform';
|
||||
import TextAncestor from './TextAncestor';
|
||||
import {NativeText, NativeVirtualText} from './TextNativeComponent';
|
||||
import {type TextProps} from './TextProps';
|
||||
import * as React from 'react';
|
||||
import {useContext, useMemo, useState} from 'react';
|
||||
|
||||
@@ -36,6 +35,7 @@ const Text: React.AbstractComponent<
|
||||
accessible,
|
||||
accessibilityLabel,
|
||||
accessibilityRole,
|
||||
accessibilityState,
|
||||
allowFontScaling,
|
||||
'aria-busy': ariaBusy,
|
||||
'aria-checked': ariaChecked,
|
||||
@@ -64,13 +64,23 @@ const Text: React.AbstractComponent<
|
||||
|
||||
const [isHighlighted, setHighlighted] = useState(false);
|
||||
|
||||
const _accessibilityState = {
|
||||
busy: ariaBusy ?? props.accessibilityState?.busy,
|
||||
checked: ariaChecked ?? props.accessibilityState?.checked,
|
||||
disabled: ariaDisabled ?? props.accessibilityState?.disabled,
|
||||
expanded: ariaExpanded ?? props.accessibilityState?.expanded,
|
||||
selected: ariaSelected ?? props.accessibilityState?.selected,
|
||||
};
|
||||
let _accessibilityState;
|
||||
if (
|
||||
accessibilityState != null ||
|
||||
ariaBusy != null ||
|
||||
ariaChecked != null ||
|
||||
ariaDisabled != null ||
|
||||
ariaExpanded != null ||
|
||||
ariaSelected != null
|
||||
) {
|
||||
_accessibilityState = {
|
||||
busy: ariaBusy ?? accessibilityState?.busy,
|
||||
checked: ariaChecked ?? accessibilityState?.checked,
|
||||
disabled: ariaDisabled ?? accessibilityState?.disabled,
|
||||
expanded: ariaExpanded ?? accessibilityState?.expanded,
|
||||
selected: ariaSelected ?? accessibilityState?.selected,
|
||||
};
|
||||
}
|
||||
|
||||
const _disabled =
|
||||
restProps.disabled != null
|
||||
@@ -174,25 +184,11 @@ const Text: React.AbstractComponent<
|
||||
? null
|
||||
: processColor(restProps.selectionColor);
|
||||
|
||||
let style = flattenStyle(restProps.style);
|
||||
|
||||
let _selectable = restProps.selectable;
|
||||
if (style?.userSelect != null) {
|
||||
_selectable = userSelectToSelectableMap[style.userSelect];
|
||||
}
|
||||
|
||||
if (style?.verticalAlign != null) {
|
||||
style = StyleSheet.compose(style, {
|
||||
textAlignVertical:
|
||||
verticalAlignToTextAlignVerticalMap[style.verticalAlign],
|
||||
});
|
||||
}
|
||||
let style = restProps.style;
|
||||
|
||||
if (__DEV__) {
|
||||
if (PressabilityDebug.isEnabled() && onPress != null) {
|
||||
style = StyleSheet.compose(restProps.style, {
|
||||
color: 'magenta',
|
||||
});
|
||||
style = [restProps.style, {color: 'magenta'}];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,10 +207,22 @@ const Text: React.AbstractComponent<
|
||||
default: accessible,
|
||||
});
|
||||
|
||||
let flattenedStyle = flattenStyle(style);
|
||||
style = flattenStyle(style);
|
||||
|
||||
if (typeof flattenedStyle?.fontWeight === 'number') {
|
||||
flattenedStyle.fontWeight = flattenedStyle?.fontWeight.toString();
|
||||
if (typeof style?.fontWeight === 'number') {
|
||||
style.fontWeight = style?.fontWeight.toString();
|
||||
}
|
||||
|
||||
let _selectable = restProps.selectable;
|
||||
if (style?.userSelect != null) {
|
||||
_selectable = userSelectToSelectableMap[style.userSelect];
|
||||
delete style.userSelect;
|
||||
}
|
||||
|
||||
if (style?.verticalAlign != null) {
|
||||
style.textAlignVertical =
|
||||
verticalAlignToTextAlignVerticalMap[style.verticalAlign];
|
||||
delete style.verticalAlign;
|
||||
}
|
||||
|
||||
const _hasOnPressOrOnLongPress =
|
||||
@@ -223,46 +231,46 @@ const Text: React.AbstractComponent<
|
||||
return hasTextAncestor ? (
|
||||
<NativeVirtualText
|
||||
{...restProps}
|
||||
accessibilityState={_accessibilityState}
|
||||
{...eventHandlersForText}
|
||||
accessibilityLabel={ariaLabel ?? accessibilityLabel}
|
||||
accessibilityRole={
|
||||
role ? getAccessibilityRoleFromRole(role) : accessibilityRole
|
||||
}
|
||||
accessibilityState={_accessibilityState}
|
||||
isHighlighted={isHighlighted}
|
||||
isPressable={isPressable}
|
||||
selectable={_selectable}
|
||||
nativeID={id ?? nativeID}
|
||||
numberOfLines={numberOfLines}
|
||||
selectionColor={selectionColor}
|
||||
style={flattenedStyle}
|
||||
ref={forwardedRef}
|
||||
selectable={_selectable}
|
||||
selectionColor={selectionColor}
|
||||
style={style}
|
||||
/>
|
||||
) : (
|
||||
<TextAncestor.Provider value={true}>
|
||||
<NativeText
|
||||
{...restProps}
|
||||
{...eventHandlersForText}
|
||||
disabled={_disabled}
|
||||
selectable={_selectable}
|
||||
accessibilityLabel={ariaLabel ?? accessibilityLabel}
|
||||
accessibilityRole={
|
||||
role ? getAccessibilityRoleFromRole(role) : accessibilityRole
|
||||
}
|
||||
accessibilityState={nativeTextAccessibilityState}
|
||||
accessible={
|
||||
accessible == null && Platform.OS === 'android'
|
||||
? _hasOnPressOrOnLongPress
|
||||
: _accessible
|
||||
}
|
||||
accessibilityLabel={ariaLabel ?? accessibilityLabel}
|
||||
accessibilityState={nativeTextAccessibilityState}
|
||||
accessibilityRole={
|
||||
role ? getAccessibilityRoleFromRole(role) : accessibilityRole
|
||||
}
|
||||
allowFontScaling={allowFontScaling !== false}
|
||||
disabled={_disabled}
|
||||
ellipsizeMode={ellipsizeMode ?? 'tail'}
|
||||
isHighlighted={isHighlighted}
|
||||
nativeID={id ?? nativeID}
|
||||
numberOfLines={numberOfLines}
|
||||
selectionColor={selectionColor}
|
||||
style={flattenedStyle}
|
||||
ref={forwardedRef}
|
||||
selectable={_selectable}
|
||||
selectionColor={selectionColor}
|
||||
style={style}
|
||||
/>
|
||||
</TextAncestor.Provider>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const render = require('../../../jest/renderer');
|
||||
const React = require('../React');
|
||||
const Text = require('../Text');
|
||||
|
||||
jest.unmock('../Text');
|
||||
jest.unmock('../TextNativeComponent');
|
||||
|
||||
describe('Text', () => {
|
||||
it('default render', () => {
|
||||
const instance = render.create(<Text />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTText
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
isHighlighted={false}
|
||||
selectionColor={null}
|
||||
/>
|
||||
`);
|
||||
});
|
||||
|
||||
it('has displayName', () => {
|
||||
expect(Text.displayName).toEqual('Text');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Text compat with web', () => {
|
||||
it('renders core props', () => {
|
||||
const props = {
|
||||
id: 'id',
|
||||
tabIndex: 0,
|
||||
testID: 'testID',
|
||||
};
|
||||
|
||||
const instance = render.create(<Text {...props} />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTText
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
isHighlighted={false}
|
||||
nativeID="id"
|
||||
selectionColor={null}
|
||||
tabIndex={0}
|
||||
testID="testID"
|
||||
/>
|
||||
`);
|
||||
});
|
||||
|
||||
it('renders "aria-*" props', () => {
|
||||
const props = {
|
||||
'aria-activedescendant': 'activedescendant',
|
||||
'aria-atomic': true,
|
||||
'aria-autocomplete': 'list',
|
||||
'aria-busy': true,
|
||||
'aria-checked': true,
|
||||
'aria-columncount': 5,
|
||||
'aria-columnindex': 3,
|
||||
'aria-columnspan': 2,
|
||||
'aria-controls': 'controls',
|
||||
'aria-current': 'current',
|
||||
'aria-describedby': 'describedby',
|
||||
'aria-details': 'details',
|
||||
'aria-disabled': true,
|
||||
'aria-errormessage': 'errormessage',
|
||||
'aria-expanded': true,
|
||||
'aria-flowto': 'flowto',
|
||||
'aria-haspopup': true,
|
||||
'aria-hidden': true,
|
||||
'aria-invalid': true,
|
||||
'aria-keyshortcuts': 'Cmd+S',
|
||||
'aria-label': 'label',
|
||||
'aria-labelledby': 'labelledby',
|
||||
'aria-level': 3,
|
||||
'aria-live': 'polite',
|
||||
'aria-modal': true,
|
||||
'aria-multiline': true,
|
||||
'aria-multiselectable': true,
|
||||
'aria-orientation': 'portrait',
|
||||
'aria-owns': 'owns',
|
||||
'aria-placeholder': 'placeholder',
|
||||
'aria-posinset': 5,
|
||||
'aria-pressed': true,
|
||||
'aria-readonly': true,
|
||||
'aria-required': true,
|
||||
role: 'main',
|
||||
'aria-roledescription': 'roledescription',
|
||||
'aria-rowcount': 5,
|
||||
'aria-rowindex': 3,
|
||||
'aria-rowspan': 3,
|
||||
'aria-selected': true,
|
||||
'aria-setsize': 5,
|
||||
'aria-sort': 'ascending',
|
||||
'aria-valuemax': 5,
|
||||
'aria-valuemin': 0,
|
||||
'aria-valuenow': 3,
|
||||
'aria-valuetext': '3',
|
||||
};
|
||||
|
||||
const instance = render.create(<Text {...props} />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTText
|
||||
accessibilityLabel="label"
|
||||
accessibilityState={
|
||||
Object {
|
||||
"busy": true,
|
||||
"checked": true,
|
||||
"disabled": true,
|
||||
"expanded": true,
|
||||
"selected": true,
|
||||
}
|
||||
}
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
aria-activedescendant="activedescendant"
|
||||
aria-atomic={true}
|
||||
aria-autocomplete="list"
|
||||
aria-columncount={5}
|
||||
aria-columnindex={3}
|
||||
aria-columnspan={2}
|
||||
aria-controls="controls"
|
||||
aria-current="current"
|
||||
aria-describedby="describedby"
|
||||
aria-details="details"
|
||||
aria-errormessage="errormessage"
|
||||
aria-flowto="flowto"
|
||||
aria-haspopup={true}
|
||||
aria-hidden={true}
|
||||
aria-invalid={true}
|
||||
aria-keyshortcuts="Cmd+S"
|
||||
aria-labelledby="labelledby"
|
||||
aria-level={3}
|
||||
aria-live="polite"
|
||||
aria-modal={true}
|
||||
aria-multiline={true}
|
||||
aria-multiselectable={true}
|
||||
aria-orientation="portrait"
|
||||
aria-owns="owns"
|
||||
aria-placeholder="placeholder"
|
||||
aria-posinset={5}
|
||||
aria-pressed={true}
|
||||
aria-readonly={true}
|
||||
aria-required={true}
|
||||
aria-roledescription="roledescription"
|
||||
aria-rowcount={5}
|
||||
aria-rowindex={3}
|
||||
aria-rowspan={3}
|
||||
aria-setsize={5}
|
||||
aria-sort="ascending"
|
||||
aria-valuemax={5}
|
||||
aria-valuemin={0}
|
||||
aria-valuenow={3}
|
||||
aria-valuetext="3"
|
||||
disabled={true}
|
||||
ellipsizeMode="tail"
|
||||
isHighlighted={false}
|
||||
selectionColor={null}
|
||||
/>
|
||||
`);
|
||||
});
|
||||
|
||||
it('renders styles', () => {
|
||||
const style = {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
backgroundColor: 'white',
|
||||
marginInlineStart: 10,
|
||||
userSelect: 'none',
|
||||
verticalAlign: 'middle',
|
||||
};
|
||||
|
||||
const instance = render.create(<Text style={style} />);
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTText
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
isHighlighted={false}
|
||||
selectable={false}
|
||||
selectionColor={null}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "white",
|
||||
"display": "flex",
|
||||
"flex": 1,
|
||||
"marginInlineStart": 10,
|
||||
"textAlignVertical": "center",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user