Skip to content

Commit

Permalink
Merge pull request #33757 from software-mansion-labs/ts/HeaderPageLayout
Browse files Browse the repository at this point in the history
[TS migration] Migrate 'HeaderPageLayout' and 'IllustratedHeaderPageLayout' components to TypeScript
  • Loading branch information
AndrewGable authored Jan 5, 2024
2 parents fa533ce + 53fb2c2 commit 393f2be
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 115 deletions.
Original file line number Diff line number Diff line change
@@ -1,64 +1,62 @@
import PropTypes from 'prop-types';
import React, {useMemo} from 'react';
import type {ReactNode} from 'react';
import {ScrollView, View} from 'react-native';
import _ from 'underscore';
import type {StyleProp, ViewStyle} from 'react-native';
import useNetwork from '@hooks/useNetwork';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import FixedFooter from './FixedFooter';
import HeaderWithBackButton from './HeaderWithBackButton';
import headerWithBackButtonPropTypes from './HeaderWithBackButton/headerWithBackButtonPropTypes';
import type HeaderWithBackButtonProps from './HeaderWithBackButton/types';
import ScreenWrapper from './ScreenWrapper';

const propTypes = {
...headerWithBackButtonPropTypes,
type HeaderPageLayoutProps = ChildrenProps &
HeaderWithBackButtonProps & {
/** The background color to apply in the upper half of the screen. */
backgroundColor?: string;

/** Children to display in the lower half of the page (below the header section w/ an animation) */
children: PropTypes.node.isRequired,
/** A fixed footer to display at the bottom of the page. */
footer?: ReactNode;

/** The background color to apply in the upper half of the screen. */
backgroundColor: PropTypes.string,
/** The image to display in the upper half of the screen. */
headerContent?: ReactNode;

/** A fixed footer to display at the bottom of the page. */
footer: PropTypes.node,
/** Style to apply to the header image container */
headerContainerStyles?: StyleProp<ViewStyle>;

/** The image to display in the upper half of the screen. */
header: PropTypes.node,
/** Style to apply to the ScrollView container */
scrollViewContainerStyles?: StyleProp<ViewStyle>;

/** Style to apply to the header image container */
// eslint-disable-next-line react/forbid-prop-types
headerContainerStyles: PropTypes.arrayOf(PropTypes.object),
/** Style to apply to the children container */
childrenContainerStyles?: StyleProp<ViewStyle>;

/** Style to apply to the ScrollView container */
// eslint-disable-next-line react/forbid-prop-types
scrollViewContainerStyles: PropTypes.arrayOf(PropTypes.object),
/** Style to apply to the whole section container */
style?: StyleProp<ViewStyle>;
};

/** Style to apply to the children container */
// eslint-disable-next-line react/forbid-prop-types
childrenContainerStyles: PropTypes.arrayOf(PropTypes.object),
};

const defaultProps = {
backgroundColor: undefined,
header: null,
headerContainerStyles: [],
scrollViewContainerStyles: [],
childrenContainerStyles: [],
footer: null,
};

function HeaderPageLayout({backgroundColor, children, footer, headerContainerStyles, scrollViewContainerStyles, childrenContainerStyles, style, headerContent, ...propsToPassToHeader}) {
function HeaderPageLayout({
backgroundColor,
children,
footer,
headerContainerStyles,
scrollViewContainerStyles,
childrenContainerStyles,
style,
headerContent,
...rest
}: HeaderPageLayoutProps) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {windowHeight, isSmallScreenWidth} = useWindowDimensions();
const {isOffline} = useNetwork();
const appBGColor = StyleUtils.getBackgroundColorStyle(theme.appBG);
const {titleColor, iconFill} = useMemo(() => {
const isColorfulBackground = (backgroundColor || theme.appBG) !== theme.appBG && (backgroundColor || theme.highlightBG) !== theme.highlightBG;
const isColorfulBackground = (backgroundColor ?? theme.appBG) !== theme.appBG && (backgroundColor ?? theme.highlightBG) !== theme.highlightBG;
return {
titleColor: isColorfulBackground ? theme.textColorfulBackground : undefined,
iconFill: isColorfulBackground ? theme.iconColorfulBackground : undefined,
Expand All @@ -67,7 +65,7 @@ function HeaderPageLayout({backgroundColor, children, footer, headerContainerSty

return (
<ScreenWrapper
style={[StyleUtils.getBackgroundColorStyle(backgroundColor || theme.appBG)]}
style={[StyleUtils.getBackgroundColorStyle(backgroundColor ?? theme.appBG)]}
shouldEnablePickerAvoiding={false}
includeSafeAreaPaddingBottom={false}
offlineIndicatorStyle={[appBGColor]}
Expand All @@ -77,38 +75,34 @@ function HeaderPageLayout({backgroundColor, children, footer, headerContainerSty
<>
<HeaderWithBackButton
// eslint-disable-next-line react/jsx-props-no-spreading
{...propsToPassToHeader}
{...rest}
titleColor={titleColor}
iconFill={iconFill}
/>
<View style={[styles.flex1, appBGColor, !isOffline && !_.isNull(footer) ? safeAreaPaddingBottomStyle : {}]}>
<View style={[styles.flex1, appBGColor, !isOffline && footer ? safeAreaPaddingBottomStyle : {}]}>
{/** Safari on ios/mac has a bug where overscrolling the page scrollview shows green background color. This is a workaround to fix that. https://github.com/Expensify/App/issues/23422 */}
{Browser.isSafari() && (
<View style={styles.dualColorOverscrollSpacer}>
<View style={[styles.flex1, StyleUtils.getBackgroundColorStyle(backgroundColor || theme.appBG)]} />
<View style={[styles.flex1, StyleUtils.getBackgroundColorStyle(backgroundColor ?? theme.appBG)]} />
<View style={[isSmallScreenWidth ? styles.flex1 : styles.flex3, appBGColor]} />
</View>
)}
<ScrollView
contentContainerStyle={[safeAreaPaddingBottomStyle, style, scrollViewContainerStyles]}
offlineIndicatorStyle={[appBGColor]}
>
{!Browser.isSafari() && <View style={styles.overscrollSpacer(backgroundColor || theme.appBG, windowHeight)} />}
<View style={[styles.alignItemsCenter, styles.justifyContentEnd, StyleUtils.getBackgroundColorStyle(backgroundColor || theme.appBG), ...headerContainerStyles]}>
<ScrollView contentContainerStyle={[safeAreaPaddingBottomStyle, style, scrollViewContainerStyles]}>
{!Browser.isSafari() && <View style={styles.overscrollSpacer(backgroundColor ?? theme.appBG, windowHeight)} />}
<View style={[styles.alignItemsCenter, styles.justifyContentEnd, StyleUtils.getBackgroundColorStyle(backgroundColor ?? theme.appBG), headerContainerStyles]}>
{headerContent}
</View>
<View style={[styles.pt5, appBGColor, childrenContainerStyles]}>{children}</View>
</ScrollView>
{!_.isNull(footer) && <FixedFooter>{footer}</FixedFooter>}
{!!footer && <FixedFooter>{footer}</FixedFooter>}
</View>
</>
)}
</ScreenWrapper>
);
}

HeaderPageLayout.propTypes = propTypes;
HeaderPageLayout.defaultProps = defaultProps;
HeaderPageLayout.displayName = 'HeaderPageLayout';

export type {HeaderPageLayoutProps};
export default HeaderPageLayout;
67 changes: 0 additions & 67 deletions src/components/IllustratedHeaderPageLayout.js

This file was deleted.

50 changes: 50 additions & 0 deletions src/components/IllustratedHeaderPageLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import type {ReactNode} from 'react';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import HeaderPageLayout from './HeaderPageLayout';
import type {HeaderPageLayoutProps} from './HeaderPageLayout';
import Lottie from './Lottie';
import type DotLottieAnimation from './LottieAnimations/types';

type IllustratedHeaderPageLayoutProps = HeaderPageLayoutProps & {
/** The illustration to display in the header. Can be a JSON object representing a Lottie animation. */
illustration: DotLottieAnimation;

/** The background color to apply in the upper half of the screen. */
backgroundColor?: string;

/** Overlay content to display on top of animation */
overlayContent?: () => ReactNode;
};

function IllustratedHeaderPageLayout({backgroundColor, children, illustration, overlayContent, ...rest}: IllustratedHeaderPageLayoutProps) {
const theme = useTheme();
const styles = useThemeStyles();
return (
<HeaderPageLayout
backgroundColor={backgroundColor ?? theme.appBG}
headerContent={
<>
<Lottie
source={illustration}
style={styles.w100}
webStyle={styles.w100}
autoPlay
loop
/>
{overlayContent?.()}
</>
}
headerContainerStyles={[styles.justifyContentCenter, styles.w100]}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
>
{children}
</HeaderPageLayout>
);
}

IllustratedHeaderPageLayout.displayName = 'IllustratedHeaderPageLayout';

export default IllustratedHeaderPageLayout;

0 comments on commit 393f2be

Please sign in to comment.