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

[TS Migration] Migrate withWindowDimensions and Modal directories to TypeScript #30338

Merged
merged 28 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
eebfbf1
migrate withWindowDimensions directory to TypeScript
JKobrynski Oct 20, 2023
8213caa
create types file for Modal
JKobrynski Oct 20, 2023
cf613ad
start migrating BaseModal to TypeScript
JKobrynski Oct 23, 2023
408ef25
add missing prop types
JKobrynski Oct 24, 2023
1c1d092
update types, update helper function types
JKobrynski Oct 24, 2023
118415e
reorganise types in StyleUtils
JKobrynski Oct 24, 2023
4c18fc0
fix typings in BaseModal
JKobrynski Oct 24, 2023
c016d84
migrate index.web.js to TypeScript
JKobrynski Oct 25, 2023
5f3968d
migrate index.ios.js to TypeScript
JKobrynski Oct 25, 2023
ffa9d70
migrate index.android.js to TypeScript
JKobrynski Oct 25, 2023
a437818
fix spacing calculation
JKobrynski Oct 25, 2023
7a637b9
Merge branch 'main' into migrateModalToTypeScript
JKobrynski Oct 25, 2023
7385968
apply finishing touches
JKobrynski Oct 26, 2023
c26a716
remove unnecessary conditional function calls
JKobrynski Oct 26, 2023
a1adc37
create common NewDimensions type
JKobrynski Oct 26, 2023
bc5c041
change index.web.tsx to index.tsx to fix linting
JKobrynski Oct 26, 2023
673d120
add event to backdrop press handler
JKobrynski Oct 26, 2023
2d7d45b
apply changes based on feedback from code review
JKobrynski Oct 26, 2023
a75557f
extract PopoverAnchorPosition to TypeScript
JKobrynski Oct 26, 2023
595c90d
Merge branch 'main' into migrateModalToTypeScript
JKobrynski Oct 30, 2023
da8b083
add return types to HOCs, fix linting
JKobrynski Oct 30, 2023
a6287ed
fix imports
JKobrynski Oct 30, 2023
3dfe00b
Merge branch 'main' into migrateModalToTypeScript
JKobrynski Nov 8, 2023
f4afdb4
remove redundant useNativeDriver prop
JKobrynski Nov 8, 2023
8082b7c
bring back default useNativeDriver prop for android
JKobrynski Nov 9, 2023
a5e8a58
add optional chaining to onModalShow
JKobrynski Nov 9, 2023
20e80d1
destructure props in Modal
JKobrynski Nov 13, 2023
434e472
bring back default props
JKobrynski Nov 14, 2023
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
@@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import React, {forwardRef, useCallback, useEffect, useMemo, useRef} from 'react';
import {View} from 'react-native';
import ReactNativeModal from 'react-native-modal';
Expand All @@ -14,44 +13,34 @@ import themeColors from '@styles/themes/default';
import variables from '@styles/variables';
import * as Modal from '@userActions/Modal';
import CONST from '@src/CONST';
import {defaultProps as modalDefaultProps, propTypes as modalPropTypes} from './modalPropTypes';

const propTypes = {
...modalPropTypes,

/** The ref to the modal container */
forwardedRef: PropTypes.func,
};

const defaultProps = {
...modalDefaultProps,
forwardedRef: () => {},
};

function BaseModal({
isVisible,
onClose,
shouldSetModalVisibility,
onModalHide,
type,
popoverAnchorPosition,
innerContainerStyle,
outerStyle,
onModalShow,
propagateSwipe,
fullscreen,
animationIn,
animationOut,
useNativeDriver: useNativeDriverProp,
hideModalContentWhileAnimating,
animationInTiming,
animationOutTiming,
statusBarTranslucent,
onLayout,
avoidKeyboard,
forwardedRef,
children,
}) {
import BaseModalProps from './types';

function BaseModal(
{
isVisible,
JKobrynski marked this conversation as resolved.
Show resolved Hide resolved
onClose,
shouldSetModalVisibility = true,
onModalHide = () => {},
type,
popoverAnchorPosition = {},
innerContainerStyle = {},
outerStyle,
onModalShow = () => {},
propagateSwipe,
fullscreen = true,
animationIn,
animationOut,
useNativeDriver: useNativeDriverProp,
hideModalContentWhileAnimating = false,
animationInTiming,
animationOutTiming,
statusBarTranslucent = true,
onLayout,
avoidKeyboard = false,
children,
}: BaseModalProps,
ref: React.ForwardedRef<View>,
) {
const {windowWidth, windowHeight, isSmallScreenWidth} = useWindowDimensions();

const safeAreaInsets = useSafeAreaInsets();
Expand All @@ -61,7 +50,7 @@ function BaseModal({

/**
* Hides modal
* @param {Boolean} [callHideCallback=true] Should we call the onModalHide callback
* @param callHideCallback - Should we call the onModalHide callback
*/
const hideModal = useCallback(
(callHideCallback = true) => {
Expand Down Expand Up @@ -113,10 +102,11 @@ function BaseModal({
onModalShow();
};

const handleBackdropPress = (e) => {
if (e && e.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) {
const handleBackdropPress = (e?: KeyboardEvent) => {
JKobrynski marked this conversation as resolved.
Show resolved Hide resolved
if (e?.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) {
return;
}

onClose();
};

Expand Down Expand Up @@ -196,8 +186,8 @@ function BaseModal({
style={modalStyle}
deviceHeight={windowHeight}
deviceWidth={windowWidth}
animationIn={animationIn || modalStyleAnimationIn}
animationOut={animationOut || modalStyleAnimationOut}
animationIn={animationIn ?? modalStyleAnimationIn}
animationOut={animationOut ?? modalStyleAnimationOut}
useNativeDriver={useNativeDriverProp && useNativeDriver}
hideModalContentWhileAnimating={hideModalContentWhileAnimating}
animationInTiming={animationInTiming}
Expand All @@ -208,26 +198,14 @@ function BaseModal({
>
<View
style={[styles.defaultModalContainer, modalContainerStyle, modalPaddingStyles, !isVisible && styles.pointerEventsNone]}
ref={forwardedRef}
ref={ref}
>
{children}
</View>
</ReactNativeModal>
);
}

BaseModal.propTypes = propTypes;
BaseModal.defaultProps = defaultProps;
BaseModal.displayName = 'BaseModal';

const BaseModalWithRef = forwardRef((props, ref) => (
<BaseModal
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

BaseModalWithRef.displayName = 'BaseModalWithRef';
BaseModal.displayName = 'BaseModalWithRef';

export default BaseModalWithRef;
export default forwardRef(BaseModal);
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {AppState} from 'react-native';
import withWindowDimensions from '@components/withWindowDimensions';
import ComposerFocusManager from '@libs/ComposerFocusManager';
import BaseModal from './BaseModal';
import {defaultProps, propTypes} from './modalPropTypes';
import BaseModalProps from './types';

AppState.addEventListener('focus', () => {
ComposerFocusManager.setReadyToFocus();
Expand All @@ -15,10 +15,9 @@ AppState.addEventListener('blur', () => {

// Only want to use useNativeDriver on Android. It has strange flashes issue on IOS
// https://github.com/react-native-modal/react-native-modal#the-modal-flashes-in-a-weird-way-when-animating
function Modal(props) {
function Modal(props: BaseModalProps) {
return (
<BaseModal
useNativeDriver
JKobrynski marked this conversation as resolved.
Show resolved Hide resolved
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
>
Expand All @@ -27,7 +26,5 @@ function Modal(props) {
);
}

Modal.propTypes = propTypes;
Modal.defaultProps = defaultProps;
Modal.displayName = 'Modal';
export default withWindowDimensions(Modal);
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import withWindowDimensions from '@components/withWindowDimensions';
import BaseModal from './BaseModal';
import {defaultProps, propTypes} from './modalPropTypes';
import BaseModalProps from './types';

function Modal(props) {
function Modal(props: BaseModalProps) {
JKobrynski marked this conversation as resolved.
Show resolved Hide resolved
return (
<BaseModal
// eslint-disable-next-line react/jsx-props-no-spreading
Expand All @@ -14,7 +14,5 @@ function Modal(props) {
);
}

Modal.propTypes = propTypes;
Modal.defaultProps = defaultProps;
Modal.displayName = 'Modal';
export default withWindowDimensions(Modal);
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import * as StyleUtils from '@styles/StyleUtils';
import themeColors from '@styles/themes/default';
import CONST from '@src/CONST';
import BaseModal from './BaseModal';
import {defaultProps, propTypes} from './modalPropTypes';
import BaseModalProps from './types';

function Modal(props) {
const [previousStatusBarColor, setPreviousStatusBarColor] = useState();
function Modal(props: BaseModalProps) {
JKobrynski marked this conversation as resolved.
Show resolved Hide resolved
const [previousStatusBarColor, setPreviousStatusBarColor] = useState<string>();

const setStatusBarColor = (color = themeColors.appBG) => {
if (!props.fullscreen) {
Expand All @@ -25,11 +25,16 @@ function Modal(props) {

const showModal = () => {
const statusBarColor = StatusBar.getBackgroundColor();

const isFullScreenModal =
props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED;
setPreviousStatusBarColor(statusBarColor);
// If it is a full screen modal then match it with appBG, otherwise we use the backdrop color
setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor));

if (statusBarColor) {
setPreviousStatusBarColor(statusBarColor);
// If it is a full screen modal then match it with appBG, otherwise we use the backdrop color
setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor));
}

props.onModalShow();
};

Expand All @@ -46,7 +51,5 @@ function Modal(props) {
);
}

Modal.propTypes = propTypes;
Modal.defaultProps = defaultProps;
Modal.displayName = 'Modal';
export default withWindowDimensions(Modal);
66 changes: 66 additions & 0 deletions src/components/Modal/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {ViewStyle} from 'react-native';
import {ModalProps} from 'react-native-modal';
import {ValueOf} from 'type-fest';
import {WindowDimensionsProps} from '@components/withWindowDimensions/types';
import CONST from '@src/CONST';

type PopoverAnchorPosition = {
top?: number;
right?: number;
bottom?: number;
left?: number;
};

type BaseModalProps = WindowDimensionsProps &
ModalProps & {
/** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */
fullscreen?: boolean;

/** Should we close modal on outside click */
shouldCloseOnOutsideClick?: boolean;

/** Should we announce the Modal visibility changes? */
shouldSetModalVisibility?: boolean;

/** Callback method fired when the user requests to close the modal */
onClose: () => void;

/** State that determines whether to display the modal or not */
isVisible: boolean;

/** Callback method fired when the user requests to submit the modal content. */
onSubmit?: () => void;

/** Callback method fired when the modal is hidden */
onModalHide?: () => void;

/** Callback method fired when the modal is shown */
onModalShow?: () => void;

/** Style of modal to display */
type?: ValueOf<typeof CONST.MODAL.MODAL_TYPE>;

/** The anchor position of a popover modal. Has no effect on other modal types. */
popoverAnchorPosition?: PopoverAnchorPosition;

outerStyle?: ViewStyle;

/** Whether the modal should go under the system statusbar */
statusBarTranslucent?: boolean;

/** Whether the modal should avoid the keyboard */
avoidKeyboard?: boolean;

/** Modal container styles */
innerContainerStyle?: ViewStyle;

/**
* Whether the modal should hide its content while animating. On iOS, set to true
* if `useNativeDriver` is also true, to avoid flashes in the UI.
*
* See: https://github.com/react-native-modal/react-native-modal/pull/116
* */
hideModalContentWhileAnimating?: boolean;
};

export default BaseModalProps;
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import PropTypes from 'prop-types';
import React, {createContext, forwardRef, useEffect, useMemo, useState} from 'react';
import React, {ComponentType, createContext, ForwardedRef, RefAttributes, useEffect, useMemo, useState} from 'react';
import {Dimensions} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import getComponentDisplayName from '@libs/getComponentDisplayName';
import getWindowHeightAdjustment from '@libs/getWindowHeightAdjustment';
import variables from '@styles/variables';
import ChildrenProps from '@src/types/utils/ChildrenProps';
import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types';

const WindowDimensionsContext = createContext(null);
const WindowDimensionsContext = createContext<WindowDimensionsContextData | null>(null);
const windowDimensionsPropTypes = {
// Width of the window
windowWidth: PropTypes.number.isRequired,
Expand All @@ -27,12 +29,7 @@ const windowDimensionsPropTypes = {
isLargeScreenWidth: PropTypes.bool.isRequired,
};

const windowDimensionsProviderPropTypes = {
/* Actual content wrapped by this component */
children: PropTypes.node.isRequired,
};

function WindowDimensionsProvider(props) {
function WindowDimensionsProvider(props: ChildrenProps) {
const [windowDimension, setWindowDimension] = useState(() => {
const initialDimensions = Dimensions.get('window');
return {
Expand All @@ -42,9 +39,8 @@ function WindowDimensionsProvider(props) {
});

useEffect(() => {
const onDimensionChange = (newDimensions) => {
const onDimensionChange = (newDimensions: NewDimensions) => {
const {window} = newDimensions;

setWindowDimension({
windowHeight: window.height,
windowWidth: window.width,
Expand Down Expand Up @@ -76,30 +72,29 @@ function WindowDimensionsProvider(props) {
return <WindowDimensionsContext.Provider value={contextValue}>{props.children}</WindowDimensionsContext.Provider>;
}

WindowDimensionsProvider.propTypes = windowDimensionsProviderPropTypes;
WindowDimensionsProvider.displayName = 'WindowDimensionsProvider';

/**
* @param {React.Component} WrappedComponent
* @returns {React.Component}
*/
export default function withWindowDimensions(WrappedComponent) {
const WithWindowDimensions = forwardRef((props, ref) => (
<WindowDimensionsContext.Consumer>
{(windowDimensionsProps) => (
<WrappedComponent
// eslint-disable-next-line react/jsx-props-no-spreading
{...windowDimensionsProps}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
ref={ref}
/>
)}
</WindowDimensionsContext.Consumer>
));
export default function withWindowDimensions<TProps extends WindowDimensionsProps, TRef>(
WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>,
): (props: Omit<TProps, keyof WindowDimensionsProps> & React.RefAttributes<TRef>) => React.ReactElement | null {
function WithWindowDimensions(props: Omit<TProps, keyof WindowDimensionsProps>, ref: ForwardedRef<TRef>) {
return (
<WindowDimensionsContext.Consumer>
{(windowDimensionsProps) => (
<WrappedComponent
// eslint-disable-next-line react/jsx-props-no-spreading
{...windowDimensionsProps}
// eslint-disable-next-line react/jsx-props-no-spreading
{...(props as TProps)}
ref={ref}
/>
)}
</WindowDimensionsContext.Consumer>
);
}

WithWindowDimensions.displayName = `withWindowDimensions(${getComponentDisplayName(WrappedComponent)})`;
return WithWindowDimensions;
return React.forwardRef(WithWindowDimensions);
}

export {WindowDimensionsProvider, windowDimensionsPropTypes};
Loading
Loading