Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RequestorStep: Fix forward ref #28454

Merged
242 changes: 124 additions & 118 deletions src/components/ScreenWrapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,142 +20,148 @@ import useKeyboardState from '../../hooks/useKeyboardState';
import useEnvironment from '../../hooks/useEnvironment';
import useNetwork from '../../hooks/useNetwork';

function ScreenWrapper({
shouldEnableMaxHeight,
includePaddingTop,
keyboardAvoidingViewBehavior,
includeSafeAreaPaddingBottom,
shouldEnableKeyboardAvoidingView,
shouldEnablePickerAvoiding,
headerGapStyles,
children,
shouldShowOfflineIndicator,
offlineIndicatorStyle,
style,
shouldDismissKeyboardBeforeClose,
onEntryTransitionEnd,
testID,
}) {
const {windowHeight, isSmallScreenWidth} = useWindowDimensions();
const keyboardState = useKeyboardState();
const {isDevelopment} = useEnvironment();
const {isOffline} = useNetwork();
const navigation = useNavigation();
const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false);
const maxHeight = shouldEnableMaxHeight ? windowHeight : undefined;
const isKeyboardShown = lodashGet(keyboardState, 'isKeyboardShown', false);
const ScreenWrapper = React.forwardRef(
(
{
shouldEnableMaxHeight,
includePaddingTop,
keyboardAvoidingViewBehavior,
includeSafeAreaPaddingBottom,
shouldEnableKeyboardAvoidingView,
shouldEnablePickerAvoiding,
headerGapStyles,
children,
shouldShowOfflineIndicator,
offlineIndicatorStyle,
style,
shouldDismissKeyboardBeforeClose,
onEntryTransitionEnd,
testID,
},
ref,
) => {
const {windowHeight, isSmallScreenWidth} = useWindowDimensions();
const keyboardState = useKeyboardState();
const {isDevelopment} = useEnvironment();
const {isOffline} = useNetwork();
const navigation = useNavigation();
const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false);
const maxHeight = shouldEnableMaxHeight ? windowHeight : undefined;
const isKeyboardShown = lodashGet(keyboardState, 'isKeyboardShown', false);

const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponderCapture: (e, gestureState) => gestureState.numberActiveTouches === CONST.TEST_TOOL.NUMBER_OF_TAPS,
onPanResponderRelease: toggleTestToolsModal,
}),
).current;
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponderCapture: (e, gestureState) => gestureState.numberActiveTouches === CONST.TEST_TOOL.NUMBER_OF_TAPS,
onPanResponderRelease: toggleTestToolsModal,
}),
).current;

const keyboardDissmissPanResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponderCapture: (e, gestureState) => {
const isHorizontalSwipe = Math.abs(gestureState.dx) > Math.abs(gestureState.dy);
const shouldDismissKeyboard = shouldDismissKeyboardBeforeClose && isKeyboardShown && Browser.isMobile();
const keyboardDissmissPanResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponderCapture: (e, gestureState) => {
const isHorizontalSwipe = Math.abs(gestureState.dx) > Math.abs(gestureState.dy);
const shouldDismissKeyboard = shouldDismissKeyboardBeforeClose && isKeyboardShown && Browser.isMobile();

return isHorizontalSwipe && shouldDismissKeyboard;
},
onPanResponderGrant: Keyboard.dismiss,
}),
).current;
return isHorizontalSwipe && shouldDismissKeyboard;
},
onPanResponderGrant: Keyboard.dismiss,
}),
).current;

useEffect(() => {
const unsubscribeTransitionEnd = navigation.addListener('transitionEnd', (event) => {
// Prevent firing the prop callback when user is exiting the page.
if (lodashGet(event, 'data.closing')) {
return;
}
useEffect(() => {
const unsubscribeTransitionEnd = navigation.addListener('transitionEnd', (event) => {
// Prevent firing the prop callback when user is exiting the page.
if (lodashGet(event, 'data.closing')) {
return;
}

setDidScreenTransitionEnd(true);
onEntryTransitionEnd();
});
setDidScreenTransitionEnd(true);
onEntryTransitionEnd();
});

// We need to have this prop to remove keyboard before going away from the screen, to avoid previous screen look weird for a brief moment,
// also we need to have generic control in future - to prevent closing keyboard for some rare cases in which beforeRemove has limitations
// described here https://reactnavigation.org/docs/preventing-going-back/#limitations
const beforeRemoveSubscription = shouldDismissKeyboardBeforeClose
? navigation.addListener('beforeRemove', () => {
if (!isKeyboardShown) {
return;
}
Keyboard.dismiss();
})
: undefined;
// We need to have this prop to remove keyboard before going away from the screen, to avoid previous screen look weird for a brief moment,
// also we need to have generic control in future - to prevent closing keyboard for some rare cases in which beforeRemove has limitations
// described here https://reactnavigation.org/docs/preventing-going-back/#limitations
const beforeRemoveSubscription = shouldDismissKeyboardBeforeClose
? navigation.addListener('beforeRemove', () => {
if (!isKeyboardShown) {
return;
}
Keyboard.dismiss();
})
: undefined;

return () => {
unsubscribeTransitionEnd();
return () => {
unsubscribeTransitionEnd();

if (beforeRemoveSubscription) {
beforeRemoveSubscription();
}
};
// Rule disabled because this effect is only for component did mount & will component unmount lifecycle event
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (beforeRemoveSubscription) {
beforeRemoveSubscription();
}
};
// Rule disabled because this effect is only for component did mount & will component unmount lifecycle event
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<SafeAreaConsumer>
{({insets, paddingTop, paddingBottom, safeAreaPaddingBottomStyle}) => {
const paddingStyle = {};
return (
<SafeAreaConsumer>
{({insets, paddingTop, paddingBottom, safeAreaPaddingBottomStyle}) => {
const paddingStyle = {};

if (includePaddingTop) {
paddingStyle.paddingTop = paddingTop;
}
if (includePaddingTop) {
paddingStyle.paddingTop = paddingTop;
}

// We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked.
if (includeSafeAreaPaddingBottom || isOffline) {
paddingStyle.paddingBottom = paddingBottom;
}
// We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked.
if (includeSafeAreaPaddingBottom || isOffline) {
paddingStyle.paddingBottom = paddingBottom;
}

return (
<View
style={styles.flex1}
// eslint-disable-next-line react/jsx-props-no-spreading
{...(isDevelopment ? panResponder.panHandlers : {})}
testID={testID}
>
return (
<View
style={[styles.flex1, paddingStyle, ...style]}
ref={ref}
style={styles.flex1}
// eslint-disable-next-line react/jsx-props-no-spreading
{...keyboardDissmissPanResponder.panHandlers}
{...(isDevelopment ? panResponder.panHandlers : {})}
testID={testID}
>
<KeyboardAvoidingView
style={[styles.w100, styles.h100, {maxHeight}]}
behavior={keyboardAvoidingViewBehavior}
enabled={shouldEnableKeyboardAvoidingView}
<View
style={[styles.flex1, paddingStyle, ...style]}
// eslint-disable-next-line react/jsx-props-no-spreading
{...keyboardDissmissPanResponder.panHandlers}
>
<PickerAvoidingView
style={styles.flex1}
enabled={shouldEnablePickerAvoiding}
<KeyboardAvoidingView
style={[styles.w100, styles.h100, {maxHeight}]}
behavior={keyboardAvoidingViewBehavior}
enabled={shouldEnableKeyboardAvoidingView}
>
<HeaderGap styles={headerGapStyles} />
{isDevelopment && <TestToolsModal />}
{isDevelopment && <CustomDevMenu />}
{
// If props.children is a function, call it to provide the insets to the children.
_.isFunction(children)
? children({
insets,
safeAreaPaddingBottomStyle,
didScreenTransitionEnd,
})
: children
}
{isSmallScreenWidth && shouldShowOfflineIndicator && <OfflineIndicator style={offlineIndicatorStyle} />}
</PickerAvoidingView>
</KeyboardAvoidingView>
<PickerAvoidingView
style={styles.flex1}
enabled={shouldEnablePickerAvoiding}
>
<HeaderGap styles={headerGapStyles} />
{isDevelopment && <TestToolsModal />}
{isDevelopment && <CustomDevMenu />}
{
// If props.children is a function, call it to provide the insets to the children.
_.isFunction(children)
? children({
insets,
safeAreaPaddingBottomStyle,
didScreenTransitionEnd,
})
: children
}
{isSmallScreenWidth && shouldShowOfflineIndicator && <OfflineIndicator style={offlineIndicatorStyle} />}
</PickerAvoidingView>
</KeyboardAvoidingView>
</View>
</View>
</View>
);
}}
</SafeAreaConsumer>
);
}
);
}}
</SafeAreaConsumer>
);
},
);

ScreenWrapper.displayName = 'ScreenWrapper';
ScreenWrapper.propTypes = propTypes;
Expand Down
12 changes: 9 additions & 3 deletions src/pages/ReimbursementAccount/RequestorStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ const validate = (values) => {
return errors;
};

function RequestorStep({reimbursementAccount, shouldShowOnfido, reimbursementAccountDraft, onBackButtonPress, getDefaultStateForField}) {
/**
* Workaround for forwardRef + propTypes issue.
* See https://stackoverflow.com/questions/59716140/using-forwardref-with-proptypes-and-eslint
*/
const RequestorStep = React.forwardRef(({reimbursementAccount, shouldShowOnfido, reimbursementAccountDraft, onBackButtonPress, getDefaultStateForField}, ref) => {
kacper-mikolajczak marked this conversation as resolved.
Show resolved Hide resolved
const {translate} = useLocalize();

const defaultValues = useMemo(
Expand Down Expand Up @@ -110,6 +114,7 @@ function RequestorStep({reimbursementAccount, shouldShowOnfido, reimbursementAcc
if (shouldShowOnfido) {
return (
<RequestorOnfidoStep
ref={ref}
reimbursementAccount={reimbursementAccount}
reimbursementAccountDraft={reimbursementAccountDraft}
onBackButtonPress={onBackButtonPress}
Expand All @@ -120,6 +125,7 @@ function RequestorStep({reimbursementAccount, shouldShowOnfido, reimbursementAcc

return (
<ScreenWrapper
ref={ref}
includeSafeAreaPaddingBottom={false}
testID={RequestorStep.displayName}
>
Expand Down Expand Up @@ -194,9 +200,9 @@ function RequestorStep({reimbursementAccount, shouldShowOnfido, reimbursementAcc
</Form>
</ScreenWrapper>
);
}
});

RequestorStep.propTypes = propTypes;
RequestorStep.displayName = 'RequestorStep';

export default React.forwardRef(RequestorStep);
export default RequestorStep;
Loading