+ `); + }); + + it('renders styles', () => { + const style = { + display: 'flex', + flex: 1, + backgroundColor: 'white', + marginInlineStart: 10, + userSelect: 'none', + verticalAlign: 'middle', + }; + + const instance = ReactTestRenderer.create(); + + expect(instance.toJSON()).toMatchInlineSnapshot(` + + `); + }); +}); diff --git a/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap b/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap index ea3b4c1f062744..ac93dc2e644c7c 100644 --- a/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap +++ b/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap @@ -2,15 +2,6 @@ exports[`TextInput tests should render as expected: should deep render when mocked (please verify output manually) 1`] = ` `; -exports[`TextInput tests should render as expected: should shallow render as when mocked 1`] = ``; +exports[`TextInput tests should render as expected: should shallow render as when mocked 1`] = ``; -exports[`TextInput tests should render as expected: should shallow render as when not mocked 1`] = ``; +exports[`TextInput tests should render as expected: should shallow render as when not mocked 1`] = ``; diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index 8ef1f814a312fb..86540410c83b64 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -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 ( diff --git a/Libraries/Components/View/__tests__/View-test.js b/Libraries/Components/View/__tests__/View-test.js new file mode 100644 index 00000000000000..78e547a673319e --- /dev/null +++ b/Libraries/Components/View/__tests__/View-test.js @@ -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(); + + expect(instance.toJSON()).toMatchInlineSnapshot(``); + }); + + 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(); + + expect(instance.toJSON()).toMatchInlineSnapshot(` + + `); + }); + + 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(); + + expect(instance.toJSON()).toMatchInlineSnapshot(` + + `); + }); + + it('renders styles', () => { + const style = { + display: 'flex', + flex: 1, + backgroundColor: 'white', + marginInlineStart: 10, + pointerEvents: 'none', + }; + + const instance = render.create(); + + expect(instance.toJSON()).toMatchInlineSnapshot(` + + `); + }); +}); diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index f874c6abc93f02..ca219faa80694b 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -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, diff --git a/Libraries/StyleSheet/StyleSheetTypes.js b/Libraries/StyleSheet/StyleSheetTypes.js index d34ae2e2f793ba..6fd7ee822d1b26 100644 --- a/Libraries/StyleSheet/StyleSheetTypes.js +++ b/Libraries/StyleSheet/StyleSheetTypes.js @@ -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, diff --git a/Libraries/Text/Text.js b/Libraries/Text/Text.js index 1f6524721cc820..dde3a5b4d75d45 100644 --- a/Libraries/Text/Text.js +++ b/Libraries/Text/Text.js @@ -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 style?.fontWeight === 'number') { + style.fontWeight = style?.fontWeight.toString(); + } + + let _selectable = restProps.selectable; + if (style?.userSelect != null) { + _selectable = userSelectToSelectableMap[style.userSelect]; + delete style.userSelect; + } - if (typeof flattenedStyle?.fontWeight === 'number') { - flattenedStyle.fontWeight = flattenedStyle?.fontWeight.toString(); + if (style?.verticalAlign != null) { + style.textAlignVertical = + verticalAlignToTextAlignVerticalMap[style.verticalAlign]; + delete style.verticalAlign; } const _hasOnPressOrOnLongPress = @@ -223,46 +231,46 @@ const Text: React.AbstractComponent< return hasTextAncestor ? ( ) : ( ); diff --git a/Libraries/Text/__tests__/Text-test.js b/Libraries/Text/__tests__/Text-test.js new file mode 100644 index 00000000000000..ad565833a1f820 --- /dev/null +++ b/Libraries/Text/__tests__/Text-test.js @@ -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(); + + expect(instance.toJSON()).toMatchInlineSnapshot(` + + `); + }); + + 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(); + + expect(instance.toJSON()).toMatchInlineSnapshot(` + + `); + }); + + 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(); + + expect(instance.toJSON()).toMatchInlineSnapshot(` + + `); + }); + + it('renders styles', () => { + const style = { + display: 'flex', + flex: 1, + backgroundColor: 'white', + marginInlineStart: 10, + userSelect: 'none', + verticalAlign: 'middle', + }; + + const instance = render.create(); + + expect(instance.toJSON()).toMatchInlineSnapshot(` + + `); + }); +});