Skip to content

Commit

Permalink
feat: mapped layout props for view component (facebook#34590)
Browse files Browse the repository at this point in the history
Summary:
This PR adds mapping for layout props, it maps

    marginInlineStart: 'marginStart',
    marginInlineEnd: 'marginEnd',
    marginBlockStart: 'marginTop',
    marginBlockEnd: 'marginBottom',
    marginBlock: 'marginVertical',
    marginInline: 'marginHorizontal',
    paddingInlineStart: 'paddingStart',
    paddingInlineEnd: 'paddingEnd',
    paddingBlockStart: 'paddingTop',
    paddingBlockEnd: 'paddingBottom',
    paddingBlock: 'paddingVertical',
    paddingInline: 'paddingHorizontal',

 as requested on facebook#34425

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[General][Added] - Added CSS logical properties by mapping layout props.

Pull Request resolved: facebook#34590

Test Plan:
```js
<View
  style={[
    {
      marginBlockStart: 5,        // maps to "marginTop"
      borderWidth: 1,
      borderRadius: 5,
      padding: 5,
    }
  ]}>
  <Text style={{fontSize: 11}}>Hello World!</Text>
</View>
```

Reviewed By: cipolleschi

Differential Revision: D41108750

Pulled By: necolas

fbshipit-source-id: 870b9b58a740aba12290a0604a9f6b52aa52de4c
  • Loading branch information
mayank-96 authored and OlimpiaZurek committed May 22, 2023
1 parent 556d976 commit 7935cb3
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 25 deletions.
12 changes: 9 additions & 3 deletions Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {TextInputType} from './TextInput.flow';

import usePressability from '../../Pressability/usePressability';
import flattenStyle from '../../StyleSheet/flattenStyle';
import processLayoutProps from '../../StyleSheet/processStyles';
import StyleSheet, {
type ColorValue,
type TextStyleProp,
Expand Down Expand Up @@ -1406,11 +1407,14 @@ function InternalTextInput(props: Props): React.Node {
? RCTMultilineTextInputView
: RCTSinglelineTextInputView;

const style =
let style =
props.multiline === true
? StyleSheet.flatten([styles.multilineInput, props.style])
? [styles.multilineInput, props.style]
: props.style;

style = flattenStyle(style);
style = processLayoutProps(style);

const useOnChangeSync =
(props.unstable_onChangeSync || props.unstable_onChangeTextSync) &&
!(props.onChange || props.onChangeText);
Expand Down Expand Up @@ -1442,7 +1446,9 @@ function InternalTextInput(props: Props): React.Node {
/>
);
} else if (Platform.OS === 'android') {
const style = [props.style];
let style = flattenStyle(props.style);
style = processLayoutProps(style);

const autoCapitalize = props.autoCapitalize || 'sentences';
const _accessibilityLabelledBy =
props?.['aria-labelledby'] ?? props?.accessibilityLabelledBy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ exports[`TextInput tests should render as expected: should deep render when mock
onStartShouldSetResponder={[Function]}
rejectResponderTermination={true}
selection={null}
style={Object {}}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
Expand Down Expand Up @@ -70,6 +71,7 @@ exports[`TextInput tests should render as expected: should deep render when not
onStartShouldSetResponder={[Function]}
rejectResponderTermination={true}
selection={null}
style={Object {}}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
Expand Down
8 changes: 5 additions & 3 deletions Libraries/Components/View/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import type {ViewProps} from './ViewPropTypes';

import flattenStyle from '../../StyleSheet/flattenStyle';
import processLayoutProps from '../../StyleSheet/processStyles';
import TextAncestor from '../../Text/TextAncestor';
import {getAccessibilityRoleFromRole} from '../../Utilities/AcessibilityMapping';
import ViewNativeComponent from './ViewNativeComponent';
Expand Down Expand Up @@ -57,7 +58,6 @@ const View: React.AbstractComponent<
nativeID,
pointerEvents,
role,
style,
tabIndex,
...otherProps
}: ViewProps,
Expand All @@ -81,8 +81,10 @@ const View: React.AbstractComponent<
text: ariaValueText ?? accessibilityValue?.text,
};

const flattenedStyle = flattenStyle(style);
const newPointerEvents = flattenedStyle?.pointerEvents || pointerEvents;
let style = flattenStyle(otherProps.style);
style = processLayoutProps(style);

const newPointerEvents = style?.pointerEvents || pointerEvents;

return (
<TextAncestor.Provider value={false}>
Expand Down
4 changes: 4 additions & 0 deletions Libraries/Image/Image.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {ImageAndroid} from './Image.flow';
import type {ImageProps as ImagePropsType} from './ImageProps';

import flattenStyle from '../StyleSheet/flattenStyle';
import processLayoutProps from '../StyleSheet/processStyles';
import StyleSheet from '../StyleSheet/StyleSheet';
import TextAncestor from '../Text/TextAncestor';
import ImageAnalyticsTagContext from './ImageAnalyticsTagContext';
Expand Down Expand Up @@ -165,6 +166,9 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => {
}

const {height, width, ...restProps} = props;

style = processLayoutProps(style);

const {onLoadStart, onLoad, onLoadEnd, onError} = props;
const nativeProps = {
...restProps,
Expand Down
3 changes: 3 additions & 0 deletions Libraries/Image/Image.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {ImageIOS} from './Image.flow';
import type {ImageProps as ImagePropsType} from './ImageProps';

import flattenStyle from '../StyleSheet/flattenStyle';
import processLayoutProps from '../StyleSheet/processStyles';
import StyleSheet from '../StyleSheet/StyleSheet';
import ImageAnalyticsTagContext from './ImageAnalyticsTagContext';
import ImageInjection from './ImageInjection';
Expand Down Expand Up @@ -137,6 +138,8 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => {
// $FlowFixMe[prop-missing]
const tintColor = props.tintColor || style.tintColor;

style = processLayoutProps(style);

if (props.children != null) {
throw new Error(
'The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning.',
Expand Down
12 changes: 12 additions & 0 deletions Libraries/StyleSheet/StyleSheetTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,15 @@ export interface FlexStyle {
| undefined;
left?: number | string | undefined;
margin?: number | string | undefined;
marginBlock?: number | string | undefined;
marginBlockEnd?: number | string | undefined;
marginBlockStart?: number | string | undefined;
marginBottom?: number | string | undefined;
marginEnd?: number | string | undefined;
marginHorizontal?: number | string | undefined;
marginInline?: number | string | undefined;
marginInlineEnd?: number | string | undefined;
marginInlineStart?: number | string | undefined;
marginLeft?: number | string | undefined;
marginRight?: number | string | undefined;
marginStart?: number | string | undefined;
Expand All @@ -84,8 +90,14 @@ export interface FlexStyle {
overflow?: 'visible' | 'hidden' | 'scroll' | undefined;
padding?: number | string | undefined;
paddingBottom?: number | string | undefined;
paddingBlock?: number | string | undefined;
paddingBlockEnd?: number | string | undefined;
paddingBlockStart?: number | string | undefined;
paddingEnd?: number | string | undefined;
paddingHorizontal?: number | string | undefined;
paddingInline?: number | string | undefined;
paddingInlineEnd?: number | string | undefined;
paddingInlineStart?: number | string | undefined;
paddingLeft?: number | string | undefined;
paddingRight?: number | string | undefined;
paddingStart?: number | string | undefined;
Expand Down
68 changes: 68 additions & 0 deletions Libraries/StyleSheet/StyleSheetTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,23 @@ type ____LayoutStyle_Internal = $ReadOnly<{
*/
margin?: DimensionValue,

/** Setting `marginBlock` has the same effect as setting both
* `marginTop` and `marginBottom`.
*/
marginBlock?: DimensionValue,

/** `marginBlockEnd` works like `margin-bottom` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-bottom
* for more details.
*/
marginBlockEnd?: DimensionValue,

/** `marginBlockStart` works like `margin-top` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-top
* for more details.
*/
marginBlockStart?: DimensionValue,

/** `marginBottom` works like `margin-bottom` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-bottom
* for more details.
Expand All @@ -196,6 +213,23 @@ type ____LayoutStyle_Internal = $ReadOnly<{
*/
marginHorizontal?: DimensionValue,

/** Setting `marginInline` has the same effect as setting
* both `marginLeft` and `marginRight`.
*/
marginInline?: DimensionValue,

/**
* When direction is `ltr`, `marginInlineEnd` is equivalent to `marginRight`.
* When direction is `rtl`, `marginInlineEnd` is equivalent to `marginLeft`.
*/
marginInlineEnd?: DimensionValue,

/**
* When direction is `ltr`, `marginInlineStart` is equivalent to `marginLeft`.
* When direction is `rtl`, `marginInlineStart` is equivalent to `marginRight`.
*/
marginInlineStart?: DimensionValue,

/** `marginLeft` works like `margin-left` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-left
* for more details.
Expand Down Expand Up @@ -232,6 +266,23 @@ type ____LayoutStyle_Internal = $ReadOnly<{
*/
padding?: DimensionValue,

/** Setting `paddingBlock` is like setting both of
* `paddingTop` and `paddingBottom`.
*/
paddingBlock?: DimensionValue,

/** `paddingBlockEnd` works like `padding-bottom` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-bottom
* for more details.
*/
paddingBlockEnd?: DimensionValue,

/** `paddingBlockStart` works like `padding-top` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-top
* for more details.
*/
paddingBlockStart?: DimensionValue,

/** `paddingBottom` works like `padding-bottom` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-bottom
* for more details.
Expand All @@ -249,6 +300,23 @@ type ____LayoutStyle_Internal = $ReadOnly<{
*/
paddingHorizontal?: DimensionValue,

/** Setting `paddingInline` is like setting both of
* `paddingLeft` and `paddingRight`.
*/
paddingInline?: DimensionValue,

/**
* When direction is `ltr`, `paddingInlineEnd` is equivalent to `paddingRight`.
* When direction is `rtl`, `paddingInlineEnd` is equivalent to `paddingLeft`.
*/
paddingInlineEnd?: DimensionValue,

/**
* When direction is `ltr`, `paddingInlineStart` is equivalent to `paddingLeft`.
* When direction is `rtl`, `paddingInlineStart` is equivalent to `paddingRight`.
*/
paddingInlineStart?: DimensionValue,

/** `paddingLeft` works like `padding-left` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-left
* for more details.
Expand Down
83 changes: 83 additions & 0 deletions Libraries/StyleSheet/__tests__/processStyles-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* 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 processLayoutProps = require('../processStyles');

describe('processLayoutProps', () => {
it('it should map layout style properties', () => {
const style = {
marginInlineStart: 10,
marginInlineEnd: 20,
marginBlockStart: 30,
marginBlockEnd: 40,
marginBlock: 50,
marginInline: 60,
paddingInlineStart: 70,
paddingInlineEnd: 80,
paddingBlockStart: 90,
paddingBlockEnd: 100,
paddingBlock: 110,
paddingInline: 120,
};
const processedStyle = processLayoutProps(style);
expect(processedStyle.marginStart).toBe(10);
expect(processedStyle.marginEnd).toBe(20);
expect(processedStyle.marginTop).toBe(30);
expect(processedStyle.marginBottom).toBe(40);
expect(processedStyle.marginVertical).toBe(50);
expect(processedStyle.marginHorizontal).toBe(60);
expect(processedStyle.paddingStart).toBe(70);
expect(processedStyle.paddingEnd).toBe(80);
expect(processedStyle.paddingTop).toBe(90);
expect(processedStyle.paddingBottom).toBe(100);
expect(processedStyle.paddingVertical).toBe(110);
expect(processedStyle.paddingHorizontal).toBe(120);

expect(processedStyle.marginInlineStart).toBe(undefined);
expect(processedStyle.marginInlineEnd).toBe(undefined);
expect(processedStyle.marginBlockStart).toBe(undefined);
expect(processedStyle.marginBlockEnd).toBe(undefined);
expect(processedStyle.marginBlock).toBe(undefined);
expect(processedStyle.marginInline).toBe(undefined);
expect(processedStyle.paddingInlineStart).toBe(undefined);
expect(processedStyle.paddingInlineEnd).toBe(undefined);
expect(processedStyle.paddingBlockStart).toBe(undefined);
expect(processedStyle.paddingBlockEnd).toBe(undefined);
expect(processedStyle.paddingBlock).toBe(undefined);
expect(processedStyle.paddingInline).toBe(undefined);
});

it('should override style properties', () => {
const style = {marginStart: 20, marginInlineStart: 40};
const processedStyle = processLayoutProps(style);
expect(processedStyle.marginStart).toBe(40);
});

it('should overwrite properties with `undefined`', () => {
const style = {marginInlineStart: 40, marginStart: undefined};
const processedStyle = processLayoutProps(style);
expect(processedStyle.marginStart).toBe(40);
});

it('should not fail on falsy values', () => {
expect(() => processLayoutProps({})).not.toThrow();
expect(() => processLayoutProps(null)).not.toThrow();
expect(() => processLayoutProps(false)).not.toThrow();
expect(() => processLayoutProps(undefined)).not.toThrow();
});

it('should not change style if there is no layout style property', () => {
const style = {backgroundColor: '#000', width: 10};
const processedStyle = processLayoutProps(style);
expect(processedStyle).toStrictEqual(style);
});
});
45 changes: 45 additions & 0 deletions Libraries/StyleSheet/processStyles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* 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
* @flow strict-local
*/

'use strict';

import type {____FlattenStyleProp_Internal} from './StyleSheetTypes';

function processLayoutProps<T>(
flattenedStyle: ____FlattenStyleProp_Internal<T>,
): ____FlattenStyleProp_Internal<T> {
const _flattenedStyle = {...flattenedStyle};
const layoutPropMap = {
marginInlineStart: 'marginStart',
marginInlineEnd: 'marginEnd',
marginBlockStart: 'marginTop',
marginBlockEnd: 'marginBottom',
marginBlock: 'marginVertical',
marginInline: 'marginHorizontal',
paddingInlineStart: 'paddingStart',
paddingInlineEnd: 'paddingEnd',
paddingBlockStart: 'paddingTop',
paddingBlockEnd: 'paddingBottom',
paddingBlock: 'paddingVertical',
paddingInline: 'paddingHorizontal',
};
if (_flattenedStyle) {
Object.keys(layoutPropMap).forEach(key => {
if (_flattenedStyle && _flattenedStyle[key] !== undefined) {
_flattenedStyle[layoutPropMap[key]] = _flattenedStyle[key];
delete _flattenedStyle[key];
}
});
}

return _flattenedStyle;
}

module.exports = processLayoutProps;
Loading

0 comments on commit 7935cb3

Please sign in to comment.