Skip to content

Commit

Permalink
Merge pull request Expensify#53250 from margelo/feat/screen-wrapper-b…
Browse files Browse the repository at this point in the history
…etter-inset-padding-handling

feat: new way to handle insets in the app
  • Loading branch information
mountiny authored Nov 30, 2024
2 parents 9056c61 + e365e1e commit 3cc88f5
Show file tree
Hide file tree
Showing 105 changed files with 407 additions and 447 deletions.
9 changes: 6 additions & 3 deletions contributingGuides/FORMS.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,14 +320,17 @@ An example of this can be seen in the [ACHContractStep](https://github.com/Expen

### Safe Area Padding

Any `FormProvider.js` that has a button will also add safe area padding by default. If the `<FormProvider>` is inside a `<ScreenWrapper>`, we will want to disable the default safe area padding applied there e.g.
Any `FormProvider.tsx` that has a button at the bottom. If the `<FormProvider>` is inside a `<ScreenWrapper>`, the bottom safe area inset is handled automatically (`includeSafeAreaPaddingBottom` needs to be set to `true`, but its the default).
If you have custom requirements and can't use `<ScreenWrapper includeSafeAreaPaddingBottom={true}>`, you can use the `useStyledSafeAreaInsets()` hook:

```jsx
<ScreenWrapper includeSafeAreaPaddingBottom={false}>
const { paddingTop, paddingBottom, safeAreaPaddingBottomStyle } = useStyledSafeAreaInsets();

<View styles={[safeAreaPaddingBottomStyle, styles.pb5]}>
<FormProvider>
{...}
</FormProvider>
</ScreenWrapper>
</View>
```

### Handling nested Pickers in Form
Expand Down
2 changes: 1 addition & 1 deletion src/components/CategorySelector/CategorySelectorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function CategorySelectorModal({policyID, isVisible, currentCategory, onCategory
<ScreenWrapper
style={[styles.pb0]}
includePaddingTop={false}
includeSafeAreaPaddingBottom={false}
includeSafeAreaPaddingBottom
shouldEnableKeyboardAvoidingView={false}
testID={CategorySelectorModal.displayName}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear
<ScreenWrapper
style={[styles.pb0]}
includePaddingTop={false}
includeSafeAreaPaddingBottom={false}
includeSafeAreaPaddingBottom
testID={YearPickerModal.displayName}
>
<HeaderWithBackButton
Expand Down
6 changes: 1 addition & 5 deletions src/components/FixedFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type {ReactNode} from 'react';
import React from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
import useThemeStyles from '@hooks/useThemeStyles';

type FixedFooterProps = {
Expand All @@ -14,16 +13,13 @@ type FixedFooterProps = {
};

function FixedFooter({style, children}: FixedFooterProps) {
const insets = useSafeAreaInsets();
const styles = useThemeStyles();

if (!children) {
return null;
}

const shouldAddBottomPadding = !insets.bottom;

return <View style={[styles.ph5, shouldAddBottomPadding && styles.pb5, styles.flexShrink0, style]}>{children}</View>;
return <View style={[styles.ph5, styles.pb5, styles.flexShrink0, style]}>{children}</View>;
}

FixedFooter.displayName = 'FixedFooter';
Expand Down
56 changes: 26 additions & 30 deletions src/components/Form/FormWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import {Keyboard} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton';
import FormElement from '@components/FormElement';
import SafeAreaConsumer from '@components/SafeAreaConsumer';
import type {SafeAreaChildrenProps} from '@components/SafeAreaConsumer/types';
import ScrollView from '@components/ScrollView';
import ScrollViewWithContext from '@components/ScrollViewWithContext';
import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ErrorUtils from '@libs/ErrorUtils';
import type {OnyxFormKey} from '@src/ONYXKEYS';
Expand Down Expand Up @@ -60,6 +59,7 @@ function FormWrapper({
isSubmitDisabled = false,
}: FormWrapperProps) {
const styles = useThemeStyles();
const {paddingBottom: safeAreaInsetPaddingBottom} = useStyledSafeAreaInsets();
const formRef = useRef<RNScrollView>(null);
const formContentRef = useRef<View>(null);

Expand Down Expand Up @@ -99,11 +99,12 @@ function FormWrapper({
}, [errors, formState?.errorFields, inputRefs]);

const scrollViewContent = useCallback(
(safeAreaPaddingBottomStyle: SafeAreaChildrenProps['safeAreaPaddingBottomStyle']) => (
() => (
<FormElement
key={formID}
ref={formContentRef}
style={[style, safeAreaPaddingBottomStyle.paddingBottom ? safeAreaPaddingBottomStyle : styles.pb5]}
// Note: the paddingBottom is only grater 0 if no parent has applied the inset yet:
style={[style, {paddingBottom: safeAreaInsetPaddingBottom + styles.pb5.paddingBottom}]}
>
{children}
{isSubmitButtonVisible && (
Expand All @@ -128,7 +129,8 @@ function FormWrapper({
[
formID,
style,
styles.pb5,
safeAreaInsetPaddingBottom,
styles.pb5.paddingBottom,
styles.mh0,
styles.mt5,
styles.flex1,
Expand All @@ -153,33 +155,27 @@ function FormWrapper({
);

if (!shouldUseScrollView) {
return scrollViewContent({});
return scrollViewContent();
}

return (
<SafeAreaConsumer>
{({safeAreaPaddingBottomStyle}) =>
scrollContextEnabled ? (
<ScrollViewWithContext
style={[styles.w100, styles.flex1]}
contentContainerStyle={styles.flexGrow1}
keyboardShouldPersistTaps="handled"
ref={formRef}
>
{scrollViewContent(safeAreaPaddingBottomStyle)}
</ScrollViewWithContext>
) : (
<ScrollView
style={[styles.w100, styles.flex1]}
contentContainerStyle={styles.flexGrow1}
keyboardShouldPersistTaps="handled"
ref={formRef}
>
{scrollViewContent(safeAreaPaddingBottomStyle)}
</ScrollView>
)
}
</SafeAreaConsumer>
return scrollContextEnabled ? (
<ScrollViewWithContext
style={[styles.w100, styles.flex1]}
contentContainerStyle={styles.flexGrow1}
keyboardShouldPersistTaps="handled"
ref={formRef}
>
{scrollViewContent()}
</ScrollViewWithContext>
) : (
<ScrollView
style={[styles.w100, styles.flex1]}
contentContainerStyle={styles.flexGrow1}
keyboardShouldPersistTaps="handled"
ref={formRef}
>
{scrollViewContent()}
</ScrollView>
);
}

Expand Down
4 changes: 1 addition & 3 deletions src/components/FormAlertWithSubmitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type {Ref} from 'react';
import React from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useSafePaddingBottomStyle from '@hooks/useSafePaddingBottomStyle';
import useThemeStyles from '@hooks/useThemeStyles';
import getPlatform from '@libs/getPlatform';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -87,7 +86,6 @@ function FormAlertWithSubmitButton({
}: FormAlertWithSubmitButtonProps) {
const styles = useThemeStyles();
const style = [!footerContent ? {} : styles.mb3, buttonStyles];
const safePaddingBottomStyle = useSafePaddingBottomStyle();

// Disable pressOnEnter for Android Native to avoid issues with the Samsung keyboard,
// where pressing Enter saves the form instead of adding a new line in multiline input.
Expand All @@ -97,7 +95,7 @@ function FormAlertWithSubmitButton({

return (
<FormAlertWrapper
containerStyles={[styles.justifyContentEnd, safePaddingBottomStyle, containerStyles]}
containerStyles={[styles.justifyContentEnd, containerStyles]}
isAlertVisible={isAlertVisible}
isMessageHtml={isMessageHtml}
message={message}
Expand Down
2 changes: 1 addition & 1 deletion src/components/InteractiveStepWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ function InteractiveStepWrapper(
<ScreenWrapper
ref={ref}
testID={wrapperID}
includeSafeAreaPaddingBottom={false}
includeSafeAreaPaddingBottom
shouldEnablePickerAvoiding={shouldEnablePickerAvoiding}
shouldEnableMaxHeight={shouldEnableMaxHeight}
shouldShowOfflineIndicator={shouldShowOfflineIndicator}
Expand Down
1 change: 1 addition & 0 deletions src/components/SafeAreaConsumer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type SafeAreaConsumerProps from './types';
/**
* This component is a light wrapper around the SafeAreaInsetsContext.Consumer. There are several places where we
* may need not just the insets, but the computed styles so we save a few lines of code with this.
* Note: if you're working within a <ScreenWrapper> please use `useStyledSafeAreaInsets` instead.
*/
function SafeAreaConsumer({children}: SafeAreaConsumerProps) {
const StyleUtils = useStyleUtils();
Expand Down
Loading

0 comments on commit 3cc88f5

Please sign in to comment.