Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @format
*/

import SafeAreaView from '../../Components/SafeAreaView/SafeAreaView';
import SafeAreaView from '../../../src/private/components/safeareaview/SafeAreaView_INTERNAL_DO_NOT_USE';
import StyleSheet, {
type ColorValue,
type ViewStyleProp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';

import type {HostInstance} from 'react-native';

import SafeAreaView from '../../../../src/private/components/safeareaview/SafeAreaView_INTERNAL_DO_NOT_USE';
import * as Fantom from '@react-native/fantom';
import * as React from 'react';
import {createRef} from 'react';
Expand Down Expand Up @@ -116,3 +117,41 @@ describe('experimental_onSafeAreaInsetsChange', () => {
).toEqual(<rn-view experimental_onSafeAreaInsetsChange="true" />);
});
});

describe('<SafeAreaView>', () => {
it('applies the insets it receives as padding', () => {
const root = Fantom.createRoot();
const nodeRef = createRef<HostInstance>();

Fantom.runTask(() => {
root.render(<SafeAreaView collapsable={false} ref={nodeRef} />);
});

expect(
root
.getRenderedOutput({
props: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
})
.toJSX(),
).toEqual(<rn-view />);

Fantom.dispatchNativeEvent(nodeRef, 'safeAreaInsetsChange', {
insets: INSETS,
});

expect(
root
.getRenderedOutput({
props: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
})
.toJSX(),
).toEqual(
<rn-view
paddingBottom="34"
paddingLeft="0"
paddingRight="0"
paddingTop="44"
/>,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @format
*/

import SafeAreaView from '../../Components/SafeAreaView/SafeAreaView';
import SafeAreaView from '../../../src/private/components/safeareaview/SafeAreaView_INTERNAL_DO_NOT_USE';
import View from '../../Components/View/View';
import StyleSheet from '../../StyleSheet/StyleSheet';
import Text from '../../Text/Text';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
* @format
*/

import type {ViewProps} from '../../Components/View/ViewPropTypes';
import type {LogLevel} from '../Data/LogBoxLog';

import SafeAreaView from '../../Components/SafeAreaView/SafeAreaView';
import SafeAreaView from '../../../src/private/components/safeareaview/SafeAreaView_INTERNAL_DO_NOT_USE';
import View from '../../Components/View/View';
import StyleSheet from '../../StyleSheet/StyleSheet';
import Text from '../../Text/Text';
Expand All @@ -27,13 +26,10 @@ type Props = Readonly<{
level: LogLevel,
}>;

const LogBoxInspectorHeaderSafeArea: React.ComponentType<ViewProps> =
Platform.OS === 'android' ? View : SafeAreaView;

export default function LogBoxInspectorHeader(props: Props): React.Node {
if (props.level === 'syntax') {
return (
<LogBoxInspectorHeaderSafeArea style={styles[props.level]}>
<SafeAreaView style={styles[props.level]}>
<View style={styles.header}>
<View style={styles.title}>
<Text
Expand All @@ -44,7 +40,7 @@ export default function LogBoxInspectorHeader(props: Props): React.Node {
</Text>
</View>
</View>
</LogBoxInspectorHeaderSafeArea>
</SafeAreaView>
);
}

Expand All @@ -56,7 +52,7 @@ export default function LogBoxInspectorHeader(props: Props): React.Node {
const titleText = `Log ${props.selectedIndex + 1} of ${props.total}`;

return (
<LogBoxInspectorHeaderSafeArea style={styles[props.level]}>
<SafeAreaView style={styles[props.level]}>
<View style={styles.header}>
<LogBoxInspectorHeaderButton
id="logbox_header_button_prev"
Expand All @@ -81,7 +77,7 @@ export default function LogBoxInspectorHeader(props: Props): React.Node {
onPress={() => props.onSelectIndex(nextIndex)}
/>
</View>
</LogBoxInspectorHeaderSafeArea>
</SafeAreaView>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`LogBoxNotificationContainer should render both an error and warning notification 1`] = `
<SafeAreaView
<View
experimental_onSafeAreaInsetsChange={[Function]}
style={
Object {
"alignItems": "center",
"bottom": 20,
"left": 10,
"pointerEvents": "box-none",
"position": "absolute",
"right": 10,
}
Array [
Object {
"alignItems": "center",
"bottom": 20,
"left": 10,
"pointerEvents": "box-none",
"position": "absolute",
"right": 10,
},
null,
]
}
>
<View
Expand Down Expand Up @@ -107,7 +111,7 @@ exports[`LogBoxNotificationContainer should render both an error and warning not
totalLogCount={1}
/>
</View>
</SafeAreaView>
</View>
`;

exports[`LogBoxNotificationContainer should render null with no logs 1`] = `null`;
Expand All @@ -119,16 +123,20 @@ exports[`LogBoxNotificationContainer should render selected fatal error even whe
exports[`LogBoxNotificationContainer should render selected syntax error even when disabled 1`] = `null`;

exports[`LogBoxNotificationContainer should render the latest error notification 1`] = `
<SafeAreaView
<View
experimental_onSafeAreaInsetsChange={[Function]}
style={
Object {
"alignItems": "center",
"bottom": 20,
"left": 10,
"pointerEvents": "box-none",
"position": "absolute",
"right": 10,
}
Array [
Object {
"alignItems": "center",
"bottom": 20,
"left": 10,
"pointerEvents": "box-none",
"position": "absolute",
"right": 10,
},
null,
]
}
>
<View
Expand Down Expand Up @@ -178,20 +186,24 @@ exports[`LogBoxNotificationContainer should render the latest error notification
totalLogCount={2}
/>
</View>
</SafeAreaView>
</View>
`;

exports[`LogBoxNotificationContainer should render the latest warning notification 1`] = `
<SafeAreaView
<View
experimental_onSafeAreaInsetsChange={[Function]}
style={
Object {
"alignItems": "center",
"bottom": 20,
"left": 10,
"pointerEvents": "box-none",
"position": "absolute",
"right": 10,
}
Array [
Object {
"alignItems": "center",
"bottom": 20,
"left": 10,
"pointerEvents": "box-none",
"position": "absolute",
"right": 10,
},
null,
]
}
>
<View
Expand Down Expand Up @@ -241,5 +253,5 @@ exports[`LogBoxNotificationContainer should render the latest warning notificati
totalLogCount={2}
/>
</View>
</SafeAreaView>
</View>
`;
5 changes: 3 additions & 2 deletions packages/react-native/ReactNativeApi.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2ceaa8e472a53fc667994edb63ccd995>>
* @generated SignedSource<<1faa2820bf867aa599cdd390c5c90b00>>
*
* This file was generated by scripts/js-api/build-types/index.js.
*/
Expand Down Expand Up @@ -5968,7 +5968,8 @@ export {
RootViewStyleProvider, // d5909148
Runnable, // 594dd93a
Runnables, // 4367c557
SafeAreaInsetsChangeEvent, // cedb5309
SafeAreaInsets, // 64d7a605
SafeAreaInsetsChangeEvent, // 4ffd35e5
SafeAreaView, // d917b6ec
SafeAreaViewInstance, // 2b5d4391
ScaledSize, // 07e417c7
Expand Down
1 change: 1 addition & 0 deletions packages/react-native/index.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ export type {
NativeTouchEvent,
NativeUIEvent,
ResponderSyntheticEvent,
SafeAreaInsets,
SafeAreaInsetsChangeEvent,
ScrollEvent,
TargetedEvent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,62 @@
*/

import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {
SafeAreaInsets,
SafeAreaInsetsChangeEvent,
} from '../../../../Libraries/Types/CoreEventTypes';
import type {HostInstance} from '../../types/HostInstance';

import View from '../../../../Libraries/Components/View/View';
import UIManager from '../../../../Libraries/ReactNative/UIManager';
import Platform from '../../../../Libraries/Utilities/Platform';
import I18nManager from '../../../../Libraries/ReactNative/I18nManager';
import * as React from 'react';
import {useCallback, useMemo, useState} from 'react';

const exported: component(
ref?: React.RefSetter<React.ElementRef<typeof View>>,
...ViewProps
) = Platform.select({
ios: require('../../../../src/private/components/safeareaview/specs/RCTSafeAreaViewNativeComponent')
.default,
android: UIManager.hasViewManagerConfig('RCTSafeAreaView')
? require('../../../../src/private/components/safeareaview/specs/RCTSafeAreaViewNativeComponent')
.default
: View,
default: View,
});
/**
* Renders its children within the safe area of the device, by applying the part
* of the view that is covered by the system UI as padding.
*/
component SafeAreaView(
ref?: React.RefSetter<HostInstance>,
...props: ViewProps
) {
const {style, experimental_onSafeAreaInsetsChange, ...otherProps} = props;
const [insets, setInsets] = useState<?SafeAreaInsets>(null);

const handleSafeAreaInsetsChange = useCallback(
(event: SafeAreaInsetsChangeEvent) => {
setInsets(event.nativeEvent.insets);
experimental_onSafeAreaInsetsChange?.(event);
},
[experimental_onSafeAreaInsetsChange],
);

const paddingStyle = useMemo(() => {
if (insets == null) {
return null;
}
// Insets are physical edges, but Yoga remaps paddingLeft/paddingRight to
// start/end when I18nManager's swapLeftAndRightInRTL is on, which would
// pad the mirror-image edge in RTL. Swap the values so the physical edge
// keeps its inset.
const {isRTL, doLeftAndRightSwapInRTL} = I18nManager.getConstants();
const swap = isRTL && doLeftAndRightSwapInRTL;
return {
paddingTop: insets.top,
paddingRight: swap ? insets.left : insets.right,
paddingBottom: insets.bottom,
paddingLeft: swap ? insets.right : insets.left,
};
}, [insets]);

return (
<View
{...otherProps}
ref={ref}
experimental_onSafeAreaInsetsChange={handleSafeAreaInsetsChange}
style={[style, paddingStyle]}
/>
);
}

export default exported;
export default SafeAreaView;
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@

import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';

import SafeAreaView from '../SafeAreaView_INTERNAL_DO_NOT_USE';
import * as Fantom from '@react-native/fantom';
import * as React from 'react';
import {SafeAreaView, Text} from 'react-native';
import {Text} from 'react-native';

describe('<SafeAreaView>', () => {
it('renders with children', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import type {ElementsHierarchy, InspectedElement} from './Inspector';

import SafeAreaView from '../../../../../Libraries/Components/SafeAreaView/SafeAreaView';
import SafeAreaView from '../../../components/safeareaview/SafeAreaView_INTERNAL_DO_NOT_USE';
import * as React from 'react';

const ScrollView =
Expand Down
Loading
Loading